mm, printk: introduce new format %pGt for page_type

%pGp format is used to display 'flags' field of a struct page.  However,
some page flags (i.e.  PG_buddy, see page-flags.h for more details) are
stored in page_type field.  To display human-readable output of page_type,
introduce %pGt format.

It is important to note the meaning of bits are different in page_type. 
if page_type is 0xffffffff, no flags are set.  Setting PG_buddy
(0x00000080) flag results in a page_type of 0xffffff7f.  Clearing a bit
actually means setting a flag.  Bits in page_type are inverted when
displaying type names.

Only values for which page_type_has_type() returns true are considered as
page_type, to avoid confusion with mapcount values.  if it returns false,
only raw values are displayed and not page type names.

Link: https://lkml.kernel.org/r/20230130042514.2418-3-42.hyeyoo@gmail.com
Signed-off-by: Hyeonggon Yoo <42.hyeyoo@gmail.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>	[vsprintf part]
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Joe Perches <joe@perches.com>
Cc: John Ogness <john.ogness@linutronix.de>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Steven Rostedt (Google) <rostedt@goodmis.org>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Hyeonggon Yoo 2023-01-30 13:25:13 +09:00 committed by Andrew Morton
parent e26fcc02c7
commit 4c85c0be3d
7 changed files with 78 additions and 6 deletions

View File

@ -575,20 +575,26 @@ The field width is passed by value, the bitmap is passed by reference.
Helper macros cpumask_pr_args() and nodemask_pr_args() are available to ease Helper macros cpumask_pr_args() and nodemask_pr_args() are available to ease
printing cpumask and nodemask. printing cpumask and nodemask.
Flags bitfields such as page flags, gfp_flags Flags bitfields such as page flags, page_type, gfp_flags
--------------------------------------------- --------------------------------------------------------
:: ::
%pGp 0x17ffffc0002036(referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff) %pGp 0x17ffffc0002036(referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff)
%pGt 0xffffff7f(buddy)
%pGg GFP_USER|GFP_DMA32|GFP_NOWARN %pGg GFP_USER|GFP_DMA32|GFP_NOWARN
%pGv read|exec|mayread|maywrite|mayexec|denywrite %pGv read|exec|mayread|maywrite|mayexec|denywrite
For printing flags bitfields as a collection of symbolic constants that For printing flags bitfields as a collection of symbolic constants that
would construct the value. The type of flags is given by the third would construct the value. The type of flags is given by the third
character. Currently supported are [p]age flags, [v]ma_flags (both character. Currently supported are:
expect ``unsigned long *``) and [g]fp_flags (expects ``gfp_t *``). The flag
names and print order depends on the particular type. - p - [p]age flags, expects value of type (``unsigned long *``)
- t - page [t]ype, expects value of type (``unsigned int *``)
- v - [v]ma_flags, expects value of type (``unsigned long *``)
- g - [g]fp_flags, expects value of type (``gfp_t *``)
The flag names and print order depends on the particular type.
Note that this format should not be used directly in the Note that this format should not be used directly in the
:c:func:`TP_printk()` part of a tracepoint. Instead, use the show_*_flags() :c:func:`TP_printk()` part of a tracepoint. Instead, use the show_*_flags()

View File

@ -926,9 +926,14 @@ static inline bool is_page_hwpoison(struct page *page)
#define PageType(page, flag) \ #define PageType(page, flag) \
((page->page_type & (PAGE_TYPE_BASE | flag)) == PAGE_TYPE_BASE) ((page->page_type & (PAGE_TYPE_BASE | flag)) == PAGE_TYPE_BASE)
static inline int page_type_has_type(unsigned int page_type)
{
return (int)page_type < PAGE_MAPCOUNT_RESERVE;
}
static inline int page_has_type(struct page *page) static inline int page_has_type(struct page *page)
{ {
return (int)page->page_type < PAGE_MAPCOUNT_RESERVE; return page_type_has_type(page->page_type);
} }
#define PAGE_TYPE_OPS(uname, lname) \ #define PAGE_TYPE_OPS(uname, lname) \

View File

@ -141,6 +141,14 @@ IF_HAVE_PG_SKIP_KASAN_POISON(skip_kasan_poison)
__def_pageflag_names \ __def_pageflag_names \
) : "none" ) : "none"
#define DEF_PAGETYPE_NAME(_name) { PG_##_name, __stringify(_name) }
#define __def_pagetype_names \
DEF_PAGETYPE_NAME(offline), \
DEF_PAGETYPE_NAME(guard), \
DEF_PAGETYPE_NAME(table), \
DEF_PAGETYPE_NAME(buddy)
#if defined(CONFIG_X86) #if defined(CONFIG_X86)
#define __VM_ARCH_SPECIFIC_1 {VM_PAT, "pat" } #define __VM_ARCH_SPECIFIC_1 {VM_PAT, "pat" }
#elif defined(CONFIG_PPC) #elif defined(CONFIG_PPC)

