diff options
Diffstat (limited to 'Documentation/vm')
-rw-r--r-- | Documentation/vm/page-types.c | 154 |
1 files changed, 89 insertions, 65 deletions
diff --git a/Documentation/vm/page-types.c b/Documentation/vm/page-types.c index 6bdcc0632c24..18e891a329e0 100644 --- a/Documentation/vm/page-types.c +++ b/Documentation/vm/page-types.c | |||
@@ -161,8 +161,6 @@ static unsigned long pg_start[MAX_VMAS]; | |||
161 | static unsigned long pg_end[MAX_VMAS]; | 161 | static unsigned long pg_end[MAX_VMAS]; |
162 | static unsigned long voffset; | 162 | static unsigned long voffset; |
163 | 163 | ||
164 | static int pagemap_fd; | ||
165 | |||
166 | #define MAX_BIT_FILTERS 64 | 164 | #define MAX_BIT_FILTERS 64 |
167 | static int nr_bit_filters; | 165 | static int nr_bit_filters; |
168 | static uint64_t opt_mask[MAX_BIT_FILTERS]; | 166 | static uint64_t opt_mask[MAX_BIT_FILTERS]; |
@@ -170,7 +168,7 @@ static uint64_t opt_bits[MAX_BIT_FILTERS]; | |||
170 | 168 | ||
171 | static int page_size; | 169 | static int page_size; |
172 | 170 | ||
173 | #define PAGES_BATCH (64 << 10) /* 64k pages */ | 171 | static int pagemap_fd; |
174 | static int kpageflags_fd; | 172 | static int kpageflags_fd; |
175 | 173 | ||
176 | #define HASH_SHIFT 13 | 174 | #define HASH_SHIFT 13 |
@@ -226,6 +224,62 @@ int checked_open(const char *pathname, int flags) | |||
226 | return fd; | 224 | return fd; |
227 | } | 225 | } |
228 | 226 | ||
227 | /* | ||
228 | * pagemap/kpageflags routines | ||
229 | */ | ||
230 | |||
231 | static unsigned long do_u64_read(int fd, char *name, | ||
232 | uint64_t *buf, | ||
233 | unsigned long index, | ||
234 | unsigned long count) | ||
235 | { | ||
236 | long bytes; | ||
237 | |||
238 | if (index > ULONG_MAX / 8) | ||
239 | fatal("index overflow: %lu\n", index); | ||
240 | |||
241 | if (lseek(fd, index * 8, SEEK_SET) < 0) { | ||
242 | perror(name); | ||
243 | exit(EXIT_FAILURE); | ||
244 | } | ||
245 | |||
246 | bytes = read(fd, buf, count * 8); | ||
247 | if (bytes < 0) { | ||
248 | perror(name); | ||
249 | exit(EXIT_FAILURE); | ||
250 | } | ||
251 | if (bytes % 8) | ||
252 | fatal("partial read: %lu bytes\n", bytes); | ||
253 | |||
254 | return bytes / 8; | ||
255 | } | ||
256 | |||
257 | static unsigned long kpageflags_read(uint64_t *buf, | ||
258 | unsigned long index, | ||
259 | unsigned long pages) | ||
260 | { | ||
261 | return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages); | ||
262 | } | ||
263 | |||
264 | static unsigned long pagemap_read(uint64_t *buf, | ||
265 | unsigned long index, | ||
266 | unsigned long pages) | ||
267 | { | ||
268 | return do_u64_read(pagemap_fd, "/proc/pid/pagemap", buf, index, pages); | ||
269 | } | ||
270 | |||
271 | static unsigned long pagemap_pfn(uint64_t val) | ||
272 | { | ||
273 | unsigned long pfn; | ||
274 | |||
275 | if (val & PM_PRESENT) | ||
276 | pfn = PM_PFRAME(val); | ||
277 | else | ||
278 | pfn = 0; | ||
279 | |||
280 | return pfn; | ||
281 | } | ||
282 | |||
229 | 283 | ||
230 | /* | 284 | /* |
231 | * page flag names | 285 | * page flag names |
@@ -432,79 +486,53 @@ static void add_page(unsigned long offset, uint64_t flags) | |||
432 | total_pages++; | 486 | total_pages++; |
433 | } | 487 | } |
434 | 488 | ||
489 | #define KPAGEFLAGS_BATCH (64 << 10) /* 64k pages */ | ||
435 | static void walk_pfn(unsigned long index, unsigned long count) | 490 | static void walk_pfn(unsigned long index, unsigned long count) |
436 | { | 491 | { |
492 | uint64_t buf[KPAGEFLAGS_BATCH]; | ||
437 | unsigned long batch; | 493 | unsigned long batch; |
438 | unsigned long n; | 494 | unsigned long pages; |
439 | unsigned long i; | 495 | unsigned long i; |
440 | 496 | ||
441 | if (index > ULONG_MAX / KPF_BYTES) | ||
442 | fatal("index overflow: %lu\n", index); | ||
443 | |||
444 | lseek(kpageflags_fd, index * KPF_BYTES, SEEK_SET); | ||
445 | |||
446 | while (count) { | 497 | while (count) { |
447 | uint64_t kpageflags_buf[KPF_BYTES * PAGES_BATCH]; | 498 | batch = min_t(unsigned long, count, KPAGEFLAGS_BATCH); |
448 | 499 | pages = kpageflags_read(buf, index, batch); | |
449 | batch = min_t(unsigned long, count, PAGES_BATCH); | 500 | if (pages == 0) |
450 | n = read(kpageflags_fd, kpageflags_buf, batch * KPF_BYTES); | ||
451 | if (n == 0) | ||
452 | break; | 501 | break; |
453 | if (n < 0) { | ||
454 | perror(PROC_KPAGEFLAGS); | ||
455 | exit(EXIT_FAILURE); | ||
456 | } | ||
457 | |||
458 | if (n % KPF_BYTES != 0) | ||
459 | fatal("partial read: %lu bytes\n", n); | ||
460 | n = n / KPF_BYTES; | ||
461 | 502 | ||
462 | for (i = 0; i < n; i++) | 503 | for (i = 0; i < pages; i++) |
463 | add_page(index + i, kpageflags_buf[i]); | 504 | add_page(index + i, buf[i]); |
464 | 505 | ||
465 | index += batch; | 506 | index += pages; |
466 | count -= batch; | 507 | count -= pages; |
467 | } | 508 | } |
468 | } | 509 | } |
469 | 510 | ||
470 | 511 | #define PAGEMAP_BATCH (64 << 10) | |
471 | #define PAGEMAP_BATCH 4096 | 512 | static void walk_vma(unsigned long index, unsigned long count) |
472 | static unsigned long task_pfn(unsigned long pgoff) | ||
473 | { | 513 | { |
474 | static uint64_t buf[PAGEMAP_BATCH]; | 514 | uint64_t buf[PAGEMAP_BATCH]; |
475 | static unsigned long start; | 515 | unsigned long batch; |
476 | static long count; | 516 | unsigned long pages; |
477 | uint64_t pfn; | 517 | unsigned long pfn; |
518 | unsigned long i; | ||
478 | 519 | ||
479 | if (pgoff < start || pgoff >= start + count) { | 520 | while (count) { |
480 | if (lseek64(pagemap_fd, | 521 | batch = min_t(unsigned long, count, PAGEMAP_BATCH); |
481 | (uint64_t)pgoff * PM_ENTRY_BYTES, | 522 | pages = pagemap_read(buf, index, batch); |
482 | SEEK_SET) < 0) { | 523 | if (pages == 0) |
483 | perror("pagemap seek"); | 524 | break; |
484 | exit(EXIT_FAILURE); | ||
485 | } | ||
486 | count = read(pagemap_fd, buf, sizeof(buf)); | ||
487 | if (count == 0) | ||
488 | return 0; | ||
489 | if (count < 0) { | ||
490 | perror("pagemap read"); | ||
491 | exit(EXIT_FAILURE); | ||
492 | } | ||
493 | if (count % PM_ENTRY_BYTES) { | ||
494 | fatal("pagemap read not aligned.\n"); | ||
495 | exit(EXIT_FAILURE); | ||
496 | } | ||
497 | count /= PM_ENTRY_BYTES; | ||
498 | start = pgoff; | ||
499 | } | ||
500 | 525 | ||
501 | pfn = buf[pgoff - start]; | 526 | for (i = 0; i < pages; i++) { |
502 | if (pfn & PM_PRESENT) | 527 | pfn = pagemap_pfn(buf[i]); |
503 | pfn = PM_PFRAME(pfn); | 528 | voffset = index + i; |
504 | else | 529 | if (pfn) |
505 | pfn = 0; | 530 | walk_pfn(pfn, 1); |
531 | } | ||
506 | 532 | ||
507 | return pfn; | 533 | index += pages; |
534 | count -= pages; | ||
535 | } | ||
508 | } | 536 | } |
509 | 537 | ||
510 | static void walk_task(unsigned long index, unsigned long count) | 538 | static void walk_task(unsigned long index, unsigned long count) |
@@ -524,11 +552,7 @@ static void walk_task(unsigned long index, unsigned long count) | |||
524 | index = min_t(unsigned long, pg_end[i], end); | 552 | index = min_t(unsigned long, pg_end[i], end); |
525 | 553 | ||
526 | assert(voffset < index); | 554 | assert(voffset < index); |
527 | for (; voffset < index; voffset++) { | 555 | walk_vma(voffset, index - voffset); |
528 | unsigned long pfn = task_pfn(voffset); | ||
529 | if (pfn) | ||
530 | walk_pfn(pfn, 1); | ||
531 | } | ||
532 | } | 556 | } |
533 | } | 557 | } |
534 | 558 | ||