Skip to content

Commit d863a12

Browse files
luiz-capakpm00
authored andcommitted
mm/util: introduce snapshot_page()
This commit refactors __dump_page() into snapshot_page(). snapshot_page() tries to take a faithful snapshot of a page and its folio representation. The snapshot is returned in the struct page_snapshot parameter along with additional flags that are best retrieved at snapshot creation time to reduce race windows. This function is intended to be used by callers that need a stable representation of a struct page and struct folio so that pointers or page information doesn't change while working on a page. The idea and original implementation of snapshot_page() comes from Matthew Wilcox with suggestions for improvements from David Hildenbrand. All bugs and misconceptions are mine. [[email protected]: fix set_ps_flags() commentary] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/637a03a05cb2e3df88f84ff9e9f9642374ef813a.1752499009.git.luizcap@redhat.com Signed-off-by: Luiz Capitulino <[email protected]> Reviewed-by: Shivank Garg <[email protected]> Tested-by: Harry Yoo <[email protected]> Acked-by: David Hildenbrand <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Oscar Salvador <[email protected]> Cc: SeongJae Park <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 92c99fc commit d863a12

File tree

3 files changed

+104
-38
lines changed

3 files changed

+104
-38
lines changed

include/linux/mm.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4199,4 +4199,23 @@ static inline bool page_pool_page_is_pp(struct page *page)
41994199
}
42004200
#endif
42014201

4202+
#define PAGE_SNAPSHOT_FAITHFUL (1 << 0)
4203+
#define PAGE_SNAPSHOT_PG_BUDDY (1 << 1)
4204+
#define PAGE_SNAPSHOT_PG_IDLE (1 << 2)
4205+
4206+
struct page_snapshot {
4207+
struct folio folio_snapshot;
4208+
struct page page_snapshot;
4209+
unsigned long pfn;
4210+
unsigned long idx;
4211+
unsigned long flags;
4212+
};
4213+
4214+
static inline bool snapshot_page_is_faithful(const struct page_snapshot *ps)
4215+
{
4216+
return ps->flags & PAGE_SNAPSHOT_FAITHFUL;
4217+
}
4218+
4219+
void snapshot_page(struct page_snapshot *ps, const struct page *page);
4220+
42024221
#endif /* _LINUX_MM_H */

mm/debug.c

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -129,47 +129,13 @@ static void __dump_folio(struct folio *folio, struct page *page,
129129

130130
static void __dump_page(const struct page *page)
131131
{
132-
struct folio *foliop, folio;
133-
struct page precise;
134-
unsigned long head;
135-
unsigned long pfn = page_to_pfn(page);
136-
unsigned long idx, nr_pages = 1;
137-
int loops = 5;
138-
139-
again:
140-
memcpy(&precise, page, sizeof(*page));
141-
head = precise.compound_head;
142-
if ((head & 1) == 0) {
143-
foliop = (struct folio *)&precise;
144-
idx = 0;
145-
if (!folio_test_large(foliop))
146-
goto dump;
147-
foliop = (struct folio *)page;
148-
} else {
149-
foliop = (struct folio *)(head - 1);
150-
idx = folio_page_idx(foliop, page);
151-
}
132+
struct page_snapshot ps;
152133

153-
if (idx < MAX_FOLIO_NR_PAGES) {
154-
memcpy(&folio, foliop, 2 * sizeof(struct page));
155-
nr_pages = folio_nr_pages(&folio);
156-
if (nr_pages > 1)
157-
memcpy(&folio.__page_2, &foliop->__page_2,
158-
sizeof(struct page));
159-
foliop = &folio;
160-
}
161-
162-
if (idx > nr_pages) {
163-
if (loops-- > 0)
164-
goto again;
134+
snapshot_page(&ps, page);
135+
if (!snapshot_page_is_faithful(&ps))
165136
pr_warn("page does not match folio\n");
166-
precise.compound_head &= ~1UL;
167-
foliop = (struct folio *)&precise;
168-
idx = 0;
169-
}
170137

171-
dump:
172-
__dump_folio(foliop, &precise, pfn, idx);
138+
__dump_folio(&ps.folio_snapshot, &ps.page_snapshot, ps.pfn, ps.idx);
173139
}
174140

175141
void dump_page(const struct page *page, const char *reason)

mm/util.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/sizes.h>
2626
#include <linux/compat.h>
2727
#include <linux/fsnotify.h>
28+
#include <linux/page_idle.h>
2829

2930
#include <linux/uaccess.h>
3031

@@ -1172,6 +1173,86 @@ int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma)
11721173
}
11731174
EXPORT_SYMBOL(compat_vma_mmap_prepare);
11741175

1176+
static void set_ps_flags(struct page_snapshot *ps, const struct folio *folio,
1177+
const struct page *page)
1178+
{
1179+
/*
1180+
* Only the first page of a high-order buddy page has PageBuddy() set.
1181+
* So we have to check manually whether this page is part of a high-
1182+
* order buddy page.
1183+
*/
1184+
if (PageBuddy(page))
1185+
ps->flags |= PAGE_SNAPSHOT_PG_BUDDY;
1186+
else if (page_count(page) == 0 && is_free_buddy_page(page))
1187+
ps->flags |= PAGE_SNAPSHOT_PG_BUDDY;
1188+
1189+
if (folio_test_idle(folio))
1190+
ps->flags |= PAGE_SNAPSHOT_PG_IDLE;
1191+
}
1192+
1193+
/**
1194+
* snapshot_page() - Create a snapshot of a struct page
1195+
* @ps: Pointer to a struct page_snapshot to store the page snapshot
1196+
* @page: The page to snapshot
1197+
*
1198+
* Create a snapshot of the page and store both its struct page and struct
1199+
* folio representations in @ps.
1200+
*
1201+
* A snapshot is marked as "faithful" if the compound state of @page was
1202+
* stable and allowed safe reconstruction of the folio representation. In
1203+
* rare cases where this is not possible (e.g. due to folio splitting),
1204+
* snapshot_page() falls back to treating @page as a single page and the
1205+
* snapshot is marked as "unfaithful". The snapshot_page_is_faithful()
1206+
* helper can be used to check for this condition.
1207+
*/
1208+
void snapshot_page(struct page_snapshot *ps, const struct page *page)
1209+
{
1210+
unsigned long head, nr_pages = 1;
1211+
struct folio *foliop;
1212+
int loops = 5;
1213+
1214+
ps->pfn = page_to_pfn(page);
1215+
ps->flags = PAGE_SNAPSHOT_FAITHFUL;
1216+
1217+
again:
1218+
memset(&ps->folio_snapshot, 0, sizeof(struct folio));
1219+
memcpy(&ps->page_snapshot, page, sizeof(*page));
1220+
head = ps->page_snapshot.compound_head;
1221+
if ((head & 1) == 0) {
1222+
ps->idx = 0;
1223+
foliop = (struct folio *)&ps->page_snapshot;
1224+
if (!folio_test_large(foliop)) {
1225+
set_ps_flags(ps, page_folio(page), page);
1226+
memcpy(&ps->folio_snapshot, foliop,
1227+
sizeof(struct page));
1228+
return;
1229+
}
1230+
foliop = (struct folio *)page;
1231+
} else {
1232+
foliop = (struct folio *)(head - 1);
1233+
ps->idx = folio_page_idx(foliop, page);
1234+
}
1235+
1236+
if (ps->idx < MAX_FOLIO_NR_PAGES) {
1237+
memcpy(&ps->folio_snapshot, foliop, 2 * sizeof(struct page));
1238+
nr_pages = folio_nr_pages(&ps->folio_snapshot);
1239+
if (nr_pages > 1)
1240+
memcpy(&ps->folio_snapshot.__page_2, &foliop->__page_2,
1241+
sizeof(struct page));
1242+
set_ps_flags(ps, foliop, page);
1243+
}
1244+
1245+
if (ps->idx > nr_pages) {
1246+
if (loops-- > 0)
1247+
goto again;
1248+
clear_compound_head(&ps->page_snapshot);
1249+
foliop = (struct folio *)&ps->page_snapshot;
1250+
memcpy(&ps->folio_snapshot, foliop, sizeof(struct page));
1251+
ps->flags = 0;
1252+
ps->idx = 0;
1253+
}
1254+
}
1255+
11751256
#ifdef CONFIG_MMU
11761257
/**
11771258
* folio_pte_batch - detect a PTE batch for a large folio

0 commit comments

Comments
 (0)