View File

@ -642,12 +642,26 @@ page_flags_test(int section, int node, int zone, int last_cpupid,
test(cmp_buf, "%pGp", &flags); test(cmp_buf, "%pGp", &flags);
} }
static void __init page_type_test(unsigned int page_type, const char *name,
char *cmp_buf)
{
unsigned long size;
size = scnprintf(cmp_buf, BUF_SIZE, "%#x(", page_type);
if (page_type_has_type(page_type))
size += scnprintf(cmp_buf + size, BUF_SIZE - size, "%s", name);
snprintf(cmp_buf + size, BUF_SIZE - size, ")");
test(cmp_buf, "%pGt", &page_type);
}
static void __init static void __init
flags(void) flags(void)
{ {
unsigned long flags; unsigned long flags;
char *cmp_buffer; char *cmp_buffer;
gfp_t gfp; gfp_t gfp;
unsigned int page_type;
cmp_buffer = kmalloc(BUF_SIZE, GFP_KERNEL); cmp_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!cmp_buffer) if (!cmp_buffer)
@ -687,6 +701,18 @@ flags(void)
gfp |= __GFP_HIGH; gfp |= __GFP_HIGH;
test(cmp_buffer, "%pGg", &gfp); test(cmp_buffer, "%pGg", &gfp);
page_type = ~0;
page_type_test(page_type, "", cmp_buffer);
page_type = 10;
page_type_test(page_type, "", cmp_buffer);
page_type = ~PG_buddy;
page_type_test(page_type, "buddy", cmp_buffer);
page_type = ~(PG_table | PG_buddy);
page_type_test(page_type, "table|buddy", cmp_buffer);
kfree(cmp_buffer); kfree(cmp_buffer);
} }

View File

@ -2052,6 +2052,25 @@ char *format_page_flags(char *buf, char *end, unsigned long flags)
return buf; return buf;
} }
static
char *format_page_type(char *buf, char *end, unsigned int page_type)
{
buf = number(buf, end, page_type, default_flag_spec);
if (buf < end)
*buf = '(';
buf++;
if (page_type_has_type(page_type))
buf = format_flags(buf, end, ~page_type, pagetype_names);
if (buf < end)
*buf = ')';
buf++;
return buf;
}
static noinline_for_stack static noinline_for_stack
char *flags_string(char *buf, char *end, void *flags_ptr, char *flags_string(char *buf, char *end, void *flags_ptr,
struct printf_spec spec, const char *fmt) struct printf_spec spec, const char *fmt)
@ -2065,6 +2084,8 @@ char *flags_string(char *buf, char *end, void *flags_ptr,
switch (fmt[1]) { switch (fmt[1]) {
case 'p': case 'p':
return format_page_flags(buf, end, *(unsigned long *)flags_ptr); return format_page_flags(buf, end, *(unsigned long *)flags_ptr);
case 't':
return format_page_type(buf, end, *(unsigned int *)flags_ptr);
case 'v': case 'v':
flags = *(unsigned long *)flags_ptr; flags = *(unsigned long *)flags_ptr;
names = vmaflag_names; names = vmaflag_names;

View File

@ -36,6 +36,11 @@ const struct trace_print_flags pageflag_names[] = {
{0, NULL} {0, NULL}
}; };
const struct trace_print_flags pagetype_names[] = {
__def_pagetype_names,
{0, NULL}
};
const struct trace_print_flags gfpflag_names[] = { const struct trace_print_flags gfpflag_names[] = {
__def_gfpflag_names, __def_gfpflag_names,
{0, NULL} {0, NULL}

View File

@ -802,6 +802,7 @@ static inline void flush_tlb_batched_pending(struct mm_struct *mm)
#endif /* CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH */ #endif /* CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH */
extern const struct trace_print_flags pageflag_names[]; extern const struct trace_print_flags pageflag_names[];
extern const struct trace_print_flags pagetype_names[];
extern const struct trace_print_flags vmaflag_names[]; extern const struct trace_print_flags vmaflag_names[];
extern const struct trace_print_flags gfpflag_names[]; extern const struct trace_print_flags gfpflag_names[];