Skip to content

Commit

Permalink
vsprintf: dump full information of page flags in pGp
Browse files Browse the repository at this point in the history
Currently the pGp only shows the names of page flags, rather than
the full information including section, node, zone, last cpupid and
kasan tag. While it is not easy to parse these information manually
because there're so many flavors. Let's interpret them in pGp as well.

To be compitable with the existed format of pGp, the new introduced ones
also use '|' as the separator, then the user tools parsing pGp won't
need to make change, suggested by Matthew. The new information is
tracked onto the end of the existed one.

On example of the output in mm/slub.c as follows,
- Before the patch,
[ 6343.396602] Slab 0x000000004382e02b objects=33 used=3 fp=0x000000009ae06ffc flags=0x17ffffc0010200(slab|head)

- After the patch,
[ 8448.272530] Slab 0x0000000090797883 objects=33 used=3 fp=0x00000000790f1c26 flags=0x17ffffc0010200(slab|head|node=0|zone=2|lastcpupid=0x1fffff)

The documentation and test cases are also updated. The output of the
test cases as follows,
[68599.816764] test_printf: loaded.
[68599.819068] test_printf: all 388 tests passed
[68599.830367] test_printf: unloaded.

[lkp@intel.com: reported issues in the prev version in test_printf.c]

Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Joe Perches <joe@perches.com>
Cc: Miaohe Lin <linmiaohe@huawei.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: kernel test robot <lkp@intel.com>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20210319101246.73513-4-laoar.shao@gmail.com
  • Loading branch information
Yafang Shao authored and Petr Mladek committed Mar 19, 2021
1 parent 96b94ab commit c244297
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Documentation/core-api/printk-formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ Flags bitfields such as page flags, gfp_flags

::

%pGp referenced|uptodate|lru|active|private
%pGp referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff
%pGg GFP_USER|GFP_DMA32|GFP_NOWARN
%pGv read|exec|mayread|maywrite|mayexec|denywrite

Expand Down
90 changes: 80 additions & 10 deletions lib/test_printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,24 +577,98 @@ netdev_features(void)
{
}

struct page_flags_test {
int width;
int shift;
int mask;
unsigned long value;
const char *fmt;
const char *name;
};

static struct page_flags_test pft[] = {
{SECTIONS_WIDTH, SECTIONS_PGSHIFT, SECTIONS_MASK,
0, "%d", "section"},
{NODES_WIDTH, NODES_PGSHIFT, NODES_MASK,
0, "%d", "node"},
{ZONES_WIDTH, ZONES_PGSHIFT, ZONES_MASK,
0, "%d", "zone"},
{LAST_CPUPID_WIDTH, LAST_CPUPID_PGSHIFT, LAST_CPUPID_MASK,
0, "%#x", "lastcpupid"},
{KASAN_TAG_WIDTH, KASAN_TAG_PGSHIFT, KASAN_TAG_MASK,
0, "%#x", "kasantag"},
};

static void __init
page_flags_test(int section, int node, int zone, int last_cpupid,
int kasan_tag, int flags, const char *name, char *cmp_buf)
{
unsigned long values[] = {section, node, zone, last_cpupid, kasan_tag};
unsigned long page_flags = 0;
unsigned long size = 0;
bool append = false;
int i;

flags &= BIT(NR_PAGEFLAGS) - 1;
if (flags) {
page_flags |= flags;
snprintf(cmp_buf + size, BUF_SIZE - size, "%s", name);
size = strlen(cmp_buf);
#if SECTIONS_WIDTH || NODES_WIDTH || ZONES_WIDTH || \
LAST_CPUPID_WIDTH || KASAN_TAG_WIDTH
/* Other information also included in page flags */
snprintf(cmp_buf + size, BUF_SIZE - size, "|");
size = strlen(cmp_buf);
#endif
}

/* Set the test value */
for (i = 0; i < ARRAY_SIZE(pft); i++)
pft[i].value = values[i];

for (i = 0; i < ARRAY_SIZE(pft); i++) {
if (!pft[i].width)
continue;

if (append) {
snprintf(cmp_buf + size, BUF_SIZE - size, "|");
size = strlen(cmp_buf);
}

page_flags |= (pft[i].value & pft[i].mask) << pft[i].shift;
snprintf(cmp_buf + size, BUF_SIZE - size, "%s=", pft[i].name);
size = strlen(cmp_buf);
snprintf(cmp_buf + size, BUF_SIZE - size, pft[i].fmt,
pft[i].value & pft[i].mask);
size = strlen(cmp_buf);
append = true;
}

test(cmp_buf, "%pGp", &page_flags);
}

