From: Michal Hocko <mhocko@kernel.org>
To: Vlastimil Babka <vbabka@suse.cz>
Cc: Andrew Morton <akpm@linux-foundation.org>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
Steven Rostedt <rostedt@goodmis.org>,
Peter Zijlstra <peterz@infradead.org>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Ingo Molnar <mingo@redhat.com>,
Rasmus Villemoes <linux@rasmusvillemoes.dk>,
Joonsoo Kim <iamjoonsoo.kim@lge.com>,
Minchan Kim <minchan@kernel.org>,
Sasha Levin <sasha.levin@oracle.com>,
"Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>,
Mel Gorman <mgorman@suse.de>
Subject: Re: [PATCH v3 05/14] mm, printk: introduce new format string for flags
Date: Thu, 7 Jan 2016 10:53:24 +0100 [thread overview]
Message-ID: <20160107095324.GE27868@dhcp22.suse.cz> (raw)
In-Reply-To: <1450429406-7081-6-git-send-email-vbabka@suse.cz>
On Fri 18-12-15 10:03:17, Vlastimil Babka wrote:
> In mm we use several kinds of flags bitfields that are sometimes printed for
> debugging purposes, or exported to userspace via sysfs. To make them easier to
> interpret independently on kernel version and config, we want to dump also the
> symbolic flag names. So far this has been done with repeated calls to
> pr_cont(), which is unreliable on SMP, and not usable for e.g. sysfs export.
>
> To get a more reliable and universal solution, this patch extends printk()
> format string for pointers to handle the page flags (%pgp), gfp_flags (%pgg)
> and vma flags (%pgv). Existing users of dump_flag_names() are converted and
> simplified.
>
> It would be possible to pass flags by value instead of pointer, but the %p
> format string for pointers already has extensions for various kernel
> structures, so it's a good fit, and the extra indirection in a non-critical
> path is negligible.
>
> [linux@rasmusvillemoes.dk: lots of good implementation suggestions]
> Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>
> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: Sasha Levin <sasha.levin@oracle.com>
> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
> Cc: Mel Gorman <mgorman@suse.de>
> Cc: Michal Hocko <mhocko@suse.cz>
With my limitted familiarity of the printk code this looks reasonable.
Acked-by: Michal Hocko <mhocko@suse.com>
> ---
> Documentation/printk-formats.txt | 18 ++++++++++
> include/linux/mmdebug.h | 6 ++++
> lib/test_printf.c | 53 ++++++++++++++++++++++++++++
> lib/vsprintf.c | 75 ++++++++++++++++++++++++++++++++++++++++
> mm/debug.c | 34 ++++++++++--------
> mm/internal.h | 6 ++++
> 6 files changed, 178 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt
> index 602fee945d1d..e878e99ad686 100644
> --- a/Documentation/printk-formats.txt
> +++ b/Documentation/printk-formats.txt
> @@ -292,6 +292,24 @@ Raw pointer value SHOULD be printed with %p. The kernel supports
>
> Passed by reference.
>
> +Flags bitfields such as page flags, gfp_flags:
> +
> + %pgp referenced|uptodate|lru|active|private
> + %pgg GFP_USER|GFP_DMA32|GFP_NOWARN
> + %pgv read|exec|mayread|maywrite|mayexec|denywrite
> +
> + For printing flags bitfields as a collection of symbolic constants that
> + would construct the value. The type of flags is given by the third
> + character. Currently supported are [p]age flags, [v]ma_flags (both
> + expect unsigned long *) and [g]fp_flags (expects gfp_t *). The flag
> + names and print order depends on the particular type.
> +
> + Note that this format should not be used directly in TP_printk() part
> + of a tracepoint. Instead, use the show_*_flags() functions from
> + <trace/events/mmflags.h>.
> +
> + Passed by reference.
> +
> Network device features:
>
> %pNF 0x000000000000c000
> diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h
> index c447d8055e50..2c8286cf162e 100644
> --- a/include/linux/mmdebug.h
> +++ b/include/linux/mmdebug.h
> @@ -2,11 +2,17 @@
> #define LINUX_MM_DEBUG_H 1
>
> #include <linux/stringify.h>
> +#include <linux/types.h>
> +#include <linux/tracepoint.h>
>
> struct page;
> struct vm_area_struct;
> struct mm_struct;
>
> +extern const struct trace_print_flags pageflag_names[];
> +extern const struct trace_print_flags vmaflag_names[];
> +extern const struct trace_print_flags gfpflag_names[];
> +
> extern void dump_page(struct page *page, const char *reason);
> extern void dump_page_badflags(struct page *page, const char *reason,
> unsigned long badflags);
> diff --git a/lib/test_printf.c b/lib/test_printf.c
> index 4f6ae60433bc..5c7c8ebf3689 100644
> --- a/lib/test_printf.c
> +++ b/lib/test_printf.c
> @@ -17,6 +17,9 @@
> #include <linux/socket.h>
> #include <linux/in.h>
>
> +#include <linux/gfp.h>
> +#include <linux/mm.h>
> +
> #define BUF_SIZE 256
> #define PAD_SIZE 16
> #define FILL_CHAR '$'
> @@ -411,6 +414,55 @@ netdev_features(void)
> }
>
> static void __init
> +flags(void)
> +{
> + unsigned long flags;
> + gfp_t gfp;
> + char *cmp_buffer;
> +
> + flags = 0;
> + test("", "%pgp", &flags);
> +
> + /* Page flags should filter the zone id */
> + flags = 1UL << NR_PAGEFLAGS;
> + test("", "%pgp", &flags);
> +
> + flags |= 1UL << PG_uptodate | 1UL << PG_dirty | 1UL << PG_lru
> + | 1UL << PG_active | 1UL << PG_swapbacked;
> + test("uptodate|dirty|lru|active|swapbacked", "%pgp", &flags);
> +
> +
> + flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC
> + | VM_DENYWRITE;
> + test("read|exec|mayread|maywrite|mayexec|denywrite", "%pgv", &flags);
> +
> + gfp = GFP_TRANSHUGE;
> + test("GFP_TRANSHUGE", "%pgg", &gfp);
> +
> + gfp = GFP_ATOMIC|__GFP_DMA;
> + test("GFP_ATOMIC|GFP_DMA", "%pgg", &gfp);
> +
> + 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);
> + test(cmp_buffer, "%pgg", &gfp);
> +
> + snprintf(cmp_buffer, BUF_SIZE, "__GFP_ATOMIC|%#lx",
> + (unsigned long) gfp);
> + gfp |= __GFP_ATOMIC;
> + test(cmp_buffer, "%pgg", &gfp);
> +
> + kfree(cmp_buffer);
> +}
> +
> +static void __init
> test_pointer(void)
> {
> plain();
> @@ -428,6 +480,7 @@ test_pointer(void)
> struct_clk();
> bitmap();
> netdev_features();
> + flags();
> }
>
> static int __init
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index cf064b17c50c..a254973d005d 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -32,6 +32,8 @@
> #include <linux/cred.h>
> #include <net/addrconf.h>
>
> +#include "../mm/internal.h" /* For the trace_print_flags arrays */
> +
> #include <asm/page.h> /* for PAGE_SIZE */
> #include <asm/sections.h> /* for dereference_function_descriptor() */
> #include <asm/byteorder.h> /* cpu_to_le16 */
> @@ -1372,6 +1374,72 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
> }
> }
>
> +static
> +char *format_flags(char *buf, char *end, unsigned long flags,
> + const struct trace_print_flags *names)
> +{
> + unsigned long mask;
> + const struct printf_spec strspec = {
> + .field_width = -1,
> + .precision = -1,
> + };
> + const struct printf_spec numspec = {
> + .flags = SPECIAL|SMALL,
> + .field_width = -1,
> + .precision = -1,
> + .base = 16,
> + };
> +
> + for ( ; flags && names->name; names++) {
> + mask = names->mask;
> + if ((flags & mask) != mask)
> + continue;
> +
> + buf = string(buf, end, names->name, strspec);
> +
> + flags &= ~mask;
> + if (flags) {
> + if (buf < end)
> + *buf = '|';
> + buf++;
> + }
> + }
> +
> + if (flags)
> + buf = number(buf, end, flags, numspec);
> +
> + return buf;
> +}
> +
> +static noinline_for_stack
> +char *flags_string(char *buf, char *end, void *flags_ptr, const char *fmt)
> +{
> + unsigned long flags;
> + const struct trace_print_flags *names;
> +
> + switch (fmt[1]) {
> + case 'p':
> + flags = *(unsigned long *)flags_ptr;
> + /* Remove zone id */
> + flags &= (1UL << NR_PAGEFLAGS) - 1;
> + names = pageflag_names;
> + break;
> + case 'v':
> + flags = *(unsigned long *)flags_ptr;
> + names = vmaflag_names;
> + break;
> + case 'g':
> + flags = *(gfp_t *)flags_ptr;
> + names = gfpflag_names;
> + break;
> + default:
> + WARN_ONCE(1, "Unsupported flags modifier: %c\n", fmt[1]);
> + return buf;
> + }
> +
> + return format_flags(buf, end, flags, names);
> +}
> +
> int kptr_restrict __read_mostly;
>
> /*
> @@ -1459,6 +1527,11 @@ int kptr_restrict __read_mostly;
> * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
> * (legacy clock framework) of the clock
> * - 'Cr' For a clock, it prints the current rate of the clock
> + * - 'g' For flags to be printed as a collection of symbolic strings that would
> + * construct the specific value. Supported flags given by option:
> + * p page flags (see struct page) given as pointer to unsigned long
> + * g gfp flags (GFP_* and __GFP_*) given as pointer to gfp_t
> + * v vma flags (VM_*) given as pointer to unsigned long
> *
> * ** Please update also Documentation/printk-formats.txt when making changes **
> *
> @@ -1611,6 +1684,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
> return dentry_name(buf, end,
> ((const struct file *)ptr)->f_path.dentry,
> spec, fmt);
> + case 'g':
> + return flags_string(buf, end, ptr, fmt);
> }
> spec.flags |= SMALL;
> if (spec.field_width == -1) {
> diff --git a/mm/debug.c b/mm/debug.c
> index 85f71e4ce59f..79621a5ce46f 100644
> --- a/mm/debug.c
> +++ b/mm/debug.c
> @@ -11,12 +11,21 @@
> #include <linux/memcontrol.h>
> #include <trace/events/mmflags.h>
>
> -static const struct trace_print_flags pageflag_names[] = {
> - __def_pageflag_names
> +#include "internal.h"
> +
> +const struct trace_print_flags pageflag_names[] = {
> + __def_pageflag_names,
> + {0, NULL}
> +};
> +
> +const struct trace_print_flags gfpflag_names[] = {
> + __def_gfpflag_names,
> + {0, NULL}
> };
>
> -static const struct trace_print_flags gfpflag_names[] = {
> - __def_gfpflag_names
> +const struct trace_print_flags vmaflag_names[] = {
> + __def_vmaflag_names,
> + {0, NULL}
> };
>
> static void dump_flags(unsigned long flags,
> @@ -58,14 +67,15 @@ void dump_page_badflags(struct page *page, const char *reason,
> if (PageCompound(page))
> pr_cont(" compound_mapcount: %d", compound_mapcount(page));
> pr_cont("\n");
> - BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS);
> - dump_flags(page->flags, pageflag_names, ARRAY_SIZE(pageflag_names));
> + BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS + 1);
> + dump_flags(page->flags, pageflag_names,
> + ARRAY_SIZE(pageflag_names) - 1);
> if (reason)
> pr_alert("page dumped because: %s\n", reason);
> if (page->flags & badflags) {
> pr_alert("bad because of flags:\n");
> - dump_flags(page->flags & badflags,
> - pageflag_names, ARRAY_SIZE(pageflag_names));
> + dump_flags(page->flags & badflags, pageflag_names,
> + ARRAY_SIZE(pageflag_names) - 1);
> }
> #ifdef CONFIG_MEMCG
> if (page->mem_cgroup)
> @@ -81,10 +91,6 @@ EXPORT_SYMBOL(dump_page);
>
> #ifdef CONFIG_DEBUG_VM
>
> -static const struct trace_print_flags vmaflag_names[] = {
> - __def_vmaflag_names
> -};
> -
> void dump_vma(const struct vm_area_struct *vma)
> {
> pr_emerg("vma %p start %p end %p\n"
> @@ -96,7 +102,7 @@ void dump_vma(const struct vm_area_struct *vma)
> (unsigned long)pgprot_val(vma->vm_page_prot),
> vma->anon_vma, vma->vm_ops, vma->vm_pgoff,
> vma->vm_file, vma->vm_private_data);
> - dump_flags(vma->vm_flags, vmaflag_names, ARRAY_SIZE(vmaflag_names));
> + dump_flags(vma->vm_flags, vmaflag_names, ARRAY_SIZE(vmaflag_names) - 1);
> }
> EXPORT_SYMBOL(dump_vma);
>
> @@ -168,7 +174,7 @@ void dump_mm(const struct mm_struct *mm)
> );
>
> dump_flags(mm->def_flags, vmaflag_names,
> - ARRAY_SIZE(vmaflag_names));
> + ARRAY_SIZE(vmaflag_names) - 1);
> }
>
> #endif /* CONFIG_DEBUG_VM */
> diff --git a/mm/internal.h b/mm/internal.h
> index d01a41c00bec..8d2f8e3fd7d8 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -14,6 +14,7 @@
> #include <linux/fs.h>
> #include <linux/mm.h>
> #include <linux/pagemap.h>
> +#include <linux/tracepoint-defs.h>
>
> /*
> * The set of flags that only affect watermark checking and reclaim
> @@ -441,4 +442,9 @@ static inline void try_to_unmap_flush_dirty(void)
> }
>
> #endif /* CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH */
> +
> +extern const struct trace_print_flags pageflag_names[];
> +extern const struct trace_print_flags vmaflag_names[];
> +extern const struct trace_print_flags gfpflag_names[];
> +
> #endif /* __MM_INTERNAL_H */
> --
> 2.6.3
--
Michal Hocko
SUSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
next prev parent reply other threads:[~2016-01-07 9:53 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-18 9:03 [PATCH v3 00/14] mm flags in printk, page_owner improvements for debugging Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 01/14] tracepoints: move trace_print_flags definitions to tracepoint-defs.h Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 02/14] mm, tracing: make show_gfp_flags() up to date Vlastimil Babka
2016-01-07 9:29 ` Michal Hocko
2015-12-18 9:03 ` [PATCH v3 03/14] tools, perf: make gfp_compact_table " Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 04/14] mm, tracing: unify mm flags handling in tracepoints and printk Vlastimil Babka
2016-01-07 9:46 ` Michal Hocko
2015-12-18 9:03 ` [PATCH v3 05/14] mm, printk: introduce new format string for flags Vlastimil Babka
2016-01-07 9:53 ` Michal Hocko [this message]
2015-12-18 9:03 ` [PATCH v3 06/14] mm, debug: replace dump_flags() with the new printk formats Vlastimil Babka
2016-01-07 9:57 ` Michal Hocko
2015-12-18 9:03 ` [PATCH v3 07/14] mm, page_alloc: print symbolic gfp_flags on allocation failure Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 08/14] mm, oom: print symbolic gfp_flags in oom warning Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 09/14] mm, page_owner: print migratetype of page and pageblock, symbolic flags Vlastimil Babka
2016-01-07 10:06 ` Michal Hocko
2015-12-18 9:03 ` [PATCH v3 10/14] mm, page_owner: convert page_owner_inited to static key Vlastimil Babka
2016-01-07 10:21 ` Michal Hocko
2015-12-18 9:03 ` [PATCH v3 11/14] mm, page_owner: copy page owner info during migration Vlastimil Babka
2016-01-07 10:44 ` Michal Hocko
2015-12-18 9:03 ` [PATCH v3 12/14] mm, page_owner: track and print last migrate reason Vlastimil Babka
2016-01-07 10:54 ` Michal Hocko
2016-01-07 13:17 ` Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 13/14] mm, page_owner: dump page owner info from dump_page() Vlastimil Babka
2015-12-18 9:03 ` [PATCH v3 14/14] mm, debug: move bad flags printing to bad_page() Vlastimil Babka
2016-01-07 13:10 ` Michal Hocko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160107095324.GE27868@dhcp22.suse.cz \
--to=mhocko@kernel.org \
--cc=acme@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=iamjoonsoo.kim@lge.com \
--cc=kirill.shutemov@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux@rasmusvillemoes.dk \
--cc=mgorman@suse.de \
--cc=minchan@kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=sasha.levin@oracle.com \
--cc=vbabka@suse.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox