summaryrefslogtreecommitdiff
path: root/mm/memfd_luo.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/memfd_luo.c')
-rw-r--r--mm/memfd_luo.c49
1 files changed, 43 insertions, 6 deletions
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index e485b828d173..b8edb9f981d7 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;
}