summaryrefslogtreecommitdiff
path: root/mm/memfd_luo.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-03-19 16:15:33 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-03-19 16:15:33 +0100
commit7e2dc8ed7862ac622b5a59953b679de97001dc83 (patch)
treed2d2cf61a22f5a6404000ee007c5e80bc2d9eca9 /mm/memfd_luo.c
parenta7e8c9cc3a13baf3dcf9734dd55609aa7ff9a1a0 (diff)
parent4a2b0ed2ac7abe9743e1559d212075a0ebac96b3 (diff)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'mm/memfd_luo.c')
-rw-r--r--mm/memfd_luo.c56
1 files changed, 49 insertions, 7 deletions
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index a34fccc23b6a..02993d83d39c 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -146,19 +146,56 @@ static int memfd_luo_preserve_folios(struct file *file,
for (i = 0; i < nr_folios; i++) {
struct memfd_luo_folio_ser *pfolio = &folios_ser[i];
struct folio *folio = folios[i];
- unsigned int flags = 0;
err = kho_preserve_folio(folio);
if (err)
goto err_unpreserve;
- if (folio_test_dirty(folio))
- flags |= MEMFD_LUO_FOLIO_DIRTY;
- if (folio_test_uptodate(folio))
- flags |= MEMFD_LUO_FOLIO_UPTODATE;
+ folio_lock(folio);
+
+ /*
+ * A dirty folio is one which has been written to. A clean folio
+ * is its opposite. Since a clean folio does not carry user
+ * data, it can be freed by page reclaim under memory pressure.
+ *
+ * Saving the dirty flag at prepare() time doesn't work since it
+ * can change later. Saving it at freeze() also won't work
+ * because the dirty bit is normally synced at unmap and there
+ * might still be a mapping of the file at freeze().
+ *
+ * To see why this is a problem, say a folio is clean at
+ * preserve, but gets dirtied later. The pfolio flags will mark
+ * it as clean. After retrieve, the next kernel might try to
+ * reclaim this folio under memory pressure, losing user data.
+ *
+ * Unconditionally mark it dirty to avoid this problem. This
+ * comes at the cost of making clean folios un-reclaimable after
+ * live update.
+ */
+ folio_mark_dirty(folio);
+
+ /*
+ * If the folio is not uptodate, it was fallocated but never
+ * used. Saving this flag at prepare() doesn't work since it
+ * might change later when someone uses the folio.
+ *
+ * Since we have taken the performance penalty of allocating,
+ * zeroing, and pinning all the folios in the holes, take a bit
+ * more and zero all non-uptodate folios too.
+ *
+ * NOTE: For someone looking to improve preserve performance,
+ * this is a good place to look.
+ */
+ if (!folio_test_uptodate(folio)) {
+ folio_zero_range(folio, 0, folio_size(folio));
+ flush_dcache_folio(folio);
+ folio_mark_uptodate(folio);
+ }
+
+ folio_unlock(folio);
pfolio->pfn = folio_pfn(folio);
- pfolio->flags = flags;
+ pfolio->flags = MEMFD_LUO_FOLIO_DIRTY | MEMFD_LUO_FOLIO_UPTODATE;
pfolio->index = folio->index;
}
@@ -326,7 +363,12 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args)
struct memfd_luo_folio_ser *folios_ser;
struct memfd_luo_ser *ser;
- if (args->retrieved)
+ /*
+ * If retrieve was successful, nothing to do. If it failed, retrieve()
+ * already cleaned up everything it could. So nothing to do there
+ * either. Only need to clean up when retrieve was not called.
+ */
+ if (args->retrieve_status)
return;
ser = phys_to_virt(args->serialized_data);