diff options
author | Konstantin Khlebnikov <koct9i@gmail.com> | 2016-03-17 17:18:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-17 18:09:34 -0400 |
commit | 075db1502ffd4ff8c58020167484a6e123ae01a3 (patch) | |
tree | 34e01d7a4e1f7ab95658ba7932a0c06b582ae616 /tools/vm | |
parent | accf62422b3a67fce8ce086aa81c8300ddbf42be (diff) |
tools/vm/page-types.c: add memory cgroup dumping and filtering
This adds two command line keys:
-c|--cgroup path|@inode Walk only pages owned by this memory cgroup
-C|--list-cgroup Show memory cgroup inodes
[vdavydov@virtuozzo.com: opt_cgroup should be uint64_t. Fix conflicts with "tools/vm/page-types.c: support swap entry"]
Signed-off-by: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Reviewed-by: Vladimir Davydov <vdavydov@virtuozzo.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'tools/vm')
-rw-r--r-- | tools/vm/page-types.c | 99 |
1 files changed, 84 insertions, 15 deletions
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index ec62ab4d8b55..dab61c377f54 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c | |||
@@ -75,6 +75,7 @@ | |||
75 | 75 | ||
76 | #define KPF_BYTES 8 | 76 | #define KPF_BYTES 8 |
77 | #define PROC_KPAGEFLAGS "/proc/kpageflags" | 77 | #define PROC_KPAGEFLAGS "/proc/kpageflags" |
78 | #define PROC_KPAGECGROUP "/proc/kpagecgroup" | ||
78 | 79 | ||
79 | /* [32-] kernel hacking assistances */ | 80 | /* [32-] kernel hacking assistances */ |
80 | #define KPF_RESERVED 32 | 81 | #define KPF_RESERVED 32 |
@@ -168,7 +169,9 @@ static int opt_raw; /* for kernel developers */ | |||
168 | static int opt_list; /* list pages (in ranges) */ | 169 | static int opt_list; /* list pages (in ranges) */ |
169 | static int opt_no_summary; /* don't show summary */ | 170 | static int opt_no_summary; /* don't show summary */ |
170 | static pid_t opt_pid; /* process to walk */ | 171 | static pid_t opt_pid; /* process to walk */ |
171 | const char * opt_file; | 172 | const char * opt_file; /* file or directory path */ |
173 | static uint64_t opt_cgroup; /* cgroup inode */ | ||
174 | static int opt_list_cgroup;/* list page cgroup */ | ||
172 | 175 | ||
173 | #define MAX_ADDR_RANGES 1024 | 176 | #define MAX_ADDR_RANGES 1024 |
174 | static int nr_addr_ranges; | 177 | static int nr_addr_ranges; |
@@ -189,6 +192,7 @@ static int page_size; | |||
189 | 192 | ||
190 | static int pagemap_fd; | 193 | static int pagemap_fd; |
191 | static int kpageflags_fd; | 194 | static int kpageflags_fd; |
195 | static int kpagecgroup_fd = -1; | ||
192 | 196 | ||
193 | static int opt_hwpoison; | 197 | static int opt_hwpoison; |
194 | static int opt_unpoison; | 198 | static int opt_unpoison; |
@@ -282,6 +286,16 @@ static unsigned long kpageflags_read(uint64_t *buf, | |||
282 | return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages); | 286 | return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages); |
283 | } | 287 | } |
284 | 288 | ||
289 | static unsigned long kpagecgroup_read(uint64_t *buf, | ||
290 | unsigned long index, | ||
291 | unsigned long pages) | ||
292 | { | ||
293 | if (kpagecgroup_fd < 0) | ||
294 | return pages; | ||
295 | |||
296 | return do_u64_read(kpagecgroup_fd, PROC_KPAGEFLAGS, buf, index, pages); | ||
297 | } | ||
298 | |||
285 | static unsigned long pagemap_read(uint64_t *buf, | 299 | static unsigned long pagemap_read(uint64_t *buf, |
286 | unsigned long index, | 300 | unsigned long index, |
287 | unsigned long pages) | 301 | unsigned long pages) |
@@ -354,14 +368,15 @@ static char *page_flag_longname(uint64_t flags) | |||
354 | */ | 368 | */ |
355 | 369 | ||
356 | static void show_page_range(unsigned long voffset, unsigned long offset, | 370 | static void show_page_range(unsigned long voffset, unsigned long offset, |
357 | unsigned long size, uint64_t flags) | 371 | unsigned long size, uint64_t flags, uint64_t cgroup) |
358 | { | 372 | { |
359 | static uint64_t flags0; | 373 | static uint64_t flags0; |
374 | static uint64_t cgroup0; | ||
360 | static unsigned long voff; | 375 | static unsigned long voff; |
361 | static unsigned long index; | 376 | static unsigned long index; |
362 | static unsigned long count; | 377 | static unsigned long count; |
363 | 378 | ||
364 | if (flags == flags0 && offset == index + count && | 379 | if (flags == flags0 && cgroup == cgroup0 && offset == index + count && |
365 | size && voffset == voff + count) { | 380 | size && voffset == voff + count) { |
366 | count += size; | 381 | count += size; |
367 | return; | 382 | return; |
@@ -372,11 +387,14 @@ static void show_page_range(unsigned long voffset, unsigned long offset, | |||
372 | printf("%lx\t", voff); | 387 | printf("%lx\t", voff); |
373 | if (opt_file) | 388 | if (opt_file) |
374 | printf("%lu\t", voff); | 389 | printf("%lu\t", voff); |
390 | if (opt_list_cgroup) | ||
391 | printf("@%llu\t", (unsigned long long)cgroup0); | ||
375 | printf("%lx\t%lx\t%s\n", | 392 | printf("%lx\t%lx\t%s\n", |
376 | index, count, page_flag_name(flags0)); | 393 | index, count, page_flag_name(flags0)); |
377 | } | 394 | } |
378 | 395 | ||
379 | flags0 = flags; | 396 | flags0 = flags; |
397 | cgroup0= cgroup; | ||
380 | index = offset; | 398 | index = offset; |
381 | voff = voffset; | 399 | voff = voffset; |
382 | count = size; | 400 | count = size; |
@@ -384,16 +402,18 @@ static void show_page_range(unsigned long voffset, unsigned long offset, | |||
384 | 402 | ||
385 | static void flush_page_range(void) | 403 | static void flush_page_range(void) |
386 | { | 404 | { |
387 | show_page_range(0, 0, 0, 0); | 405 | show_page_range(0, 0, 0, 0, 0); |
388 | } | 406 | } |
389 | 407 | ||
390 | static void show_page(unsigned long voffset, | 408 | static void show_page(unsigned long voffset, unsigned long offset, |
391 | unsigned long offset, uint64_t flags) | 409 | uint64_t flags, uint64_t cgroup) |
392 | { | 410 | { |
393 | if (opt_pid) | 411 | if (opt_pid) |
394 | printf("%lx\t", voffset); | 412 | printf("%lx\t", voffset); |
395 | if (opt_file) | 413 | if (opt_file) |
396 | printf("%lu\t", voffset); | 414 | printf("%lu\t", voffset); |
415 | if (opt_list_cgroup) | ||
416 | printf("@%llu\t", (unsigned long long)cgroup); | ||
397 | printf("%lx\t%s\n", offset, page_flag_name(flags)); | 417 | printf("%lx\t%s\n", offset, page_flag_name(flags)); |
398 | } | 418 | } |
399 | 419 | ||
@@ -576,23 +596,26 @@ static size_t hash_slot(uint64_t flags) | |||
576 | exit(EXIT_FAILURE); | 596 | exit(EXIT_FAILURE); |
577 | } | 597 | } |
578 | 598 | ||
579 | static void add_page(unsigned long voffset, | 599 | static void add_page(unsigned long voffset, unsigned long offset, |
580 | unsigned long offset, uint64_t flags, uint64_t pme) | 600 | uint64_t flags, uint64_t cgroup, uint64_t pme) |
581 | { | 601 | { |
582 | flags = kpageflags_flags(flags, pme); | 602 | flags = kpageflags_flags(flags, pme); |
583 | 603 | ||
584 | if (!bit_mask_ok(flags)) | 604 | if (!bit_mask_ok(flags)) |
585 | return; | 605 | return; |
586 | 606 | ||
607 | if (opt_cgroup && cgroup != (uint64_t)opt_cgroup) | ||
608 | return; | ||
609 | |||
587 | if (opt_hwpoison) | 610 | if (opt_hwpoison) |
588 | hwpoison_page(offset); | 611 | hwpoison_page(offset); |
589 | if (opt_unpoison) | 612 | if (opt_unpoison) |
590 | unpoison_page(offset); | 613 | unpoison_page(offset); |
591 | 614 | ||
592 | if (opt_list == 1) | 615 | if (opt_list == 1) |
593 | show_page_range(voffset, offset, 1, flags); | 616 | show_page_range(voffset, offset, 1, flags, cgroup); |
594 | else if (opt_list == 2) | 617 | else if (opt_list == 2) |
595 | show_page(voffset, offset, flags); | 618 | show_page(voffset, offset, flags, cgroup); |
596 | 619 | ||
597 | nr_pages[hash_slot(flags)]++; | 620 | nr_pages[hash_slot(flags)]++; |
598 | total_pages++; | 621 | total_pages++; |
@@ -605,18 +628,24 @@ static void walk_pfn(unsigned long voffset, | |||
605 | uint64_t pme) | 628 | uint64_t pme) |
606 | { | 629 | { |
607 | uint64_t buf[KPAGEFLAGS_BATCH]; | 630 | uint64_t buf[KPAGEFLAGS_BATCH]; |
631 | uint64_t cgi[KPAGEFLAGS_BATCH]; | ||
608 | unsigned long batch; | 632 | unsigned long batch; |
609 | unsigned long pages; | 633 | unsigned long pages; |
610 | unsigned long i; | 634 | unsigned long i; |
611 | 635 | ||
636 | memset(cgi, 0, sizeof cgi); | ||
637 | |||
612 | while (count) { | 638 | while (count) { |
613 | batch = min_t(unsigned long, count, KPAGEFLAGS_BATCH); | 639 | batch = min_t(unsigned long, count, KPAGEFLAGS_BATCH); |
614 | pages = kpageflags_read(buf, index, batch); | 640 | pages = kpageflags_read(buf, index, batch); |
615 | if (pages == 0) | 641 | if (pages == 0) |
616 | break; | 642 | break; |
617 | 643 | ||
644 | if (kpagecgroup_read(cgi, index, pages) != pages) | ||
645 | fatal("kpagecgroup returned fewer pages than expected"); | ||
646 | |||
618 | for (i = 0; i < pages; i++) | 647 | for (i = 0; i < pages; i++) |
619 | add_page(voffset + i, index + i, buf[i], pme); | 648 | add_page(voffset + i, index + i, buf[i], cgi[i], pme); |
620 | 649 | ||
621 | index += pages; | 650 | index += pages; |
622 | count -= pages; | 651 | count -= pages; |
@@ -630,10 +659,13 @@ static void walk_swap(unsigned long voffset, uint64_t pme) | |||
630 | if (!bit_mask_ok(flags)) | 659 | if (!bit_mask_ok(flags)) |
631 | return; | 660 | return; |
632 | 661 | ||
662 | if (opt_cgroup) | ||
663 | return; | ||
664 | |||
633 | if (opt_list == 1) | 665 | if (opt_list == 1) |
634 | show_page_range(voffset, pagemap_swap_offset(pme), 1, flags); | 666 | show_page_range(voffset, pagemap_swap_offset(pme), 1, flags, 0); |
635 | else if (opt_list == 2) | 667 | else if (opt_list == 2) |
636 | show_page(voffset, pagemap_swap_offset(pme), flags); | 668 | show_page(voffset, pagemap_swap_offset(pme), flags, 0); |
637 | 669 | ||
638 | nr_pages[hash_slot(flags)]++; | 670 | nr_pages[hash_slot(flags)]++; |
639 | total_pages++; | 671 | total_pages++; |
@@ -741,10 +773,12 @@ static void usage(void) | |||
741 | " -d|--describe flags Describe flags\n" | 773 | " -d|--describe flags Describe flags\n" |
742 | " -a|--addr addr-spec Walk a range of pages\n" | 774 | " -a|--addr addr-spec Walk a range of pages\n" |
743 | " -b|--bits bits-spec Walk pages with specified bits\n" | 775 | " -b|--bits bits-spec Walk pages with specified bits\n" |
776 | " -c|--cgroup path|@inode Walk pages within memory cgroup\n" | ||
744 | " -p|--pid pid Walk process address space\n" | 777 | " -p|--pid pid Walk process address space\n" |
745 | " -f|--file filename Walk file address space\n" | 778 | " -f|--file filename Walk file address space\n" |
746 | " -l|--list Show page details in ranges\n" | 779 | " -l|--list Show page details in ranges\n" |
747 | " -L|--list-each Show page details one by one\n" | 780 | " -L|--list-each Show page details one by one\n" |
781 | " -C|--list-cgroup Show cgroup inode for pages\n" | ||
748 | " -N|--no-summary Don't show summary info\n" | 782 | " -N|--no-summary Don't show summary info\n" |
749 | " -X|--hwpoison hwpoison pages\n" | 783 | " -X|--hwpoison hwpoison pages\n" |
750 | " -x|--unpoison unpoison pages\n" | 784 | " -x|--unpoison unpoison pages\n" |
@@ -879,6 +913,7 @@ static void walk_file(const char *name, const struct stat *st) | |||
879 | { | 913 | { |
880 | uint8_t vec[PAGEMAP_BATCH]; | 914 | uint8_t vec[PAGEMAP_BATCH]; |
881 | uint64_t buf[PAGEMAP_BATCH], flags; | 915 | uint64_t buf[PAGEMAP_BATCH], flags; |
916 | uint64_t cgroup = 0; | ||
882 | unsigned long nr_pages, pfn, i; | 917 | unsigned long nr_pages, pfn, i; |
883 | off_t off, end = st->st_size; | 918 | off_t off, end = st->st_size; |
884 | int fd; | 919 | int fd; |
@@ -936,12 +971,15 @@ got_sigbus: | |||
936 | continue; | 971 | continue; |
937 | if (!kpageflags_read(&flags, pfn, 1)) | 972 | if (!kpageflags_read(&flags, pfn, 1)) |
938 | continue; | 973 | continue; |
974 | if (!kpagecgroup_read(&cgroup, pfn, 1)) | ||
975 | fatal("kpagecgroup_read failed"); | ||
939 | if (first && opt_list) { | 976 | if (first && opt_list) { |
940 | first = 0; | 977 | first = 0; |
941 | flush_page_range(); | 978 | flush_page_range(); |
942 | show_file(name, st); | 979 | show_file(name, st); |
943 | } | 980 | } |
944 | add_page(off / page_size + i, pfn, flags, buf[i]); | 981 | add_page(off / page_size + i, pfn, |
982 | flags, cgroup, buf[i]); | ||
945 | } | 983 | } |
946 | } | 984 | } |
947 | 985 | ||
@@ -993,6 +1031,24 @@ static void parse_file(const char *name) | |||
993 | opt_file = name; | 1031 | opt_file = name; |
994 | } | 1032 | } |
995 | 1033 | ||
1034 | static void parse_cgroup(const char *path) | ||
1035 | { | ||
1036 | if (path[0] == '@') { | ||
1037 | opt_cgroup = parse_number(path + 1); | ||
1038 | return; | ||
1039 | } | ||
1040 | |||
1041 | struct stat st; | ||
1042 | |||
1043 | if (stat(path, &st)) | ||
1044 | fatal("stat failed: %s: %m\n", path); | ||
1045 | |||
1046 | if (!S_ISDIR(st.st_mode)) | ||
1047 | fatal("cgroup supposed to be a directory: %s\n", path); | ||
1048 | |||
1049 | opt_cgroup = st.st_ino; | ||
1050 | } | ||
1051 | |||
996 | static void parse_addr_range(const char *optarg) | 1052 | static void parse_addr_range(const char *optarg) |
997 | { | 1053 | { |
998 | unsigned long offset; | 1054 | unsigned long offset; |
@@ -1116,9 +1172,11 @@ static const struct option opts[] = { | |||
1116 | { "file" , 1, NULL, 'f' }, | 1172 | { "file" , 1, NULL, 'f' }, |
1117 | { "addr" , 1, NULL, 'a' }, | 1173 | { "addr" , 1, NULL, 'a' }, |
1118 | { "bits" , 1, NULL, 'b' }, | 1174 | { "bits" , 1, NULL, 'b' }, |
1175 | { "cgroup" , 1, NULL, 'c' }, | ||
1119 | { "describe" , 1, NULL, 'd' }, | 1176 | { "describe" , 1, NULL, 'd' }, |
1120 | { "list" , 0, NULL, 'l' }, | 1177 | { "list" , 0, NULL, 'l' }, |
1121 | { "list-each" , 0, NULL, 'L' }, | 1178 | { "list-each" , 0, NULL, 'L' }, |
1179 | { "list-cgroup", 0, NULL, 'C' }, | ||
1122 | { "no-summary", 0, NULL, 'N' }, | 1180 | { "no-summary", 0, NULL, 'N' }, |
1123 | { "hwpoison" , 0, NULL, 'X' }, | 1181 | { "hwpoison" , 0, NULL, 'X' }, |
1124 | { "unpoison" , 0, NULL, 'x' }, | 1182 | { "unpoison" , 0, NULL, 'x' }, |
@@ -1133,7 +1191,7 @@ int main(int argc, char *argv[]) | |||
1133 | page_size = getpagesize(); | 1191 | page_size = getpagesize(); |
1134 | 1192 | ||
1135 | while ((c = getopt_long(argc, argv, | 1193 | while ((c = getopt_long(argc, argv, |
1136 | "rp:f:a:b:d:lLNXxh", opts, NULL)) != -1) { | 1194 | "rp:f:a:b:d:c:ClLNXxh", opts, NULL)) != -1) { |
1137 | switch (c) { | 1195 | switch (c) { |
1138 | case 'r': | 1196 | case 'r': |
1139 | opt_raw = 1; | 1197 | opt_raw = 1; |
@@ -1150,6 +1208,12 @@ int main(int argc, char *argv[]) | |||
1150 | case 'b': | 1208 | case 'b': |
1151 | parse_bits_mask(optarg); | 1209 | parse_bits_mask(optarg); |
1152 | break; | 1210 | break; |
1211 | case 'c': | ||
1212 | parse_cgroup(optarg); | ||
1213 | break; | ||
1214 | case 'C': | ||
1215 | opt_list_cgroup = 1; | ||
1216 | break; | ||
1153 | case 'd': | 1217 | case 'd': |
1154 | describe_flags(optarg); | 1218 | describe_flags(optarg); |
1155 | exit(0); | 1219 | exit(0); |
@@ -1179,10 +1243,15 @@ int main(int argc, char *argv[]) | |||
1179 | } | 1243 | } |
1180 | } | 1244 | } |
1181 | 1245 | ||
1246 | if (opt_cgroup || opt_list_cgroup) | ||
1247 | kpagecgroup_fd = checked_open(PROC_KPAGECGROUP, O_RDONLY); | ||
1248 | |||
1182 | if (opt_list && opt_pid) | 1249 | if (opt_list && opt_pid) |
1183 | printf("voffset\t"); | 1250 | printf("voffset\t"); |
1184 | if (opt_list && opt_file) | 1251 | if (opt_list && opt_file) |
1185 | printf("foffset\t"); | 1252 | printf("foffset\t"); |
1253 | if (opt_list && opt_list_cgroup) | ||
1254 | printf("cgroup\t"); | ||
1186 | if (opt_list == 1) | 1255 | if (opt_list == 1) |
1187 | printf("offset\tlen\tflags\n"); | 1256 | printf("offset\tlen\tflags\n"); |
1188 | if (opt_list == 2) | 1257 | if (opt_list == 2) |