static void __init
flags(void)
{
unsigned long flags;
gfp_t gfp;
char *cmp_buffer;
gfp_t gfp;

cmp_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!cmp_buffer)
return;

flags = 0;
test("", "%pGp", &flags);
page_flags_test(0, 0, 0, 0, 0, flags, "", cmp_buffer);

/* Page flags should filter the zone id */
flags = 1UL << NR_PAGEFLAGS;
test("", "%pGp", &flags);
page_flags_test(0, 0, 0, 0, 0, flags, "", cmp_buffer);

flags |= 1UL << PG_uptodate | 1UL << PG_dirty | 1UL << PG_lru
| 1UL << PG_active | 1UL << PG_swapbacked;
test("uptodate|dirty|lru|active|swapbacked", "%pGp", &flags);

page_flags_test(1, 1, 1, 0x1fffff, 1, flags,
"uptodate|dirty|lru|active|swapbacked",
cmp_buffer);

flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC
| VM_DENYWRITE;
Expand All @@ -609,10 +683,6 @@ flags(void)
gfp = __GFP_ATOMIC;
test("__GFP_ATOMIC", "%pGg", &gfp);

cmp_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!cmp_buffer)
return;

/* Any flags not translated by the table should remain numeric */
gfp = ~__GFP_BITS_MASK;
snprintf(cmp_buffer, BUF_SIZE, "%#lx", (unsigned long) gfp);
Expand Down
66 changes: 61 additions & 5 deletions lib/vsprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1916,6 +1916,66 @@ char *format_flags(char *buf, char *end, unsigned long flags,
return buf;
}

struct page_flags_fields {
int width;
int shift;
int mask;
const struct printf_spec *spec;
const char *name;
};

static const struct page_flags_fields pff[] = {
{SECTIONS_WIDTH, SECTIONS_PGSHIFT, SECTIONS_MASK,
&default_dec_spec, "section"},
{NODES_WIDTH, NODES_PGSHIFT, NODES_MASK,
&default_dec_spec, "node"},
{ZONES_WIDTH, ZONES_PGSHIFT, ZONES_MASK,
&default_dec_spec, "zone"},
{LAST_CPUPID_WIDTH, LAST_CPUPID_PGSHIFT, LAST_CPUPID_MASK,
&default_flag_spec, "lastcpupid"},
{KASAN_TAG_WIDTH, KASAN_TAG_PGSHIFT, KASAN_TAG_MASK,
&default_flag_spec, "kasantag"},
};

static
char *format_page_flags(char *buf, char *end, unsigned long flags)
{
unsigned long main_flags = flags & (BIT(NR_PAGEFLAGS) - 1);
bool append = false;
int i;

/* Page flags from the main area. */
if (main_flags) {
buf = format_flags(buf, end, main_flags, pageflag_names);
append = true;
}

/* Page flags from the fields area */
for (i = 0; i < ARRAY_SIZE(pff); i++) {
/* Skip undefined fields. */
if (!pff[i].width)
continue;

/* Format: Flag Name + '=' (equals sign) + Number + '|' (separator) */
if (append) {
if (buf < end)
*buf = '|';
buf++;
}

buf = string(buf, end, pff[i].name, default_str_spec);
if (buf < end)
*buf = '=';
buf++;
buf = number(buf, end, (flags >> pff[i].shift) & pff[i].mask,
*pff[i].spec);

append = true;
}

return buf;
}

static noinline_for_stack
char *flags_string(char *buf, char *end, void *flags_ptr,
struct printf_spec spec, const char *fmt)
Expand All @@ -1928,11 +1988,7 @@ char *flags_string(char *buf, char *end, void *flags_ptr,

switch (fmt[1]) {
case 'p':
flags = *(unsigned long *)flags_ptr;
/* Remove zone id */
flags &= (1UL << NR_PAGEFLAGS) - 1;
names = pageflag_names;
break;
return format_page_flags(buf, end, *(unsigned long *)flags_ptr);
case 'v':
flags = *(unsigned long *)flags_ptr;
names = vmaflag_names;
Expand Down

0 comments on commit c244297

Please sign in to comment.