|
25 | 25 | #include <linux/sizes.h>
|
26 | 26 | #include <linux/compat.h>
|
27 | 27 | #include <linux/fsnotify.h>
|
| 28 | +#include <linux/page_idle.h> |
28 | 29 |
|
29 | 30 | #include <linux/uaccess.h>
|
30 | 31 |
|
@@ -1172,6 +1173,86 @@ int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma)
|
1172 | 1173 | }
|
1173 | 1174 | EXPORT_SYMBOL(compat_vma_mmap_prepare);
|
1174 | 1175 |
|
| 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 | + |
1175 | 1256 | #ifdef CONFIG_MMU
|
1176 | 1257 | /**
|
1177 | 1258 | * folio_pte_batch - detect a PTE batch for a large folio
|
|
0 commit comments