Commit 7f1d23e6 authored by Christian Hansen's avatar Christian Hansen Committed by Linus Torvalds

tools/vm/page-types.c: include shared map counts

Add a new flag that will read kpagecount for each PFN and print out the
number of times the page is mapped along with the flags in the listing
view.

This information is useful in understanding and optimizing memory usage.
Identifying pages which are not shared allows us to focus on adjusting
the memory layout or access patterns for the sole owning process.
Knowing the number of processes that share a page tells us how many
other times we must make the same adjustments or how many processes to
potentially disable.

Truncated sample output:

  voffset map-cnt offset  len     flags
  561a3591e       1       15fe8   1       ___U_lA____Ma_b___________________________
  561a3591f       1       2b103   1       ___U_lA____Ma_b___________________________
  561a36ca4       1       2cc78   1       ___U_lA____Ma_b___________________________
  7f588bb4e       14      2273c   1       __RU_lA____M______________________________

[akpm@linux-foundation.org: coding-style fixes]
[chansen3@cisco.com: add documentation, tweak whitespace]
  Link: http://lkml.kernel.org/r/20180705181204.5529-1-chansen3@cisco.com
Link: http://lkml.kernel.org/r/20180612153205.12879-1-chansen3@cisco.comSigned-off-by: default avatarChristian Hansen <chansen3@cisco.com>
Reviewed-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent fadae295
...@@ -44,6 +44,9 @@ There are four components to pagemap: ...@@ -44,6 +44,9 @@ There are four components to pagemap:
* ``/proc/kpagecount``. This file contains a 64-bit count of the number of * ``/proc/kpagecount``. This file contains a 64-bit count of the number of
times each page is mapped, indexed by PFN. times each page is mapped, indexed by PFN.
The page-types tool in the tools/vm directory can be used to query the
number of times a page is mapped.
* ``/proc/kpageflags``. This file contains a 64-bit set of flags for each * ``/proc/kpageflags``. This file contains a 64-bit set of flags for each
page, indexed by PFN. page, indexed by PFN.
......
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
#define KPF_BYTES 8 #define KPF_BYTES 8
#define PROC_KPAGEFLAGS "/proc/kpageflags" #define PROC_KPAGEFLAGS "/proc/kpageflags"
#define PROC_KPAGECOUNT "/proc/kpagecount"
#define PROC_KPAGECGROUP "/proc/kpagecgroup" #define PROC_KPAGECGROUP "/proc/kpagecgroup"
/* [32-] kernel hacking assistances */ /* [32-] kernel hacking assistances */
...@@ -173,6 +174,7 @@ static pid_t opt_pid; /* process to walk */ ...@@ -173,6 +174,7 @@ static pid_t opt_pid; /* process to walk */
const char *opt_file; /* file or directory path */ const char *opt_file; /* file or directory path */
static uint64_t opt_cgroup; /* cgroup inode */ static uint64_t opt_cgroup; /* cgroup inode */
static int opt_list_cgroup;/* list page cgroup */ static int opt_list_cgroup;/* list page cgroup */
static int opt_list_mapcnt;/* list page map count */
static const char *opt_kpageflags;/* kpageflags file to parse */ static const char *opt_kpageflags;/* kpageflags file to parse */
#define MAX_ADDR_RANGES 1024 #define MAX_ADDR_RANGES 1024
...@@ -194,6 +196,7 @@ static int page_size; ...@@ -194,6 +196,7 @@ static int page_size;
static int pagemap_fd; static int pagemap_fd;
static int kpageflags_fd; static int kpageflags_fd;
static int kpagecount_fd = -1;
static int kpagecgroup_fd = -1; static int kpagecgroup_fd = -1;
static int opt_hwpoison; static int opt_hwpoison;
...@@ -298,6 +301,15 @@ static unsigned long kpagecgroup_read(uint64_t *buf, ...@@ -298,6 +301,15 @@ static unsigned long kpagecgroup_read(uint64_t *buf,
return do_u64_read(kpagecgroup_fd, opt_kpageflags, buf, index, pages); return do_u64_read(kpagecgroup_fd, opt_kpageflags, buf, index, pages);
} }
static unsigned long kpagecount_read(uint64_t *buf,
unsigned long index,
unsigned long pages)
{
return kpagecount_fd < 0 ? pages :
do_u64_read(kpagecount_fd, PROC_KPAGECOUNT,
buf, index, pages);
}
static unsigned long pagemap_read(uint64_t *buf, static unsigned long pagemap_read(uint64_t *buf,
unsigned long index, unsigned long index,
unsigned long pages) unsigned long pages)
...@@ -370,16 +382,18 @@ static char *page_flag_longname(uint64_t flags) ...@@ -370,16 +382,18 @@ static char *page_flag_longname(uint64_t flags)
*/ */
static void show_page_range(unsigned long voffset, unsigned long offset, static void show_page_range(unsigned long voffset, unsigned long offset,
unsigned long size, uint64_t flags, uint64_t cgroup) unsigned long size, uint64_t flags,
uint64_t cgroup, uint64_t mapcnt)
{ {
static uint64_t flags0; static uint64_t flags0;
static uint64_t cgroup0; static uint64_t cgroup0;
static uint64_t mapcnt0;
static unsigned long voff; static unsigned long voff;
static unsigned long index; static unsigned long index;
static unsigned long count; static unsigned long count;
if (flags == flags0 && cgroup == cgroup0 && offset == index + count && if (flags == flags0 && cgroup == cgroup0 && mapcnt == mapcnt0 &&
size && voffset == voff + count) { offset == index + count && size && voffset == voff + count) {
count += size; count += size;
return; return;
} }
...@@ -391,12 +405,15 @@ static void show_page_range(unsigned long voffset, unsigned long offset, ...@@ -391,12 +405,15 @@ static void show_page_range(unsigned long voffset, unsigned long offset,
printf("%lu\t", voff); printf("%lu\t", voff);
if (opt_list_cgroup) if (opt_list_cgroup)
printf("@%llu\t", (unsigned long long)cgroup0); printf("@%llu\t", (unsigned long long)cgroup0);
if (opt_list_mapcnt)
printf("%lu\t", mapcnt0);
printf("%lx\t%lx\t%s\n", printf("%lx\t%lx\t%s\n",
index, count, page_flag_name(flags0)); index, count, page_flag_name(flags0));
} }
flags0 = flags; flags0 = flags;
cgroup0= cgroup; cgroup0 = cgroup;
mapcnt0 = mapcnt;
index = offset; index = offset;
voff = voffset; voff = voffset;
count = size; count = size;
...@@ -404,11 +421,11 @@ static void show_page_range(unsigned long voffset, unsigned long offset, ...@@ -404,11 +421,11 @@ static void show_page_range(unsigned long voffset, unsigned long offset,
static void flush_page_range(void) static void flush_page_range(void)
{ {
show_page_range(0, 0, 0, 0, 0); show_page_range(0, 0, 0, 0, 0, 0);
} }
static void show_page(unsigned long voffset, unsigned long offset, static void show_page(unsigned long voffset, unsigned long offset,
uint64_t flags, uint64_t cgroup) uint64_t flags, uint64_t cgroup, uint64_t mapcnt)
{ {
if (opt_pid) if (opt_pid)
printf("%lx\t", voffset); printf("%lx\t", voffset);
...@@ -416,6 +433,9 @@ static void show_page(unsigned long voffset, unsigned long offset, ...@@ -416,6 +433,9 @@ static void show_page(unsigned long voffset, unsigned long offset,
printf("%lu\t", voffset); printf("%lu\t", voffset);
if (opt_list_cgroup) if (opt_list_cgroup)
printf("@%llu\t", (unsigned long long)cgroup); printf("@%llu\t", (unsigned long long)cgroup);
if (opt_list_mapcnt)
printf("%lu\t", mapcnt);
printf("%lx\t%s\n", offset, page_flag_name(flags)); printf("%lx\t%s\n", offset, page_flag_name(flags));
} }
...@@ -599,7 +619,8 @@ static size_t hash_slot(uint64_t flags) ...@@ -599,7 +619,8 @@ static size_t hash_slot(uint64_t flags)
} }
static void add_page(unsigned long voffset, unsigned long offset, static void add_page(unsigned long voffset, unsigned long offset,
uint64_t flags, uint64_t cgroup, uint64_t pme) uint64_t flags, uint64_t cgroup, uint64_t mapcnt,
uint64_t pme)
{ {
flags = kpageflags_flags(flags, pme); flags = kpageflags_flags(flags, pme);
...@@ -615,9 +636,9 @@ static void add_page(unsigned long voffset, unsigned long offset, ...@@ -615,9 +636,9 @@ static void add_page(unsigned long voffset, unsigned long offset,
unpoison_page(offset); unpoison_page(offset);
if (opt_list == 1) if (opt_list == 1)
show_page_range(voffset, offset, 1, flags, cgroup); show_page_range(voffset, offset, 1, flags, cgroup, mapcnt);
else if (opt_list == 2) else if (opt_list == 2)
show_page(voffset, offset, flags, cgroup); show_page(voffset, offset, flags, cgroup, mapcnt);
nr_pages[hash_slot(flags)]++; nr_pages[hash_slot(flags)]++;
total_pages++; total_pages++;
...@@ -631,6 +652,7 @@ static void walk_pfn(unsigned long voffset, ...@@ -631,6 +652,7 @@ static void walk_pfn(unsigned long voffset,
{ {
uint64_t buf[KPAGEFLAGS_BATCH]; uint64_t buf[KPAGEFLAGS_BATCH];
uint64_t cgi[KPAGEFLAGS_BATCH]; uint64_t cgi[KPAGEFLAGS_BATCH];
uint64_t cnt[KPAGEFLAGS_BATCH];
unsigned long batch; unsigned long batch;
unsigned long pages; unsigned long pages;
unsigned long i; unsigned long i;
...@@ -654,8 +676,12 @@ static void walk_pfn(unsigned long voffset, ...@@ -654,8 +676,12 @@ static void walk_pfn(unsigned long voffset,
if (kpagecgroup_read(cgi, index, pages) != pages) if (kpagecgroup_read(cgi, index, pages) != pages)
fatal("kpagecgroup returned fewer pages than expected"); fatal("kpagecgroup returned fewer pages than expected");
if (kpagecount_read(cnt, index, batch) != pages)
fatal("kpagecount returned fewer pages than expected");
for (i = 0; i < pages; i++) for (i = 0; i < pages; i++)
add_page(voffset + i, index + i, buf[i], cgi[i], pme); add_page(voffset + i, index + i,
buf[i], cgi[i], cnt[i], pme);
index += pages; index += pages;
count -= pages; count -= pages;
...@@ -673,9 +699,10 @@ static void walk_swap(unsigned long voffset, uint64_t pme) ...@@ -673,9 +699,10 @@ static void walk_swap(unsigned long voffset, uint64_t pme)
return; return;
if (opt_list == 1) if (opt_list == 1)
show_page_range(voffset, pagemap_swap_offset(pme), 1, flags, 0); show_page_range(voffset, pagemap_swap_offset(pme),
1, flags, 0, 0);
else if (opt_list == 2) else if (opt_list == 2)
show_page(voffset, pagemap_swap_offset(pme), flags, 0); show_page(voffset, pagemap_swap_offset(pme), flags, 0, 0);
nr_pages[hash_slot(flags)]++; nr_pages[hash_slot(flags)]++;
total_pages++; total_pages++;
...@@ -789,6 +816,7 @@ static void usage(void) ...@@ -789,6 +816,7 @@ static void usage(void)
" -l|--list Show page details in ranges\n" " -l|--list Show page details in ranges\n"
" -L|--list-each Show page details one by one\n" " -L|--list-each Show page details one by one\n"
" -C|--list-cgroup Show cgroup inode for pages\n" " -C|--list-cgroup Show cgroup inode for pages\n"
" -M|--list-mapcnt Show page map count\n"
" -N|--no-summary Don't show summary info\n" " -N|--no-summary Don't show summary info\n"
" -X|--hwpoison hwpoison pages\n" " -X|--hwpoison hwpoison pages\n"
" -x|--unpoison unpoison pages\n" " -x|--unpoison unpoison pages\n"
...@@ -925,6 +953,7 @@ static void walk_file(const char *name, const struct stat *st) ...@@ -925,6 +953,7 @@ static void walk_file(const char *name, const struct stat *st)
uint8_t vec[PAGEMAP_BATCH]; uint8_t vec[PAGEMAP_BATCH];
uint64_t buf[PAGEMAP_BATCH], flags; uint64_t buf[PAGEMAP_BATCH], flags;
uint64_t cgroup = 0; uint64_t cgroup = 0;
uint64_t mapcnt = 0;
unsigned long nr_pages, pfn, i; unsigned long nr_pages, pfn, i;
off_t off, end = st->st_size; off_t off, end = st->st_size;
int fd; int fd;
...@@ -984,13 +1013,15 @@ static void walk_file(const char *name, const struct stat *st) ...@@ -984,13 +1013,15 @@ static void walk_file(const char *name, const struct stat *st)
continue; continue;
if (!kpagecgroup_read(&cgroup, pfn, 1)) if (!kpagecgroup_read(&cgroup, pfn, 1))
fatal("kpagecgroup_read failed"); fatal("kpagecgroup_read failed");
if (!kpagecount_read(&mapcnt, pfn, 1))
fatal("kpagecount_read failed");
if (first && opt_list) { if (first && opt_list) {
first = 0; first = 0;
flush_page_range(); flush_page_range();
show_file(name, st); show_file(name, st);
} }
add_page(off / page_size + i, pfn, add_page(off / page_size + i, pfn,
flags, cgroup, buf[i]); flags, cgroup, mapcnt, buf[i]);
} }
} }
...@@ -1193,6 +1224,7 @@ static const struct option opts[] = { ...@@ -1193,6 +1224,7 @@ static const struct option opts[] = {
{ "list" , 0, NULL, 'l' }, { "list" , 0, NULL, 'l' },
{ "list-each" , 0, NULL, 'L' }, { "list-each" , 0, NULL, 'L' },
{ "list-cgroup", 0, NULL, 'C' }, { "list-cgroup", 0, NULL, 'C' },
{ "list-mapcnt", 0, NULL, 'M' },
{ "no-summary", 0, NULL, 'N' }, { "no-summary", 0, NULL, 'N' },
{ "hwpoison" , 0, NULL, 'X' }, { "hwpoison" , 0, NULL, 'X' },
{ "unpoison" , 0, NULL, 'x' }, { "unpoison" , 0, NULL, 'x' },
...@@ -1208,7 +1240,8 @@ int main(int argc, char *argv[]) ...@@ -1208,7 +1240,8 @@ int main(int argc, char *argv[])
page_size = getpagesize(); page_size = getpagesize();
while ((c = getopt_long(argc, argv, while ((c = getopt_long(argc, argv,
"rp:f:a:b:d:c:ClLNXxF:h", opts, NULL)) != -1) { "rp:f:a:b:d:c:ClLMNXxF:h",
opts, NULL)) != -1) {
switch (c) { switch (c) {
case 'r': case 'r':
opt_raw = 1; opt_raw = 1;
...@@ -1240,6 +1273,9 @@ int main(int argc, char *argv[]) ...@@ -1240,6 +1273,9 @@ int main(int argc, char *argv[])
case 'L': case 'L':
opt_list = 2; opt_list = 2;
break; break;
case 'M':
opt_list_mapcnt = 1;
break;
case 'N': case 'N':
opt_no_summary = 1; opt_no_summary = 1;
break; break;
...@@ -1269,12 +1305,18 @@ int main(int argc, char *argv[]) ...@@ -1269,12 +1305,18 @@ int main(int argc, char *argv[])
if (opt_cgroup || opt_list_cgroup) if (opt_cgroup || opt_list_cgroup)
kpagecgroup_fd = checked_open(PROC_KPAGECGROUP, O_RDONLY); kpagecgroup_fd = checked_open(PROC_KPAGECGROUP, O_RDONLY);
if (opt_list && opt_list_mapcnt)
kpagecount_fd = checked_open(PROC_KPAGECOUNT, O_RDONLY);
if (opt_list && opt_pid) if (opt_list && opt_pid)
printf("voffset\t"); printf("voffset\t");
if (opt_list && opt_file) if (opt_list && opt_file)
printf("foffset\t"); printf("foffset\t");
if (opt_list && opt_list_cgroup) if (opt_list && opt_list_cgroup)
printf("cgroup\t"); printf("cgroup\t");
if (opt_list && opt_list_mapcnt)
printf("map-cnt\t");
if (opt_list == 1) if (opt_list == 1)
printf("offset\tlen\tflags\n"); printf("offset\tlen\tflags\n");
if (opt_list == 2) if (opt_list == 2)
...@@ -1296,5 +1338,8 @@ int main(int argc, char *argv[]) ...@@ -1296,5 +1338,8 @@ int main(int argc, char *argv[])
show_summary(); show_summary();
if (opt_list_mapcnt)
close(kpagecount_fd);
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment