* Ingo Molnar <mi...@kernel.org> wrote:

> 
> * Greg KH <gre...@linuxfoundation.org> wrote:
> 
> > On Mon, Nov 14, 2016 at 10:16:55AM -0800, Christoph Hellwig wrote:
> > > On Mon, Nov 14, 2016 at 06:39:48PM +0100, Peter Zijlstra wrote:
> > > > Since we need to change the implementation, stop exposing internals.
> > > > 
> > > > Provide kref_read() to read the current reference count; typically
> > > > used for debug messages.
> > > 
> > > Can we just provide a printk specifier for a kref value instead as
> > > that is the only valid use case for reading the value?
> > 
> > Yeah, that would be great as no one should be doing anything
> > logic-related based on the kref value.
> 
> Find below a patch that implements %pAk for 'struct kref' count printing and
> %pAr for atomic_t counter printing.
> 
> This is against vanilla upstream.

The patch below is against Peter's refcount series. Note that this patch 
depends 
on this patch in Peter's series:

  kref: Implement using refcount_t

Thanks,

        Ingo

==================================>
Subject: printk, locking/atomics, kref: Introduce new %pAr and %pAk format 
string options for atomic_t and 'struct kref'
From: Ingo Molnar <mi...@kernel.org>
Date: Tue Nov 15 08:53:14 CET 2016

A decade of kref internals exposed to driver writers has proven that
exposing internals to them is a bad idea.

Make the bad patterns a bit easier to detect and allow cleaner
printouts by offering two new printk format string extensions:

        %pAr - print the atomic_t count in decimal
        %pAk - print the struct kref count in decimal

Also add printf testcases:

  [    0.328495] test_printf: all 268 tests passed

Cc: Arnaldo Carvalho de Melo <a...@kernel.org>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Cc: Linus Torvalds <torva...@linux-foundation.org>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Thomas Gleixner <t...@linutronix.de>
Signed-off-by: Ingo Molnar <mi...@kernel.org>
---
 Documentation/printk-formats.txt |   10 +++++++++
 lib/test_printf.c                |   28 ++++++++++++++++++++++++++
 lib/vsprintf.c                   |   42 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)

Index: tip/Documentation/printk-formats.txt
===================================================================
--- tip.orig/Documentation/printk-formats.txt
+++ tip/Documentation/printk-formats.txt
@@ -316,6 +316,16 @@ Flags bitfields such as page flags, gfp_
 
        Passed by reference.
 
+atomic variables such atomic_t or struct kref:
+
+       %pAr    atomic_t count
+       %pAk    struct kref count
+
+       For printing the current count value of atomic variables. This is
+       preferred to accessing the counts directly.
+
+       Passed by reference.
+
 Network device features:
 
        %pNF    0x000000000000c000
Index: tip/lib/test_printf.c
===================================================================
--- tip.orig/lib/test_printf.c
+++ tip/lib/test_printf.c
@@ -20,6 +20,8 @@
 #include <linux/gfp.h>
 #include <linux/mm.h>
 
+#include <linux/kref.h>
+
 #define BUF_SIZE 256
 #define PAD_SIZE 16
 #define FILL_CHAR '$'
@@ -462,6 +464,31 @@ flags(void)
        kfree(cmp_buffer);
 }
 
+/*
+ * Testcases for %pAr (atomic_t) and %pAk (struct kref) count printing:
+ */
+static void __init test_atomics__atomic_t(void)
+{
+       atomic_t count = ATOMIC_INIT(1);
+
+       test("1", "%pAr", &count);
+}
+
+static void __init test_atomics__kref(void)
+{
+       struct kref kref;
+
+       kref_init(&kref);
+
+       test("1", "%pAk", &kref);
+}
+
+static void __init test_atomics(void)
+{
+       test_atomics__atomic_t();
+       test_atomics__kref();
+}
+
 static void __init
 test_pointer(void)
 {
@@ -481,6 +508,7 @@ test_pointer(void)
        bitmap();
        netdev_features();
        flags();
+       test_atomics();
 }
 
 static int __init
Index: tip/lib/vsprintf.c
===================================================================
--- tip.orig/lib/vsprintf.c
+++ tip/lib/vsprintf.c
@@ -38,6 +38,8 @@
 
 #include "../mm/internal.h"    /* For the trace_print_flags arrays */
 
+#include <linux/kref.h>
+
 #include <asm/page.h>          /* for PAGE_SIZE */
 #include <asm/sections.h>      /* for dereference_function_descriptor() */
 #include <asm/byteorder.h>     /* cpu_to_le16 */
@@ -1470,6 +1472,40 @@ char *flags_string(char *buf, char *end,
        return format_flags(buf, end, flags, names);
 }
 
+static noinline_for_stack
+char *atomic_var(char *buf, char *end, void *atomic_ptr, const char *fmt)
+{
+       unsigned long num;
+       const struct printf_spec numspec = {
+               .flags          = SPECIAL|SMALL,
+               .field_width    = -1,
+               .precision      = -1,
+               .base           = 10,
+       };
+
+       switch (fmt[1]) {
+               case 'r':
+               {
+                       atomic_t *count_p = (void *)atomic_ptr;
+
+                       num = atomic_read(count_p);
+                       break;
+               }
+               case 'k':
+               {
+                       struct kref *kref_p = (void *)atomic_ptr;
+
+                       num = refcount_read(&kref_p->refcount);
+                       break;
+               }
+               default:
+                       WARN_ONCE(1, "Unsupported atomics modifier: %c\n", 
fmt[1]);
+                       return buf;
+       }
+
+       return number(buf, end, num, numspec);
+}
+
 int kptr_restrict __read_mostly;
 
 /*
@@ -1563,6 +1599,10 @@ int kptr_restrict __read_mostly;
  *       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
+ * - 'A' For the count of atomic variables to be printed.
+ *       Supported flags given by option:
+ *        r atomic_t    ('r'aw count)
+ *        k struct kref ('k'ref count)
  *
  * ** Please update also Documentation/printk-formats.txt when making changes 
**
  *
@@ -1718,6 +1758,8 @@ char *pointer(const char *fmt, char *buf
 
        case 'G':
                return flags_string(buf, end, ptr, fmt);
+       case 'A':
+               return atomic_var(buf, end, ptr, fmt);
        }
        spec.flags |= SMALL;
        if (spec.field_width == -1) {

Reply via email to