All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/28] Printbufs (now with more printbufs!)
@ 2022-05-19 17:23 Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
                   ` (28 more replies)
  0 siblings, 29 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

So there's a lot of new stuff since the first posting:

 - Printbufs have been broken up into multiple patches that each add distinct
   functionality - this is intended to make it easier to review and to see
   what's used for what

 - Printbufs now support both auto-heap allocated buffers, and external/static
   buffers: this was required for the vsprintf.c refactoring, and means they're
   (almost) a direct replacement for seq_buf

 - The big thing: a new %pf(%p) format string extension for calling pretty
   printers from printf (idea from Matthew Wilcox)

   This is intended to replace most of our other format string extensions: e.g.
   instead of writing
     printf("%pg", bdev);

   You'd now write
     printf("%pf(%p)", bdev_name, bdev);

   The advantage of this is that pretty printers no longer have to live in
   lib/vsprintf.c, and they're much more discoverable - you can cscope to the
   pretty printer!

   And my hope is that this will help induce people to write lots more pretty
   printers; since they can now live with the code they're printing and don't
   require touching code in vsprintf.c, there's less friction to creating new
   ones.

   We hope to standardize this extension as %(%p), but since gcc's printf format
   checking doesn't yet understand that we're going with %pf(%p) for now.

   Currently, we only support pointer arguments to pretty-printers. I think we
   can improve this in the future to support at least integer arguments as well,
   i.e. "%(%u)" will eventually work. This will require using libffi to do it
   correctly, but it looks like libffi is nearly suitable for in kernel use (it
   supports all linux architectures, and configured with the features we want it
   compiles down to practically nothing).

 - Massive vsprintf.c refactoring

   printbufs are now the core data structure used by vsprintf.c - we're not
   passing around a bunch of raw char pointers anymore! yay!

   This gets us a sane standard calling convention for pretty printers - i.e.,
   we need this for %pf(%p).

   Couple notes on the refactoring:

   - printf_spec has become a dumping ground of state, passed everywhere and
     used inconsistently. The refactoring attempts to improve this, and
     centralize printf_spec handling as much as possible near/in the top level
     code that handles format strings. Some %p extensions use
     field/width/precision in nonstandard ways; the refactoring patches make it
     clearer where this is going on.

   - a _lot_ of pretty printers were allocating secondary buffers on the stack,
     mainly to avoid ever writing past the terminating null in the output
     buffer. There was a test that checked for this, but it had no documentation
     where the requirement came from nor does that requirement make any sense,
     so I deleted it (if anyone knows anything about it, speak up!). The code
     now takes the approach of just writing to the output buffer and then
     truncating afterwards if required by the precision argument.

     Yay, less stack usage!

   - format string parsing is still a mess: I'd like to consolidate that to only
     happen in one place, but that's going to be a much more involved
     refactoring - and if we just switch to new-style calling pretty printers
     directly, we'll be able to just delete all that code.

 - More seq_buf conversions

   Using printbufs to clean up vsprintf.c meant adding a second, non
   heap-allocated mode, so printbufs now do almost everything seq_buf does -
   seq_buf has a read_pos member for some reason (tracing?) that I didn't get
   into.

   So now seq_buf is just used by the tracing code, and that can also probably
   be converted to printbuf, but seq_buf is Steven's thing so I'll let him take
   a look before getting into that.

Kent Overstreet (28):
  lib/printbuf: New data structure for printing strings
  vsprintf: Convert to printbuf
  vsprintf: %pf(%p)
  lib/string_helpers: string_get_size() now returns characters wrote
  lib/printbuf: Heap allocation
  lib/printbuf: Tabstops, indenting
  lib/printbuf: Unit specifiers
  lib/pretty-printers: pr_string_option(), pr_bitflags()
  vsprintf: Improve number()
  vsprintf: pr_u64_minwidth(), pr_u64()
  vsprintf: Lift pr_hex_bytes() out from hex_string()
  test_printf: Drop requirement that sprintf not write past nul
  vsprintf: Start consolidating printf_spec handling
  vsprintf: Refactor resource_string()
  vsprintf: Refactor fourcc_string()
  vsprintf: Refactor ip_addr_string()
  vsprintf: Refactor mac_address_string()
  vsprintf: time_and_date() no longer takes printf_spec
  vsprintf: flags_string() no longer takes printf_spec
  vsprintf: Refactor device_node_string, fwnode_string
  vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string
  Input/joystick/analog: Convert from seq_buf -> printbuf
  mm/memcontrol.c: Convert to printbuf
  clk: tegra: bpmp: Convert to printbuf
  tools/testing/nvdimm: Convert to printbuf
  powerpc: Convert to printbuf
  x86/resctrl: Convert to printbuf
  PCI/P2PDMA: Convert to printbuf

 Documentation/core-api/printk-formats.rst |   19 +
 arch/powerpc/kernel/process.c             |   16 +-
 arch/powerpc/kernel/security.c            |   75 +-
 arch/powerpc/platforms/pseries/papr_scm.c |   34 +-
 arch/x86/kernel/cpu/resctrl/rdtgroup.c    |   16 +-
 drivers/clk/tegra/clk-bpmp.c              |   21 +-
 drivers/input/joystick/analog.c           |   37 +-
 drivers/pci/p2pdma.c                      |   17 +-
 include/linux/kernel.h                    |    4 +
 include/linux/pretty-printers.h           |   11 +
 include/linux/printbuf.h                  |  225 +++
 include/linux/string_helpers.h            |    8 +-
 lib/Makefile                              |    2 +-
 lib/pretty-printers.c                     |   81 ++
 lib/printbuf.c                            |  252 ++++
 lib/string_helpers.c                      |   18 +-
 lib/test_printf.c                         |   23 +-
 lib/vsprintf.c                            | 1612 ++++++++++-----------
 mm/memcontrol.c                           |   68 +-
 tools/testing/nvdimm/test/ndtest.c        |   22 +-
 20 files changed, 1527 insertions(+), 1034 deletions(-)
 create mode 100644 include/linux/pretty-printers.h
 create mode 100644 include/linux/printbuf.h
 create mode 100644 lib/pretty-printers.c
 create mode 100644 lib/printbuf.c

-- 
2.36.0


^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
@ 2022-05-19 17:23 ` Kent Overstreet
  2022-05-19 18:21   ` Matthew Wilcox
  2022-05-26 15:06   ` Petr Mladek
  2022-05-19 17:23 ` [PATCH v2 02/28] vsprintf: Convert to printbuf Kent Overstreet
                   ` (27 subsequent siblings)
  28 siblings, 2 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This adds printbufs: a printbuf points to a char * buffer and knows the
size of the output buffer as well as the current output position.

Future patches will be adding more features to printbuf, but initially
printbufs are targeted at refactoring and improving our existing code in
lib/vsprintf.c - so this initial printbuf patch has the features
required for that.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/printbuf.h | 94 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 include/linux/printbuf.h

diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
new file mode 100644
index 0000000000..40dc07040d
--- /dev/null
+++ b/include/linux/printbuf.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022 Kent Overstreet */
+
+#ifndef _LINUX_PRINTBUF_H
+#define _LINUX_PRINTBUF_H
+
+#include <linux/string.h>
+
+/*
+ * Printbufs: String buffer for outputting (printing) to, for vsnprintf
+ */
+
+struct printbuf {
+	char			*buf;
+	unsigned		size;
+	unsigned		pos;
+};
+
+static inline unsigned printbuf_remaining(struct printbuf *out)
+{
+	return out->pos < out->size ? out->size - out->pos : 0;
+}
+
+static inline unsigned printbuf_written(struct printbuf *out)
+{
+	return min(out->pos, out->size);
+}
+
+static inline void printbuf_nul_terminate(struct printbuf *out)
+{
+	if (out->pos < out->size)
+		out->buf[out->pos] = 0;
+	else if (out->size)
+		out->buf[out->size - 1] = 0;
+}
+
+static inline void pr_chars(struct printbuf *out, char c, unsigned n)
+{
+	memset(out->buf + out->pos,
+	       c,
+	       min(n, printbuf_remaining(out)));
+	out->pos += n;
+	printbuf_nul_terminate(out);
+}
+
+static inline void __pr_char(struct printbuf *out, char c)
+{
+	if (printbuf_remaining(out))
+		out->buf[out->pos] = c;
+	out->pos++;
+}
+
+static inline void pr_char(struct printbuf *out, char c)
+{
+	__pr_char(out, c);
+	printbuf_nul_terminate(out);
+}
+
+static inline void pr_bytes(struct printbuf *out, const void *b, unsigned n)
+{
+	memcpy(out->buf + out->pos,
+	       b,
+	       min(n, printbuf_remaining(out)));
+	out->pos += n;
+	printbuf_nul_terminate(out);
+}
+
+static inline void pr_str(struct printbuf *out, const char *str)
+{
+	pr_bytes(out, str, strlen(str));
+}
+
+static inline void pr_hex_byte(struct printbuf *out, u8 byte)
+{
+	__pr_char(out, hex_asc_hi(byte));
+	__pr_char(out, hex_asc_lo(byte));
+	printbuf_nul_terminate(out);
+}
+
+static inline void pr_hex_byte_upper(struct printbuf *out, u8 byte)
+{
+	__pr_char(out, hex_asc_upper_hi(byte));
+	__pr_char(out, hex_asc_upper_lo(byte));
+	printbuf_nul_terminate(out);
+}
+
+#define PRINTBUF ((struct printbuf) { .si_units = PRINTBUF_UNITS_10 })
+#define PRINTBUF_EXTERN(_buf, _size)			\
+((struct printbuf) {					\
+	.buf	= _buf,					\
+	.size	= _size,				\
+})
+
+#endif /* _LINUX_PRINTBUF_H */
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 02/28] vsprintf: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
@ 2022-05-19 17:23 ` Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This converts vsnprintf() to printbufs: instead of passing around raw
char * pointers for current buf position and end of buf, we have a real
type!

This makes the calling convention for our existing pretty printers a lot
saner and less error prone, plus printbufs add some new helpers that
make the code smaller and more readable, with a lot less crazy pointer
arithmetic.

There are a lot more refactorings to be done: this patch tries to stick
to just converting the calling conventions, as that needs to be done all
at once in order to avoid introducing a ton of wrappers that will just
be deleted.

Thankfully we have good unit tests for printf, and they have been run
and are all passing with this patch.

We have two new exported functions with this patch:
 - pr_buf(), which is like snprintf but outputs to a printbuf
 - vpr_buf, like vsnprintf

These are the actual core print routines now - vsnprintf() is a wrapper
around vpr_buf().

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/kernel.h         |    4 +
 include/linux/string_helpers.h |    4 +
 lib/string_helpers.c           |   11 +
 lib/vsprintf.c                 | 1261 ++++++++++++++------------------
 4 files changed, 576 insertions(+), 704 deletions(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 33f47a9965..ca9149de7c 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -202,6 +202,10 @@ extern int num_to_str(char *buf, int size,
 
 /* lib/printf utilities */
 
+struct printbuf;
+extern __printf(2, 3) void pr_buf(struct printbuf *out, const char *fmt, ...);
+extern __printf(2, 0) void vpr_buf(struct printbuf *out, const char *fmt, va_list);
+
 extern __printf(2, 3) int sprintf(char *buf, const char * fmt, ...);
 extern __printf(2, 0) int vsprintf(char *buf, const char *, va_list);
 extern __printf(3, 4)
diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h
index 7a22921c9d..2e56b4ce98 100644
--- a/include/linux/string_helpers.h
+++ b/include/linux/string_helpers.h
@@ -10,6 +10,7 @@
 struct device;
 struct file;
 struct task_struct;
+struct printbuf;
 
 /* Descriptions of the types of units to
  * print in */
@@ -71,6 +72,9 @@ static inline int string_escape_mem_any_np(const char *src, size_t isz,
 	return string_escape_mem(src, isz, dst, osz, ESCAPE_ANY_NP, only);
 }
 
+void pr_escaped_string(struct printbuf *out, const char *src, size_t isz,
+		       unsigned int flags, const char *only);
+
 static inline int string_escape_str(const char *src, char *dst, size_t sz,
 		unsigned int flags, const char *only)
 {
diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index 90f9f1b7af..c890e83f59 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -15,6 +15,7 @@
 #include <linux/fs.h>
 #include <linux/limits.h>
 #include <linux/mm.h>
+#include <linux/printbuf.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/string_helpers.h>
@@ -587,6 +588,16 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
 }
 EXPORT_SYMBOL(string_escape_mem);
 
+void pr_escaped_string(struct printbuf *out,
+		       const char *src, size_t isz,
+		       unsigned int flags, const char *only)
+{
+	out->pos += string_escape_mem(src, isz, out->buf + out->pos,
+				      printbuf_remaining(out),
+				      flags, only);
+}
+EXPORT_SYMBOL(pr_escaped_string);
+
 /*
  * Return an allocated string that has been escaped of special characters
  * and double quotes, making it safe to log in quotes.
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3b8129dd37..09b259e030 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -44,6 +44,7 @@
 #ifdef CONFIG_BLOCK
 #include <linux/blkdev.h>
 #endif
+#include <linux/printbuf.h>
 
 #include "../mm/internal.h"	/* For the trace_print_flags arrays */
 
@@ -446,8 +447,8 @@ static_assert(sizeof(struct printf_spec) == 8);
 #define PRECISION_MAX ((1 << 15) - 1)
 
 static noinline_for_stack
-char *number(char *buf, char *end, unsigned long long num,
-	     struct printf_spec spec)
+void number(struct printbuf *out, unsigned long long num,
+	    struct printf_spec spec)
 {
 	/* put_dec requires 2-byte alignment of the buffer. */
 	char tmp[3 * sizeof(num)] __aligned(2);
@@ -507,67 +508,40 @@ char *number(char *buf, char *end, unsigned long long num,
 	if (i > precision)
 		precision = i;
 	/* leading space padding */
-	field_width -= precision;
+	field_width = max(0, field_width - precision);
 	if (!(spec.flags & (ZEROPAD | LEFT))) {
-		while (--field_width >= 0) {
-			if (buf < end)
-				*buf = ' ';
-			++buf;
-		}
+		pr_chars(out, ' ', field_width);
+		field_width = 0;
 	}
 	/* sign */
-	if (sign) {
-		if (buf < end)
-			*buf = sign;
-		++buf;
-	}
+	if (sign)
+		__pr_char(out, sign);
 	/* "0x" / "0" prefix */
 	if (need_pfx) {
-		if (spec.base == 16 || !is_zero) {
-			if (buf < end)
-				*buf = '0';
-			++buf;
-		}
-		if (spec.base == 16) {
-			if (buf < end)
-				*buf = ('X' | locase);
-			++buf;
-		}
+		if (spec.base == 16 || !is_zero)
+			__pr_char(out, '0');
+		if (spec.base == 16)
+			__pr_char(out, 'X' | locase);
 	}
 	/* zero or space padding */
 	if (!(spec.flags & LEFT)) {
 		char c = ' ' + (spec.flags & ZEROPAD);
 
-		while (--field_width >= 0) {
-			if (buf < end)
-				*buf = c;
-			++buf;
-		}
+		pr_chars(out, c, field_width);
+		field_width = 0;
 	}
 	/* hmm even more zero padding? */
-	while (i <= --precision) {
-		if (buf < end)
-			*buf = '0';
-		++buf;
-	}
+	if (precision > i)
+		pr_chars(out, '0', precision - i);
 	/* actual digits of result */
-	while (--i >= 0) {
-		if (buf < end)
-			*buf = tmp[i];
-		++buf;
-	}
+	while (--i >= 0)
+		__pr_char(out, tmp[i]);
 	/* trailing space padding */
-	while (--field_width >= 0) {
-		if (buf < end)
-			*buf = ' ';
-		++buf;
-	}
-
-	return buf;
+	pr_chars(out, ' ', field_width);
 }
 
 static noinline_for_stack
-char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
+void special_hex_number(struct printbuf *out, unsigned long long num, int size)
 {
 	struct printf_spec spec;
 
@@ -577,25 +551,28 @@ char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
 	spec.base = 16;
 	spec.precision = -1;
 
-	return number(buf, end, num, spec);
+	number(out, num, spec);
 }
 
-static void move_right(char *buf, char *end, unsigned len, unsigned spaces)
+/*
+ * inserts @spaces spaces @len from the end of @out
+ */
+static void move_right(struct printbuf *out,
+		       unsigned len, unsigned spaces)
 {
-	size_t size;
-	if (buf >= end)	/* nowhere to put anything */
-		return;
-	size = end - buf;
-	if (size <= spaces) {
-		memset(buf, ' ', size);
-		return;
-	}
-	if (len) {
-		if (len > size - spaces)
-			len = size - spaces;
-		memmove(buf + spaces, buf, len);
-	}
-	memset(buf, ' ', spaces);
+	unsigned move_src = out->pos - len;
+	unsigned move_dst = move_src + spaces;
+	unsigned remaining_from_dst = move_dst < out->size ? out->size - move_dst : 0;
+	unsigned remaining_from_src = move_src < out->size ? out->size - move_src : 0;
+
+	BUG_ON(len > out->pos);
+
+	memmove(out->buf + move_dst,
+		out->buf + move_src,
+		min(remaining_from_dst, len));
+	memset(out->buf + move_src, ' ',
+	       min(remaining_from_src, spaces));
+	out->pos += spaces;
 }
 
 /*
@@ -607,67 +584,55 @@ static void move_right(char *buf, char *end, unsigned len, unsigned spaces)
  * Returns: new buffer position after padding.
  */
 static noinline_for_stack
-char *widen_string(char *buf, int n, char *end, struct printf_spec spec)
+void widen_string(struct printbuf *out, int n,
+		  struct printf_spec spec)
 {
 	unsigned spaces;
 
 	if (likely(n >= spec.field_width))
-		return buf;
+		return;
 	/* we want to pad the sucker */
 	spaces = spec.field_width - n;
-	if (!(spec.flags & LEFT)) {
-		move_right(buf - n, end, n, spaces);
-		return buf + spaces;
-	}
-	while (spaces--) {
-		if (buf < end)
-			*buf = ' ';
-		++buf;
-	}
-	return buf;
+	if (!(spec.flags & LEFT))
+		move_right(out, n, spaces);
+	else
+		pr_chars(out, ' ', spaces);
 }
 
 /* Handle string from a well known address. */
-static char *string_nocheck(char *buf, char *end, const char *s,
-			    struct printf_spec spec)
+static void string_nocheck(struct printbuf *out,
+			   const char *s,
+			   struct printf_spec spec)
 {
-	int len = 0;
-	int lim = spec.precision;
+	int len = min_t(unsigned, strlen(s), spec.precision);
 
-	while (lim--) {
-		char c = *s++;
-		if (!c)
-			break;
-		if (buf < end)
-			*buf = c;
-		++buf;
-		++len;
-	}
-	return widen_string(buf, len, end, spec);
+	pr_bytes(out, s, len);
+	widen_string(out, len, spec);
 }
 
-static char *err_ptr(char *buf, char *end, void *ptr,
-		     struct printf_spec spec)
+static void err_ptr(struct printbuf *out, void *ptr,
+		    struct printf_spec spec)
 {
 	int err = PTR_ERR(ptr);
 	const char *sym = errname(err);
 
-	if (sym)
-		return string_nocheck(buf, end, sym, spec);
-
-	/*
-	 * Somebody passed ERR_PTR(-1234) or some other non-existing
-	 * Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to
-	 * printing it as its decimal representation.
-	 */
-	spec.flags |= SIGN;
-	spec.base = 10;
-	return number(buf, end, err, spec);
+	if (sym) {
+		string_nocheck(out, sym, spec);
+	} else {
+		/*
+		 * Somebody passed ERR_PTR(-1234) or some other non-existing
+		 * Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to
+		 * printing it as its decimal representation.
+		 */
+		spec.flags |= SIGN;
+		spec.base = 10;
+		number(out, err, spec);
+	}
 }
 
 /* Be careful: error messages must fit into the given buffer. */
-static char *error_string(char *buf, char *end, const char *s,
-			  struct printf_spec spec)
+static void error_string(struct printbuf *out, const char *s,
+			 struct printf_spec spec)
 {
 	/*
 	 * Hard limit to avoid a completely insane messages. It actually
@@ -677,7 +642,7 @@ static char *error_string(char *buf, char *end, const char *s,
 	if (spec.precision == -1)
 		spec.precision = 2 * sizeof(void *);
 
-	return string_nocheck(buf, end, s, spec);
+	string_nocheck(out, s, spec);
 }
 
 /*
@@ -696,14 +661,15 @@ static const char *check_pointer_msg(const void *ptr)
 	return NULL;
 }
 
-static int check_pointer(char **buf, char *end, const void *ptr,
+static int check_pointer(struct printbuf *out,
+			 const void *ptr,
 			 struct printf_spec spec)
 {
 	const char *err_msg;
 
 	err_msg = check_pointer_msg(ptr);
 	if (err_msg) {
-		*buf = error_string(*buf, end, err_msg, spec);
+		error_string(out, err_msg, spec);
 		return -EFAULT;
 	}
 
@@ -711,18 +677,19 @@ static int check_pointer(char **buf, char *end, const void *ptr,
 }
 
 static noinline_for_stack
-char *string(char *buf, char *end, const char *s,
-	     struct printf_spec spec)
+void string(struct printbuf *out,
+	    const char *s,
+	    struct printf_spec spec)
 {
-	if (check_pointer(&buf, end, s, spec))
-		return buf;
+	if (check_pointer(out, s, spec))
+		return;
 
-	return string_nocheck(buf, end, s, spec);
+	string_nocheck(out, s, spec);
 }
 
-static char *pointer_string(char *buf, char *end,
-			    const void *ptr,
-			    struct printf_spec spec)
+static void pointer_string(struct printbuf *out,
+			   const void *ptr,
+			   struct printf_spec spec)
 {
 	spec.base = 16;
 	spec.flags |= SMALL;
@@ -731,7 +698,7 @@ static char *pointer_string(char *buf, char *end,
 		spec.flags |= ZEROPAD;
 	}
 
-	return number(buf, end, (unsigned long int)ptr, spec);
+	number(out, (unsigned long int)ptr, spec);
 }
 
 /* Make pointers available for printing early in the boot sequence. */
@@ -818,8 +785,9 @@ int ptr_to_hashval(const void *ptr, unsigned long *hashval_out)
 	return __ptr_to_hashval(ptr, hashval_out);
 }
 
-static char *ptr_to_id(char *buf, char *end, const void *ptr,
-		       struct printf_spec spec)
+static void ptr_to_id(struct printbuf *out,
+		      const void *ptr,
+		      struct printf_spec spec)
 {
 	const char *str = sizeof(ptr) == 8 ? "(____ptrval____)" : "(ptrval)";
 	unsigned long hashval;
@@ -830,34 +798,35 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr,
 	 * as they are not actual addresses.
 	 */
 	if (IS_ERR_OR_NULL(ptr))
-		return pointer_string(buf, end, ptr, spec);
+		return pointer_string(out, ptr, spec);
 
 	/* When debugging early boot use non-cryptographically secure hash. */
 	if (unlikely(debug_boot_weak_hash)) {
 		hashval = hash_long((unsigned long)ptr, 32);
-		return pointer_string(buf, end, (const void *)hashval, spec);
+		return pointer_string(out, (const void *)hashval, spec);
 	}
 
 	ret = __ptr_to_hashval(ptr, &hashval);
 	if (ret) {
 		spec.field_width = 2 * sizeof(ptr);
 		/* string length must be less than default_width */
-		return error_string(buf, end, str, spec);
+		return error_string(out, str, spec);
 	}
 
-	return pointer_string(buf, end, (const void *)hashval, spec);
+	pointer_string(out, (const void *)hashval, spec);
 }
 
 int kptr_restrict __read_mostly;
 
 static noinline_for_stack
-char *restricted_pointer(char *buf, char *end, const void *ptr,
-			 struct printf_spec spec)
+void restricted_pointer(struct printbuf *out,
+			const void *ptr,
+			struct printf_spec spec)
 {
 	switch (kptr_restrict) {
 	case 0:
 		/* Handle as %p, hash and do _not_ leak addresses. */
-		return ptr_to_id(buf, end, ptr, spec);
+		return ptr_to_id(out, ptr, spec);
 	case 1: {
 		const struct cred *cred;
 
@@ -868,7 +837,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
 		if (in_irq() || in_serving_softirq() || in_nmi()) {
 			if (spec.field_width == -1)
 				spec.field_width = 2 * sizeof(ptr);
-			return error_string(buf, end, "pK-error", spec);
+			return error_string(out, "pK-error", spec);
 		}
 
 		/*
@@ -894,12 +863,13 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
 		break;
 	}
 
-	return pointer_string(buf, end, ptr, spec);
+	return pointer_string(out, ptr, spec);
 }
 
 static noinline_for_stack
-char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_spec spec,
-		  const char *fmt)
+void dentry_name(struct printbuf *out,
+		 const struct dentry *d, struct printf_spec spec,
+		 const char *fmt)
 {
 	const char *array[4], *s;
 	const struct dentry *p;
@@ -916,9 +886,9 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
 
 	rcu_read_lock();
 	for (i = 0; i < depth; i++, d = p) {
-		if (check_pointer(&buf, end, d, spec)) {
+		if (check_pointer(out, d, spec)) {
 			rcu_read_unlock();
-			return buf;
+			return;
 		}
 
 		p = READ_ONCE(d->d_parent);
@@ -931,7 +901,7 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
 		}
 	}
 	s = array[--i];
-	for (n = 0; n != spec.precision; n++, buf++) {
+	for (n = 0; n != spec.precision; n++) {
 		char c = *s++;
 		if (!c) {
 			if (!i)
@@ -939,49 +909,46 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
 			c = '/';
 			s = array[--i];
 		}
-		if (buf < end)
-			*buf = c;
+		__pr_char(out, c);
 	}
 	rcu_read_unlock();
-	return widen_string(buf, n, end, spec);
+	widen_string(out, n, spec);
 }
 
 static noinline_for_stack
-char *file_dentry_name(char *buf, char *end, const struct file *f,
-			struct printf_spec spec, const char *fmt)
+void file_dentry_name(struct printbuf *out,
+		      const struct file *f,
+		      struct printf_spec spec, const char *fmt)
 {
-	if (check_pointer(&buf, end, f, spec))
-		return buf;
+	if (check_pointer(out, f, spec))
+		return;
 
-	return dentry_name(buf, end, f->f_path.dentry, spec, fmt);
+	return dentry_name(out, f->f_path.dentry, spec, fmt);
 }
 #ifdef CONFIG_BLOCK
 static noinline_for_stack
-char *bdev_name(char *buf, char *end, struct block_device *bdev,
-		struct printf_spec spec, const char *fmt)
+void bdev_name(struct printbuf *out,
+	       struct block_device *bdev,
+	       struct printf_spec spec, const char *fmt)
 {
 	struct gendisk *hd;
 
-	if (check_pointer(&buf, end, bdev, spec))
-		return buf;
+	if (check_pointer(out, bdev, spec))
+		return;
 
 	hd = bdev->bd_disk;
-	buf = string(buf, end, hd->disk_name, spec);
+	string(out, hd->disk_name, spec);
 	if (bdev->bd_partno) {
-		if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) {
-			if (buf < end)
-				*buf = 'p';
-			buf++;
-		}
-		buf = number(buf, end, bdev->bd_partno, spec);
+		if (isdigit(hd->disk_name[strlen(hd->disk_name)-1]))
+			__pr_char(out, 'p');
+		number(out, bdev->bd_partno, spec);
 	}
-	return buf;
 }
 #endif
 
 static noinline_for_stack
-char *symbol_string(char *buf, char *end, void *ptr,
-		    struct printf_spec spec, const char *fmt)
+void symbol_string(struct printbuf *out, void *ptr,
+		   struct printf_spec spec, const char *fmt)
 {
 	unsigned long value;
 #ifdef CONFIG_KALLSYMS
@@ -1004,9 +971,9 @@ char *symbol_string(char *buf, char *end, void *ptr,
 	else
 		sprint_symbol_no_offset(sym, value);
 
-	return string_nocheck(buf, end, sym, spec);
+	string_nocheck(out, sym, spec);
 #else
-	return special_hex_number(buf, end, value, sizeof(void *));
+	special_hex_number(out, value, sizeof(void *));
 #endif
 }
 
@@ -1041,8 +1008,8 @@ static const struct printf_spec default_dec04_spec = {
 };
 
 static noinline_for_stack
-char *resource_string(char *buf, char *end, struct resource *res,
-		      struct printf_spec spec, const char *fmt)
+void resource_string(struct printbuf *out, struct resource *res,
+		     struct printf_spec spec, const char *fmt)
 {
 #ifndef IO_RSRC_PRINTK_SIZE
 #define IO_RSRC_PRINTK_SIZE	6
@@ -1081,69 +1048,68 @@ char *resource_string(char *buf, char *end, struct resource *res,
 #define FLAG_BUF_SIZE		(2 * sizeof(res->flags))
 #define DECODED_BUF_SIZE	sizeof("[mem - 64bit pref window disabled]")
 #define RAW_BUF_SIZE		sizeof("[mem - flags 0x]")
-	char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
+	char sym_buf[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
 		     2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
-
-	char *p = sym, *pend = sym + sizeof(sym);
+	struct printbuf sym = PRINTBUF_EXTERN(sym_buf, sizeof(sym_buf));
 	int decode = (fmt[0] == 'R') ? 1 : 0;
 	const struct printf_spec *specp;
 
-	if (check_pointer(&buf, end, res, spec))
-		return buf;
+	if (check_pointer(out, res, spec))
+		return;
 
-	*p++ = '[';
+	__pr_char(&sym, '[');
 	if (res->flags & IORESOURCE_IO) {
-		p = string_nocheck(p, pend, "io  ", str_spec);
+		string_nocheck(&sym, "io  ", str_spec);
 		specp = &io_spec;
 	} else if (res->flags & IORESOURCE_MEM) {
-		p = string_nocheck(p, pend, "mem ", str_spec);
+		string_nocheck(&sym, "mem ", str_spec);
 		specp = &mem_spec;
 	} else if (res->flags & IORESOURCE_IRQ) {
-		p = string_nocheck(p, pend, "irq ", str_spec);
+		string_nocheck(&sym, "irq ", str_spec);
 		specp = &default_dec_spec;
 	} else if (res->flags & IORESOURCE_DMA) {
-		p = string_nocheck(p, pend, "dma ", str_spec);
+		string_nocheck(&sym, "dma ", str_spec);
 		specp = &default_dec_spec;
 	} else if (res->flags & IORESOURCE_BUS) {
-		p = string_nocheck(p, pend, "bus ", str_spec);
+		string_nocheck(&sym, "bus ", str_spec);
 		specp = &bus_spec;
 	} else {
-		p = string_nocheck(p, pend, "??? ", str_spec);
+		string_nocheck(&sym, "??? ", str_spec);
 		specp = &mem_spec;
 		decode = 0;
 	}
 	if (decode && res->flags & IORESOURCE_UNSET) {
-		p = string_nocheck(p, pend, "size ", str_spec);
-		p = number(p, pend, resource_size(res), *specp);
+		string_nocheck(&sym, "size ", str_spec);
+		number(&sym, resource_size(res), *specp);
 	} else {
-		p = number(p, pend, res->start, *specp);
+		number(&sym, res->start, *specp);
 		if (res->start != res->end) {
-			*p++ = '-';
-			p = number(p, pend, res->end, *specp);
+			__pr_char(&sym, '-');
+			number(&sym, res->end, *specp);
 		}
 	}
 	if (decode) {
 		if (res->flags & IORESOURCE_MEM_64)
-			p = string_nocheck(p, pend, " 64bit", str_spec);
+			string_nocheck(&sym, " 64bit", str_spec);
 		if (res->flags & IORESOURCE_PREFETCH)
-			p = string_nocheck(p, pend, " pref", str_spec);
+			string_nocheck(&sym, " pref", str_spec);
 		if (res->flags & IORESOURCE_WINDOW)
-			p = string_nocheck(p, pend, " window", str_spec);
+			string_nocheck(&sym, " window", str_spec);
 		if (res->flags & IORESOURCE_DISABLED)
-			p = string_nocheck(p, pend, " disabled", str_spec);
+			string_nocheck(&sym, " disabled", str_spec);
 	} else {
-		p = string_nocheck(p, pend, " flags ", str_spec);
-		p = number(p, pend, res->flags, default_flag_spec);
+		string_nocheck(&sym, " flags ", str_spec);
+		number(&sym, res->flags, default_flag_spec);
 	}
-	*p++ = ']';
-	*p = '\0';
+	__pr_char(&sym, ']');
+	printbuf_nul_terminate(&sym);
 
-	return string_nocheck(buf, end, sym, spec);
+	string_nocheck(out, sym_buf, spec);
 }
 
 static noinline_for_stack
-char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
-		 const char *fmt)
+void hex_string(struct printbuf *out, u8 *addr,
+		struct printf_spec spec, const char *fmt)
 {
 	int i, len = 1;		/* if we pass '%ph[CDN]', field width remains
 				   negative value, fallback to the default */
@@ -1151,10 +1117,10 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
 
 	if (spec.field_width == 0)
 		/* nothing to print */
-		return buf;
+		return;
 
-	if (check_pointer(&buf, end, addr, spec))
-		return buf;
+	if (check_pointer(out, addr, spec))
+		return;
 
 	switch (fmt[1]) {
 	case 'C':
@@ -1175,34 +1141,25 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
 		len = min_t(int, spec.field_width, 64);
 
 	for (i = 0; i < len; ++i) {
-		if (buf < end)
-			*buf = hex_asc_hi(addr[i]);
-		++buf;
-		if (buf < end)
-			*buf = hex_asc_lo(addr[i]);
-		++buf;
-
-		if (separator && i != len - 1) {
-			if (buf < end)
-				*buf = separator;
-			++buf;
-		}
-	}
+		__pr_char(out, hex_asc_hi(addr[i]));
+		__pr_char(out, hex_asc_lo(addr[i]));
 
-	return buf;
+		if (separator && i != len - 1)
+			__pr_char(out, separator);
+	}
 }
 
 static noinline_for_stack
-char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
-		    struct printf_spec spec, const char *fmt)
+void bitmap_string(struct printbuf *out, unsigned long *bitmap,
+		   struct printf_spec spec, const char *fmt)
 {
 	const int CHUNKSZ = 32;
 	int nr_bits = max_t(int, spec.field_width, 0);
 	int i, chunksz;
 	bool first = true;
 
-	if (check_pointer(&buf, end, bitmap, spec))
-		return buf;
+	if (check_pointer(out, bitmap, spec))
+		return;
 
 	/* reused to print numbers */
 	spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 };
@@ -1221,54 +1178,45 @@ char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
 		bit = i % BITS_PER_LONG;
 		val = (bitmap[word] >> bit) & chunkmask;
 
-		if (!first) {
-			if (buf < end)
-				*buf = ',';
-			buf++;
-		}
+		if (!first)
+			__pr_char(out, ',');
 		first = false;
 
 		spec.field_width = DIV_ROUND_UP(chunksz, 4);
-		buf = number(buf, end, val, spec);
+		number(out, val, spec);
 
 		chunksz = CHUNKSZ;
 	}
-	return buf;
 }
 
 static noinline_for_stack
-char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap,
-			 struct printf_spec spec, const char *fmt)
+void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
+			struct printf_spec spec, const char *fmt)
 {
 	int nr_bits = max_t(int, spec.field_width, 0);
 	bool first = true;
 	int rbot, rtop;
 
-	if (check_pointer(&buf, end, bitmap, spec))
-		return buf;
+	if (check_pointer(out, bitmap, spec))
+		return ;
 
 	for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) {
-		if (!first) {
-			if (buf < end)
-				*buf = ',';
-			buf++;
-		}
+		if (!first)
+			__pr_char(out, ',');
 		first = false;
 
-		buf = number(buf, end, rbot, default_dec_spec);
+		number(out, rbot, default_dec_spec);
 		if (rtop == rbot + 1)
 			continue;
 
-		if (buf < end)
-			*buf = '-';
-		buf = number(++buf, end, rtop - 1, default_dec_spec);
+		__pr_char(out, '-');
+		number(out, rtop - 1, default_dec_spec);
 	}
-	return buf;
 }
 
 static noinline_for_stack
-char *mac_address_string(char *buf, char *end, u8 *addr,
-			 struct printf_spec spec, const char *fmt)
+void mac_address_string(struct printbuf *out, u8 *addr,
+			struct printf_spec spec, const char *fmt)
 {
 	char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")];
 	char *p = mac_addr;
@@ -1276,8 +1224,8 @@ char *mac_address_string(char *buf, char *end, u8 *addr,
 	char separator;
 	bool reversed = false;
 
-	if (check_pointer(&buf, end, addr, spec))
-		return buf;
+	if (check_pointer(out, addr, spec))
+		return;
 
 	switch (fmt[1]) {
 	case 'F':
@@ -1304,11 +1252,12 @@ char *mac_address_string(char *buf, char *end, u8 *addr,
 	}
 	*p = '\0';
 
-	return string_nocheck(buf, end, mac_addr, spec);
+	string_nocheck(out, mac_addr, spec);
 }
 
 static noinline_for_stack
-char *ip4_string(char *p, const u8 *addr, const char *fmt)
+void ip4_string(struct printbuf *out,
+		const u8 *addr, const char *fmt)
 {
 	int i;
 	bool leading_zeros = (fmt[0] == 'i');
@@ -1341,24 +1290,23 @@ char *ip4_string(char *p, const u8 *addr, const char *fmt)
 		int digits = put_dec_trunc8(temp, addr[index]) - temp;
 		if (leading_zeros) {
 			if (digits < 3)
-				*p++ = '0';
+				__pr_char(out, '0');
 			if (digits < 2)
-				*p++ = '0';
+				__pr_char(out, '0');
 		}
 		/* reverse the digits in the quad */
 		while (digits--)
-			*p++ = temp[digits];
+			__pr_char(out, temp[digits]);
 		if (i < 3)
-			*p++ = '.';
+			__pr_char(out, '.');
 		index += step;
 	}
-	*p = '\0';
 
-	return p;
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
-char *ip6_compressed_string(char *p, const char *addr)
+void ip6_compressed_string(struct printbuf *out, const char *addr)
 {
 	int i, j, range;
 	unsigned char zerolength[8];
@@ -1402,14 +1350,14 @@ char *ip6_compressed_string(char *p, const char *addr)
 	for (i = 0; i < range; i++) {
 		if (i == colonpos) {
 			if (needcolon || i == 0)
-				*p++ = ':';
-			*p++ = ':';
+				__pr_char(out, ':');
+			__pr_char(out, ':');
 			needcolon = false;
 			i += longest - 1;
 			continue;
 		}
 		if (needcolon) {
-			*p++ = ':';
+			__pr_char(out, ':');
 			needcolon = false;
 		}
 		/* hex u16 without leading 0s */
@@ -1418,81 +1366,81 @@ char *ip6_compressed_string(char *p, const char *addr)
 		lo = word & 0xff;
 		if (hi) {
 			if (hi > 0x0f)
-				p = hex_byte_pack(p, hi);
+				pr_hex_byte(out, hi);
 			else
-				*p++ = hex_asc_lo(hi);
-			p = hex_byte_pack(p, lo);
+				__pr_char(out, hex_asc_lo(hi));
+			pr_hex_byte(out, lo);
 		}
 		else if (lo > 0x0f)
-			p = hex_byte_pack(p, lo);
+			pr_hex_byte(out, lo);
 		else
-			*p++ = hex_asc_lo(lo);
+			__pr_char(out, hex_asc_lo(lo));
 		needcolon = true;
 	}
 
 	if (useIPv4) {
 		if (needcolon)
-			*p++ = ':';
-		p = ip4_string(p, &in6.s6_addr[12], "I4");
+			__pr_char(out, ':');
+		ip4_string(out, &in6.s6_addr[12], "I4");
 	}
-	*p = '\0';
 
-	return p;
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
-char *ip6_string(char *p, const char *addr, const char *fmt)
+void ip6_string(struct printbuf *out, const char *addr, const char *fmt)
 {
 	int i;
 
 	for (i = 0; i < 8; i++) {
-		p = hex_byte_pack(p, *addr++);
-		p = hex_byte_pack(p, *addr++);
+		pr_hex_byte(out, *addr++);
+		pr_hex_byte(out, *addr++);
 		if (fmt[0] == 'I' && i != 7)
-			*p++ = ':';
+			__pr_char(out, ':');
 	}
-	*p = '\0';
 
-	return p;
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
-char *ip6_addr_string(char *buf, char *end, const u8 *addr,
-		      struct printf_spec spec, const char *fmt)
+void ip6_addr_string(struct printbuf *out, const u8 *addr,
+		     struct printf_spec spec, const char *fmt)
 {
-	char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+	char ip6_addr_buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+	struct printbuf ip6_addr = PRINTBUF_EXTERN(ip6_addr_buf, sizeof(ip6_addr_buf));
 
 	if (fmt[0] == 'I' && fmt[2] == 'c')
-		ip6_compressed_string(ip6_addr, addr);
+		ip6_compressed_string(&ip6_addr, addr);
 	else
-		ip6_string(ip6_addr, addr, fmt);
+		ip6_string(&ip6_addr, addr, fmt);
 
-	return string_nocheck(buf, end, ip6_addr, spec);
+	string_nocheck(out, ip6_addr_buf, spec);
 }
 
 static noinline_for_stack
-char *ip4_addr_string(char *buf, char *end, const u8 *addr,
-		      struct printf_spec spec, const char *fmt)
+void ip4_addr_string(struct printbuf *out, const u8 *addr,
+		     struct printf_spec spec, const char *fmt)
 {
-	char ip4_addr[sizeof("255.255.255.255")];
+	char ip4_addr_buf[sizeof("255.255.255.255")];
+	struct printbuf ip4_addr = PRINTBUF_EXTERN(ip4_addr_buf, sizeof(ip4_addr_buf));
 
-	ip4_string(ip4_addr, addr, fmt);
+	ip4_string(&ip4_addr, addr, fmt);
 
-	return string_nocheck(buf, end, ip4_addr, spec);
+	string_nocheck(out, ip4_addr_buf, spec);
 }
 
 static noinline_for_stack
-char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
-			 struct printf_spec spec, const char *fmt)
+void ip6_addr_string_sa(struct printbuf *out,
+			const struct sockaddr_in6 *sa,
+			struct printf_spec spec, const char *fmt)
 {
 	bool have_p = false, have_s = false, have_f = false, have_c = false;
-	char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
-		      sizeof(":12345") + sizeof("/123456789") +
-		      sizeof("%1234567890")];
-	char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr);
+	char ip6_addr_buf[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
+		sizeof(":12345") + sizeof("/123456789") +
+		sizeof("%1234567890")];
+	struct printbuf ip6_addr = PRINTBUF_EXTERN(ip6_addr_buf, sizeof(ip6_addr_buf));
 	const u8 *addr = (const u8 *) &sa->sin6_addr;
 	char fmt6[2] = { fmt[0], '6' };
-	u8 off = 0;
 
 	fmt++;
 	while (isalpha(*++fmt)) {
@@ -1512,44 +1460,43 @@ char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
 		}
 	}
 
-	if (have_p || have_s || have_f) {
-		*p = '[';
-		off = 1;
-	}
+	if (have_p || have_s || have_f)
+		__pr_char(&ip6_addr, '[');
 
 	if (fmt6[0] == 'I' && have_c)
-		p = ip6_compressed_string(ip6_addr + off, addr);
+		ip6_compressed_string(&ip6_addr, addr);
 	else
-		p = ip6_string(ip6_addr + off, addr, fmt6);
+		ip6_string(&ip6_addr, addr, fmt6);
 
 	if (have_p || have_s || have_f)
-		*p++ = ']';
+		__pr_char(&ip6_addr, ']');
 
 	if (have_p) {
-		*p++ = ':';
-		p = number(p, pend, ntohs(sa->sin6_port), spec);
+		__pr_char(&ip6_addr, ':');
+		number(&ip6_addr, ntohs(sa->sin6_port), spec);
 	}
 	if (have_f) {
-		*p++ = '/';
-		p = number(p, pend, ntohl(sa->sin6_flowinfo &
-					  IPV6_FLOWINFO_MASK), spec);
+		__pr_char(&ip6_addr, '/');
+		number(&ip6_addr, ntohl(sa->sin6_flowinfo &
+					IPV6_FLOWINFO_MASK), spec);
 	}
 	if (have_s) {
-		*p++ = '%';
-		p = number(p, pend, sa->sin6_scope_id, spec);
+		__pr_char(&ip6_addr, '%');
+		number(&ip6_addr, sa->sin6_scope_id, spec);
 	}
-	*p = '\0';
+	printbuf_nul_terminate(&ip6_addr);
 
-	return string_nocheck(buf, end, ip6_addr, spec);
+	string_nocheck(out, ip6_addr_buf, spec);
 }
 
 static noinline_for_stack
-char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
-			 struct printf_spec spec, const char *fmt)
+void ip4_addr_string_sa(struct printbuf *out,
+			const struct sockaddr_in *sa,
+			struct printf_spec spec, const char *fmt)
 {
 	bool have_p = false;
-	char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")];
-	char *pend = ip4_addr + sizeof(ip4_addr);
+	char ip4_addr_buf[sizeof("255.255.255.255") + sizeof(":12345")];
+	struct printbuf ip4_addr = PRINTBUF_EXTERN(ip4_addr_buf, sizeof(ip4_addr_buf));
 	const u8 *addr = (const u8 *) &sa->sin_addr.s_addr;
 	char fmt4[3] = { fmt[0], '4', 0 };
 
@@ -1568,30 +1515,30 @@ char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
 		}
 	}
 
-	p = ip4_string(ip4_addr, addr, fmt4);
+	ip4_string(&ip4_addr, addr, fmt4);
 	if (have_p) {
-		*p++ = ':';
-		p = number(p, pend, ntohs(sa->sin_port), spec);
+		__pr_char(&ip4_addr, ':');
+		number(&ip4_addr, ntohs(sa->sin_port), spec);
 	}
-	*p = '\0';
+	printbuf_nul_terminate(&ip4_addr);
 
-	return string_nocheck(buf, end, ip4_addr, spec);
+	string_nocheck(out, ip4_addr_buf, spec);
 }
 
 static noinline_for_stack
-char *ip_addr_string(char *buf, char *end, const void *ptr,
-		     struct printf_spec spec, const char *fmt)
+void ip_addr_string(struct printbuf *out, const void *ptr,
+		    struct printf_spec spec, const char *fmt)
 {
 	char *err_fmt_msg;
 
-	if (check_pointer(&buf, end, ptr, spec))
-		return buf;
+	if (check_pointer(out, ptr, spec))
+		return;
 
 	switch (fmt[1]) {
 	case '6':
-		return ip6_addr_string(buf, end, ptr, spec, fmt);
+		return ip6_addr_string(out, ptr, spec, fmt);
 	case '4':
-		return ip4_addr_string(buf, end, ptr, spec, fmt);
+		return ip4_addr_string(out, ptr, spec, fmt);
 	case 'S': {
 		const union {
 			struct sockaddr		raw;
@@ -1601,21 +1548,21 @@ char *ip_addr_string(char *buf, char *end, const void *ptr,
 
 		switch (sa->raw.sa_family) {
 		case AF_INET:
-			return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt);
+			return ip4_addr_string_sa(out, &sa->v4, spec, fmt);
 		case AF_INET6:
-			return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt);
+			return ip6_addr_string_sa(out, &sa->v6, spec, fmt);
 		default:
-			return error_string(buf, end, "(einval)", spec);
+			return error_string(out, "(einval)", spec);
 		}}
 	}
 
 	err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)";
-	return error_string(buf, end, err_fmt_msg, spec);
+	return error_string(out, err_fmt_msg, spec);
 }
 
 static noinline_for_stack
-char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
-		     const char *fmt)
+void escaped_string(struct printbuf *out, u8 *addr,
+		    struct printf_spec spec, const char *fmt)
 {
 	bool found = true;
 	int count = 1;
@@ -1623,10 +1570,10 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
 	int len;
 
 	if (spec.field_width == 0)
-		return buf;				/* nothing to print */
+		return;				/* nothing to print */
 
-	if (check_pointer(&buf, end, addr, spec))
-		return buf;
+	if (check_pointer(out, addr, spec))
+		return;
 
 	do {
 		switch (fmt[count++]) {
@@ -1661,44 +1608,35 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
 		flags = ESCAPE_ANY_NP;
 
 	len = spec.field_width < 0 ? 1 : spec.field_width;
-
-	/*
-	 * string_escape_mem() writes as many characters as it can to
-	 * the given buffer, and returns the total size of the output
-	 * had the buffer been big enough.
-	 */
-	buf += string_escape_mem(addr, len, buf, buf < end ? end - buf : 0, flags, NULL);
-
-	return buf;
+	pr_escaped_string(out, addr, len, flags, NULL);
 }
 
-static char *va_format(char *buf, char *end, struct va_format *va_fmt,
-		       struct printf_spec spec, const char *fmt)
+static void va_format(struct printbuf *out,
+		      struct va_format *va_fmt,
+		      struct printf_spec spec, const char *fmt)
 {
 	va_list va;
 
-	if (check_pointer(&buf, end, va_fmt, spec))
-		return buf;
+	if (check_pointer(out, va_fmt, spec))
+		return;
 
 	va_copy(va, *va_fmt->va);
-	buf += vsnprintf(buf, end > buf ? end - buf : 0, va_fmt->fmt, va);
+	vpr_buf(out, va_fmt->fmt, va);
 	va_end(va);
-
-	return buf;
 }
 
 static noinline_for_stack
-char *uuid_string(char *buf, char *end, const u8 *addr,
-		  struct printf_spec spec, const char *fmt)
+void uuid_string(struct printbuf *out, const u8 *addr,
+		 struct printf_spec spec, const char *fmt)
 {
-	char uuid[UUID_STRING_LEN + 1];
-	char *p = uuid;
+	char uuid_buf[UUID_STRING_LEN + 1];
+	struct printbuf uuid = PRINTBUF_EXTERN(uuid_buf, sizeof(uuid_buf));
 	int i;
 	const u8 *index = uuid_index;
 	bool uc = false;
 
-	if (check_pointer(&buf, end, addr, spec))
-		return buf;
+	if (check_pointer(out, addr, spec))
+		return;
 
 	switch (*(++fmt)) {
 	case 'L':
@@ -1714,60 +1652,60 @@ char *uuid_string(char *buf, char *end, const u8 *addr,
 
 	for (i = 0; i < 16; i++) {
 		if (uc)
-			p = hex_byte_pack_upper(p, addr[index[i]]);
+			pr_hex_byte_upper(&uuid, addr[index[i]]);
 		else
-			p = hex_byte_pack(p, addr[index[i]]);
+			pr_hex_byte(&uuid, addr[index[i]]);
 		switch (i) {
 		case 3:
 		case 5:
 		case 7:
 		case 9:
-			*p++ = '-';
+			__pr_char(&uuid, '-');
 			break;
 		}
 	}
 
-	*p = 0;
+	printbuf_nul_terminate(&uuid);
 
-	return string_nocheck(buf, end, uuid, spec);
+	string_nocheck(out, uuid_buf, spec);
 }
 
 static noinline_for_stack
-char *netdev_bits(char *buf, char *end, const void *addr,
-		  struct printf_spec spec,  const char *fmt)
+void netdev_bits(struct printbuf *out, const void *addr,
+		 struct printf_spec spec,  const char *fmt)
 {
 	unsigned long long num;
 	int size;
 
-	if (check_pointer(&buf, end, addr, spec))
-		return buf;
+	if (check_pointer(out, addr, spec))
+		return;
 
 	switch (fmt[1]) {
 	case 'F':
 		num = *(const netdev_features_t *)addr;
 		size = sizeof(netdev_features_t);
+		special_hex_number(out, num, size);
 		break;
 	default:
-		return error_string(buf, end, "(%pN?)", spec);
+		error_string(out, "(%pN?)", spec);
+		break;
 	}
-
-	return special_hex_number(buf, end, num, size);
 }
 
 static noinline_for_stack
-char *fourcc_string(char *buf, char *end, const u32 *fourcc,
-		    struct printf_spec spec, const char *fmt)
+void fourcc_string(struct printbuf *out, const u32 *fourcc,
+		   struct printf_spec spec, const char *fmt)
 {
-	char output[sizeof("0123 little-endian (0x01234567)")];
-	char *p = output;
+	char output_buf[sizeof("0123 little-endian (0x01234567)")];
+	struct printbuf output = PRINTBUF_EXTERN(output_buf, sizeof(output_buf));
 	unsigned int i;
 	u32 val;
 
 	if (fmt[1] != 'c' || fmt[2] != 'c')
-		return error_string(buf, end, "(%p4?)", spec);
+		return error_string(out, "(%p4?)", spec);
 
-	if (check_pointer(&buf, end, fourcc, spec))
-		return buf;
+	if (check_pointer(out, fourcc, spec))
+		return;
 
 	val = *fourcc & ~BIT(31);
 
@@ -1775,30 +1713,29 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc,
 		unsigned char c = val >> (i * 8);
 
 		/* Print non-control ASCII characters as-is, dot otherwise */
-		*p++ = isascii(c) && isprint(c) ? c : '.';
+		__pr_char(&output, isascii(c) && isprint(c) ? c : '.');
 	}
 
-	strcpy(p, *fourcc & BIT(31) ? " big-endian" : " little-endian");
-	p += strlen(p);
+	pr_str(&output, *fourcc & BIT(31) ? " big-endian" : " little-endian");
 
-	*p++ = ' ';
-	*p++ = '(';
-	p = special_hex_number(p, output + sizeof(output) - 2, *fourcc, sizeof(u32));
-	*p++ = ')';
-	*p = '\0';
+	__pr_char(&output, ' ');
+	__pr_char(&output, '(');
+	special_hex_number(&output, *fourcc, sizeof(u32));
+	__pr_char(&output, ')');
+	printbuf_nul_terminate(&output);
 
-	return string(buf, end, output, spec);
+	string(out, output_buf, spec);
 }
 
 static noinline_for_stack
-char *address_val(char *buf, char *end, const void *addr,
-		  struct printf_spec spec, const char *fmt)
+void address_val(struct printbuf *out, const void *addr,
+		 struct printf_spec spec, const char *fmt)
 {
 	unsigned long long num;
 	int size;
 
-	if (check_pointer(&buf, end, addr, spec))
-		return buf;
+	if (check_pointer(out, addr, spec))
+		return;
 
 	switch (fmt[1]) {
 	case 'd':
@@ -1812,55 +1749,44 @@ char *address_val(char *buf, char *end, const void *addr,
 		break;
 	}
 
-	return special_hex_number(buf, end, num, size);
+	special_hex_number(out, num, size);
 }
 
 static noinline_for_stack
-char *date_str(char *buf, char *end, const struct rtc_time *tm, bool r)
+void date_str(struct printbuf *out,
+	      const struct rtc_time *tm, bool r)
 {
 	int year = tm->tm_year + (r ? 0 : 1900);
 	int mon = tm->tm_mon + (r ? 0 : 1);
 
-	buf = number(buf, end, year, default_dec04_spec);
-	if (buf < end)
-		*buf = '-';
-	buf++;
-
-	buf = number(buf, end, mon, default_dec02_spec);
-	if (buf < end)
-		*buf = '-';
-	buf++;
-
-	return number(buf, end, tm->tm_mday, default_dec02_spec);
+	number(out, year, default_dec04_spec);
+	__pr_char(out, '-');
+	number(out, mon, default_dec02_spec);
+	__pr_char(out, '-');
+	number(out, tm->tm_mday, default_dec02_spec);
 }
 
 static noinline_for_stack
-char *time_str(char *buf, char *end, const struct rtc_time *tm, bool r)
+void time_str(struct printbuf *out, const struct rtc_time *tm, bool r)
 {
-	buf = number(buf, end, tm->tm_hour, default_dec02_spec);
-	if (buf < end)
-		*buf = ':';
-	buf++;
-
-	buf = number(buf, end, tm->tm_min, default_dec02_spec);
-	if (buf < end)
-		*buf = ':';
-	buf++;
-
-	return number(buf, end, tm->tm_sec, default_dec02_spec);
+	number(out, tm->tm_hour, default_dec02_spec);
+	__pr_char(out, ':');
+	number(out, tm->tm_min, default_dec02_spec);
+	__pr_char(out, ':');
+	number(out, tm->tm_sec, default_dec02_spec);
 }
 
 static noinline_for_stack
-char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
-	      struct printf_spec spec, const char *fmt)
+void rtc_str(struct printbuf *out, const struct rtc_time *tm,
+	     struct printf_spec spec, const char *fmt)
 {
 	bool have_t = true, have_d = true;
 	bool raw = false, iso8601_separator = true;
 	bool found = true;
 	int count = 2;
 
-	if (check_pointer(&buf, end, tm, spec))
-		return buf;
+	if (check_pointer(out, tm, spec))
+		return;
 
 	switch (fmt[count]) {
 	case 'd':
@@ -1888,21 +1814,16 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
 	} while (found);
 
 	if (have_d)
-		buf = date_str(buf, end, tm, raw);
-	if (have_d && have_t) {
-		if (buf < end)
-			*buf = iso8601_separator ? 'T' : ' ';
-		buf++;
-	}
+		date_str(out, tm, raw);
+	if (have_d && have_t)
+		__pr_char(out, iso8601_separator ? 'T' : ' ');
 	if (have_t)
-		buf = time_str(buf, end, tm, raw);
-
-	return buf;
+		time_str(out, tm, raw);
 }
 
 static noinline_for_stack
-char *time64_str(char *buf, char *end, const time64_t time,
-		 struct printf_spec spec, const char *fmt)
+void time64_str(struct printbuf *out, const time64_t time,
+		struct printf_spec spec, const char *fmt)
 {
 	struct rtc_time rtc_time;
 	struct tm tm;
@@ -1920,47 +1841,48 @@ char *time64_str(char *buf, char *end, const time64_t time,
 
 	rtc_time.tm_isdst = 0;
 
-	return rtc_str(buf, end, &rtc_time, spec, fmt);
+	rtc_str(out, &rtc_time, spec, fmt);
 }
 
 static noinline_for_stack
-char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
-		    const char *fmt)
+void time_and_date(struct printbuf *out,
+		   void *ptr, struct printf_spec spec,
+		   const char *fmt)
 {
 	switch (fmt[1]) {
 	case 'R':
-		return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt);
+		return rtc_str(out, (const struct rtc_time *)ptr, spec, fmt);
 	case 'T':
-		return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt);
+		return time64_str(out, *(const time64_t *)ptr, spec, fmt);
 	default:
-		return error_string(buf, end, "(%pt?)", spec);
+		return error_string(out, "(%pt?)", spec);
 	}
 }
 
 static noinline_for_stack
-char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
-	    const char *fmt)
+void clock(struct printbuf *out, struct clk *clk,
+	   struct printf_spec spec, const char *fmt)
 {
 	if (!IS_ENABLED(CONFIG_HAVE_CLK))
-		return error_string(buf, end, "(%pC?)", spec);
+		return error_string(out, "(%pC?)", spec);
 
-	if (check_pointer(&buf, end, clk, spec))
-		return buf;
+	if (check_pointer(out, clk, spec))
+		return;
 
 	switch (fmt[1]) {
 	case 'n':
 	default:
 #ifdef CONFIG_COMMON_CLK
-		return string(buf, end, __clk_get_name(clk), spec);
+		return string(out, __clk_get_name(clk), spec);
 #else
-		return ptr_to_id(buf, end, clk, spec);
+		return ptr_to_id(out, clk, spec);
 #endif
 	}
 }
 
 static
-char *format_flags(char *buf, char *end, unsigned long flags,
-					const struct trace_print_flags *names)
+void format_flags(struct printbuf *out, unsigned long flags,
+		  const struct trace_print_flags *names)
 {
 	unsigned long mask;
 
@@ -1969,20 +1891,15 @@ char *format_flags(char *buf, char *end, unsigned long flags,
 		if ((flags & mask) != mask)
 			continue;
 
-		buf = string(buf, end, names->name, default_str_spec);
+		string(out, names->name, default_str_spec);
 
 		flags &= ~mask;
-		if (flags) {
-			if (buf < end)
-				*buf = '|';
-			buf++;
-		}
+		if (flags)
+			__pr_char(out, '|');
 	}
 
 	if (flags)
-		buf = number(buf, end, flags, default_flag_spec);
-
-	return buf;
+		number(out, flags, default_flag_spec);
 }
 
 struct page_flags_fields {
@@ -2007,20 +1924,18 @@ static const struct page_flags_fields pff[] = {
 };
 
 static
-char *format_page_flags(char *buf, char *end, unsigned long flags)
+void format_page_flags(struct printbuf *out, unsigned long flags)
 {
 	unsigned long main_flags = flags & PAGEFLAGS_MASK;
 	bool append = false;
 	int i;
 
-	buf = number(buf, end, flags, default_flag_spec);
-	if (buf < end)
-		*buf = '(';
-	buf++;
+	number(out, flags, default_flag_spec);
+	__pr_char(out, '(');
 
 	/* Page flags from the main area. */
 	if (main_flags) {
-		buf = format_flags(buf, end, main_flags, pageflag_names);
+		format_flags(out, main_flags, pageflag_names);
 		append = true;
 	}
 
@@ -2031,41 +1946,31 @@ char *format_page_flags(char *buf, char *end, unsigned long flags)
 			continue;
 
 		/* Format: Flag Name + '=' (equals sign) + Number + '|' (separator) */
-		if (append) {
-			if (buf < end)
-				*buf = '|';
-			buf++;
-		}
+		if (append)
+			__pr_char(out, '|');
 
-		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);
+		string(out, pff[i].name, default_str_spec);
+		__pr_char(out, '=');
+		number(out, (flags >> pff[i].shift) & pff[i].mask, *pff[i].spec);
 
 		append = true;
 	}
-	if (buf < end)
-		*buf = ')';
-	buf++;
-
-	return buf;
+	__pr_char(out, ')');
 }
 
 static noinline_for_stack
-char *flags_string(char *buf, char *end, void *flags_ptr,
-		   struct printf_spec spec, const char *fmt)
+void flags_string(struct printbuf *out, void *flags_ptr,
+		  struct printf_spec spec, const char *fmt)
 {
 	unsigned long flags;
 	const struct trace_print_flags *names;
 
-	if (check_pointer(&buf, end, flags_ptr, spec))
-		return buf;
+	if (check_pointer(out, flags_ptr, spec))
+		return;
 
 	switch (fmt[1]) {
 	case 'p':
-		return format_page_flags(buf, end, *(unsigned long *)flags_ptr);
+		return format_page_flags(out, *(unsigned long *)flags_ptr);
 	case 'v':
 		flags = *(unsigned long *)flags_ptr;
 		names = vmaflag_names;
@@ -2075,15 +1980,15 @@ char *flags_string(char *buf, char *end, void *flags_ptr,
 		names = gfpflag_names;
 		break;
 	default:
-		return error_string(buf, end, "(%pG?)", spec);
+		return error_string(out, "(%pG?)", spec);
 	}
 
-	return format_flags(buf, end, flags, names);
+	return format_flags(out, flags, names);
 }
 
 static noinline_for_stack
-char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf,
-			      char *end)
+void fwnode_full_name_string(struct printbuf *out,
+			     struct fwnode_handle *fwnode)
 {
 	int depth;
 
@@ -2092,25 +1997,23 @@ char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf,
 		struct fwnode_handle *__fwnode =
 			fwnode_get_nth_parent(fwnode, depth);
 
-		buf = string(buf, end, fwnode_get_name_prefix(__fwnode),
-			     default_str_spec);
-		buf = string(buf, end, fwnode_get_name(__fwnode),
-			     default_str_spec);
+		string(out, fwnode_get_name_prefix(__fwnode),
+		       default_str_spec);
+		string(out, fwnode_get_name(__fwnode),
+		       default_str_spec);
 
 		fwnode_handle_put(__fwnode);
 	}
-
-	return buf;
 }
 
 static noinline_for_stack
-char *device_node_string(char *buf, char *end, struct device_node *dn,
-			 struct printf_spec spec, const char *fmt)
+void device_node_string(struct printbuf *out, struct device_node *dn,
+			struct printf_spec spec, const char *fmt)
 {
 	char tbuf[sizeof("xxxx") + 1];
 	const char *p;
 	int ret;
-	char *buf_start = buf;
+	unsigned start = out->pos;
 	struct property *prop;
 	bool has_mult, pass;
 
@@ -2118,13 +2021,13 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
 	str_spec.field_width = -1;
 
 	if (fmt[0] != 'F')
-		return error_string(buf, end, "(%pO?)", spec);
+		return error_string(out, "(%pO?)", spec);
 
 	if (!IS_ENABLED(CONFIG_OF))
-		return error_string(buf, end, "(%pOF?)", spec);
+		return error_string(out, "(%pOF?)", spec);
 
-	if (check_pointer(&buf, end, dn, spec))
-		return buf;
+	if (check_pointer(out, dn, spec))
+		return;
 
 	/* simple case without anything any more format specifiers */
 	fmt++;
@@ -2133,32 +2036,28 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
 
 	for (pass = false; strspn(fmt,"fnpPFcC"); fmt++, pass = true) {
 		int precision;
-		if (pass) {
-			if (buf < end)
-				*buf = ':';
-			buf++;
-		}
+		if (pass)
+			__pr_char(out, ':');
 
 		switch (*fmt) {
 		case 'f':	/* full_name */
-			buf = fwnode_full_name_string(of_fwnode_handle(dn), buf,
-						      end);
+			fwnode_full_name_string(out, of_fwnode_handle(dn));
 			break;
 		case 'n':	/* name */
 			p = fwnode_get_name(of_fwnode_handle(dn));
 			precision = str_spec.precision;
 			str_spec.precision = strchrnul(p, '@') - p;
-			buf = string(buf, end, p, str_spec);
+			string(out, p, str_spec);
 			str_spec.precision = precision;
 			break;
 		case 'p':	/* phandle */
-			buf = number(buf, end, (unsigned int)dn->phandle, default_dec_spec);
+			number(out, (unsigned int)dn->phandle, default_dec_spec);
 			break;
 		case 'P':	/* path-spec */
 			p = fwnode_get_name(of_fwnode_handle(dn));
 			if (!p[1])
 				p = "/";
-			buf = string(buf, end, p, str_spec);
+			string(out, p, str_spec);
 			break;
 		case 'F':	/* flags */
 			tbuf[0] = of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-';
@@ -2166,21 +2065,21 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
 			tbuf[2] = of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-';
 			tbuf[3] = of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-';
 			tbuf[4] = 0;
-			buf = string_nocheck(buf, end, tbuf, str_spec);
+			string_nocheck(out, tbuf, str_spec);
 			break;
 		case 'c':	/* major compatible string */
 			ret = of_property_read_string(dn, "compatible", &p);
 			if (!ret)
-				buf = string(buf, end, p, str_spec);
+				string(out, p, str_spec);
 			break;
 		case 'C':	/* full compatible string */
 			has_mult = false;
 			of_property_for_each_string(dn, "compatible", prop, p) {
 				if (has_mult)
-					buf = string_nocheck(buf, end, ",", str_spec);
-				buf = string_nocheck(buf, end, "\"", str_spec);
-				buf = string(buf, end, p, str_spec);
-				buf = string_nocheck(buf, end, "\"", str_spec);
+					string_nocheck(out, ",", str_spec);
+				string_nocheck(out, "\"", str_spec);
+				string(out, p, str_spec);
+				string_nocheck(out, "\"", str_spec);
 
 				has_mult = true;
 			}
@@ -2190,37 +2089,38 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
 		}
 	}
 
-	return widen_string(buf, buf - buf_start, end, spec);
+	widen_string(out, out->pos - start, spec);
 }
 
 static noinline_for_stack
-char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
-		    struct printf_spec spec, const char *fmt)
+void fwnode_string(struct printbuf *out,
+		   struct fwnode_handle *fwnode,
+		   struct printf_spec spec, const char *fmt)
 {
 	struct printf_spec str_spec = spec;
-	char *buf_start = buf;
+	unsigned start = out->pos;
 
 	str_spec.field_width = -1;
 
 	if (*fmt != 'w')
-		return error_string(buf, end, "(%pf?)", spec);
+		return error_string(out, "(%pf?)", spec);
 
-	if (check_pointer(&buf, end, fwnode, spec))
-		return buf;
+	if (check_pointer(out, fwnode, spec))
+		return;
 
 	fmt++;
 
 	switch (*fmt) {
 	case 'P':	/* name */
-		buf = string(buf, end, fwnode_get_name(fwnode), str_spec);
+		string(out, fwnode_get_name(fwnode), str_spec);
 		break;
 	case 'f':	/* full_name */
 	default:
-		buf = fwnode_full_name_string(fwnode, buf, end);
+		fwnode_full_name_string(out, fwnode);
 		break;
 	}
 
-	return widen_string(buf, buf - buf_start, end, spec);
+	widen_string(out, out->pos - start, spec);
 }
 
 /* Disable pointer hashing if requested */
@@ -2380,8 +2280,8 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
  * rendering it useful as a unique identifier.
  */
 static noinline_for_stack
-char *pointer(const char *fmt, char *buf, char *end, void *ptr,
-	      struct printf_spec spec)
+void pointer(struct printbuf *out, const char *fmt,
+	     void *ptr, struct printf_spec spec)
 {
 	switch (*fmt) {
 	case 'S':
@@ -2389,24 +2289,24 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 		ptr = dereference_symbol_descriptor(ptr);
 		fallthrough;
 	case 'B':
-		return symbol_string(buf, end, ptr, spec, fmt);
+		return symbol_string(out, ptr, spec, fmt);
 	case 'R':
 	case 'r':
-		return resource_string(buf, end, ptr, spec, fmt);
+		return resource_string(out, ptr, spec, fmt);
 	case 'h':
-		return hex_string(buf, end, ptr, spec, fmt);
+		return hex_string(out, ptr, spec, fmt);
 	case 'b':
 		switch (fmt[1]) {
 		case 'l':
-			return bitmap_list_string(buf, end, ptr, spec, fmt);
+			return bitmap_list_string(out, ptr, spec, fmt);
 		default:
-			return bitmap_string(buf, end, ptr, spec, fmt);
+			return bitmap_string(out, ptr, spec, fmt);
 		}
 	case 'M':			/* Colon separated: 00:01:02:03:04:05 */
 	case 'm':			/* Contiguous: 000102030405 */
 					/* [mM]F (FDDI) */
 					/* [mM]R (Reverse order; Bluetooth) */
-		return mac_address_string(buf, end, ptr, spec, fmt);
+		return mac_address_string(out, ptr, spec, fmt);
 	case 'I':			/* Formatted IP supported
 					 * 4:	1.2.3.4
 					 * 6:	0001:0203:...:0708
@@ -2416,54 +2316,54 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 					 * 4:	001.002.003.004
 					 * 6:   000102...0f
 					 */
-		return ip_addr_string(buf, end, ptr, spec, fmt);
+		return ip_addr_string(out, ptr, spec, fmt);
 	case 'E':
-		return escaped_string(buf, end, ptr, spec, fmt);
+		return escaped_string(out, ptr, spec, fmt);
 	case 'U':
-		return uuid_string(buf, end, ptr, spec, fmt);
+		return uuid_string(out, ptr, spec, fmt);
 	case 'V':
-		return va_format(buf, end, ptr, spec, fmt);
+		return va_format(out, ptr, spec, fmt);
 	case 'K':
-		return restricted_pointer(buf, end, ptr, spec);
+		return restricted_pointer(out, ptr, spec);
 	case 'N':
-		return netdev_bits(buf, end, ptr, spec, fmt);
+		return netdev_bits(out, ptr, spec, fmt);
 	case '4':
-		return fourcc_string(buf, end, ptr, spec, fmt);
+		return fourcc_string(out, ptr, spec, fmt);
 	case 'a':
-		return address_val(buf, end, ptr, spec, fmt);
+		return address_val(out, ptr, spec, fmt);
 	case 'd':
-		return dentry_name(buf, end, ptr, spec, fmt);
+		return dentry_name(out, ptr, spec, fmt);
 	case 't':
-		return time_and_date(buf, end, ptr, spec, fmt);
+		return time_and_date(out, ptr, spec, fmt);
 	case 'C':
-		return clock(buf, end, ptr, spec, fmt);
+		return clock(out, ptr, spec, fmt);
 	case 'D':
-		return file_dentry_name(buf, end, ptr, spec, fmt);
+		return file_dentry_name(out, ptr, spec, fmt);
 #ifdef CONFIG_BLOCK
 	case 'g':
-		return bdev_name(buf, end, ptr, spec, fmt);
+		return bdev_name(out, ptr, spec, fmt);
 #endif
 
 	case 'G':
-		return flags_string(buf, end, ptr, spec, fmt);
+		return flags_string(out, ptr, spec, fmt);
 	case 'O':
-		return device_node_string(buf, end, ptr, spec, fmt + 1);
+		return device_node_string(out, ptr, spec, fmt + 1);
 	case 'f':
-		return fwnode_string(buf, end, ptr, spec, fmt + 1);
+		return fwnode_string(out, ptr, spec, fmt + 1);
 	case 'x':
-		return pointer_string(buf, end, ptr, spec);
+		return pointer_string(out, ptr, spec);
 	case 'e':
 		/* %pe with a non-ERR_PTR gets treated as plain %p */
 		if (!IS_ERR(ptr))
 			break;
-		return err_ptr(buf, end, ptr, spec);
+		return err_ptr(out, ptr, spec);
 	case 'u':
 	case 'k':
 		switch (fmt[1]) {
 		case 's':
-			return string(buf, end, ptr, spec);
+			return string(out, ptr, spec);
 		default:
-			return error_string(buf, end, "(einval)", spec);
+			return error_string(out, "(einval)", spec);
 		}
 	}
 
@@ -2472,9 +2372,9 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 	 * unless no_hash_pointers is specified on the command line.
 	 */
 	if (unlikely(no_hash_pointers))
-		return pointer_string(buf, end, ptr, spec);
+		return pointer_string(out, ptr, spec);
 	else
-		return ptr_to_id(buf, end, ptr, spec);
+		return ptr_to_id(out, ptr, spec);
 }
 
 /*
@@ -2695,52 +2595,27 @@ set_precision(struct printf_spec *spec, int prec)
 }
 
 /**
- * vsnprintf - Format a string and place it in a buffer
- * @buf: The buffer to place the result into
- * @size: The size of the buffer, including the trailing null space
+ * vpr_buf - Format a string, outputting to a printbuf
+ * @out: The printbuf to output to
  * @fmt: The format string to use
  * @args: Arguments for the format string
  *
- * This function generally follows C99 vsnprintf, but has some
- * extensions and a few limitations:
- *
- *  - ``%n`` is unsupported
- *  - ``%p*`` is handled by pointer()
- *
- * See pointer() or Documentation/core-api/printk-formats.rst for more
- * extensive description.
+ * vpr_buf works much like the traditional vsnprintf(), but outputs to a
+ * printbuf instead of raw pointer/size.
  *
- * **Please update the documentation in both places when making changes**
+ * If you're not already dealing with a va_list consider using pr_buf().
  *
- * The return value is the number of characters which would
- * be generated for the given input, excluding the trailing
- * '\0', as per ISO C99. If you want to have the exact
- * number of characters written into @buf as return value
- * (not including the trailing '\0'), use vscnprintf(). If the
- * return is greater than or equal to @size, the resulting
- * string is truncated.
- *
- * If you're not already dealing with a va_list consider using snprintf().
+ * See the vsnprintf() documentation for format string extensions over C99.
  */
-int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+void vpr_buf(struct printbuf *out, const char *fmt, va_list args)
 {
 	unsigned long long num;
-	char *str, *end;
 	struct printf_spec spec = {0};
 
 	/* Reject out-of-range values early.  Large positive sizes are
 	   used for unknown buffer sizes. */
-	if (WARN_ON_ONCE(size > INT_MAX))
-		return 0;
-
-	str = buf;
-	end = buf + size;
-
-	/* Make sure end is always >= buf */
-	if (end < buf) {
-		end = ((void *)-1);
-		size = end - buf;
-	}
+	if (WARN_ON_ONCE(out->size > INT_MAX))
+		return;
 
 	while (*fmt) {
 		const char *old_fmt = fmt;
@@ -2749,16 +2624,9 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 		fmt += read;
 
 		switch (spec.type) {
-		case FORMAT_TYPE_NONE: {
-			int copy = read;
-			if (str < end) {
-				if (copy > end - str)
-					copy = end - str;
-				memcpy(str, old_fmt, copy);
-			}
-			str += read;
+		case FORMAT_TYPE_NONE:
+			pr_bytes(out, old_fmt, read);
 			break;
-		}
 
 		case FORMAT_TYPE_WIDTH:
 			set_field_width(&spec, va_arg(args, int));
@@ -2768,44 +2636,29 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 			set_precision(&spec, va_arg(args, int));
 			break;
 
-		case FORMAT_TYPE_CHAR: {
-			char c;
+		case FORMAT_TYPE_CHAR:
+			if (spec.field_width > 0 && !(spec.flags & LEFT))
+				pr_chars(out, spec.field_width, ' ');
 
-			if (!(spec.flags & LEFT)) {
-				while (--spec.field_width > 0) {
-					if (str < end)
-						*str = ' ';
-					++str;
+			__pr_char(out, (unsigned char) va_arg(args, int));
 
-				}
-			}
-			c = (unsigned char) va_arg(args, int);
-			if (str < end)
-				*str = c;
-			++str;
-			while (--spec.field_width > 0) {
-				if (str < end)
-					*str = ' ';
-				++str;
-			}
+			if (spec.field_width > 0 && (spec.flags & LEFT))
+				pr_chars(out, spec.field_width, ' ');
+			spec.field_width = 0;
 			break;
-		}
 
 		case FORMAT_TYPE_STR:
-			str = string(str, end, va_arg(args, char *), spec);
+			string(out, va_arg(args, char *), spec);
 			break;
 
 		case FORMAT_TYPE_PTR:
-			str = pointer(fmt, str, end, va_arg(args, void *),
-				      spec);
+			pointer(out, fmt, va_arg(args, void *), spec);
 			while (isalnum(*fmt))
 				fmt++;
 			break;
 
 		case FORMAT_TYPE_PERCENT_CHAR:
-			if (str < end)
-				*str = '%';
-			++str;
+			__pr_char(out, '%');
 			break;
 
 		case FORMAT_TYPE_INVALID:
@@ -2858,21 +2711,71 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				num = va_arg(args, unsigned int);
 			}
 
-			str = number(str, end, num, spec);
+			number(out, num, spec);
 		}
 	}
-
 out:
-	if (size > 0) {
-		if (str < end)
-			*str = '\0';
-		else
-			end[-1] = '\0';
-	}
+	printbuf_nul_terminate(out);
+}
+EXPORT_SYMBOL(vpr_buf);
 
-	/* the trailing null byte doesn't count towards the total */
-	return str-buf;
+/**
+ * pr_buf - Format a string, outputting to a printbuf
+ * @out: The printbuf to output to
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ *
+ * pr_buf works much like the traditional sprintf(), but outputs to a
+ * printbuf instead of raw pointer/size.
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
+ */
+void pr_buf(struct printbuf *out, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vpr_buf(out, fmt, args);
+	va_end(args);
+}
+EXPORT_SYMBOL(pr_buf);
+
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * This function generally follows C99 vsnprintf, but has some
+ * extensions and a few limitations:
+ *
+ *  - ``%n`` is unsupported
+ *  - ``%p*`` is handled by pointer()
+ *
+ * See pointer() or Documentation/core-api/printk-formats.rst for more
+ * extensive description.
+ *
+ * **Please update the documentation in both places when making changes**
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf(). If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * If you're not already dealing with a va_list consider using snprintf().
+ */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+	struct printbuf out = PRINTBUF_EXTERN(buf, size);
 
+	vpr_buf(&out, fmt, args);
+
+	return out.pos;
 }
 EXPORT_SYMBOL(vsnprintf);
 
@@ -3008,6 +2911,13 @@ EXPORT_SYMBOL(sprintf);
  * bstr_printf() - Binary data to text string
  */
 
+static inline void printbuf_align(struct printbuf *out, unsigned align)
+{
+	/* Assumes output buffer is correctly aligned: */
+	out->pos += align - 1;
+	out->pos &= ~(align - 1);
+}
+
 /**
  * vbin_printf - Parse a format string and place args' binary value in a buffer
  * @bin_buf: The buffer to place args' binary value
@@ -3027,34 +2937,25 @@ EXPORT_SYMBOL(sprintf);
  */
 int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
 {
+	struct printbuf out = PRINTBUF_EXTERN((char *) bin_buf, size);
 	struct printf_spec spec = {0};
-	char *str, *end;
 	int width;
 
-	str = (char *)bin_buf;
-	end = (char *)(bin_buf + size);
-
 #define save_arg(type)							\
 ({									\
 	unsigned long long value;					\
 	if (sizeof(type) == 8) {					\
-		unsigned long long val8;				\
-		str = PTR_ALIGN(str, sizeof(u32));			\
-		val8 = va_arg(args, unsigned long long);		\
-		if (str + sizeof(type) <= end) {			\
-			*(u32 *)str = *(u32 *)&val8;			\
-			*(u32 *)(str + 4) = *((u32 *)&val8 + 1);	\
-		}							\
+		u64 val8 = va_arg(args, u64);				\
+		printbuf_align(&out, sizeof(u32));			\
+		pr_bytes(&out, (u32 *) &val8, 4);			\
+		pr_bytes(&out, ((u32 *) &val8) + 1, 4);			\
 		value = val8;						\
 	} else {							\
-		unsigned int val4;					\
-		str = PTR_ALIGN(str, sizeof(type));			\
-		val4 = va_arg(args, int);				\
-		if (str + sizeof(type) <= end)				\
-			*(typeof(type) *)str = (type)(long)val4;	\
+		u32 val4 = va_arg(args, u32);				\
+		printbuf_align(&out, sizeof(type));			\
+		pr_bytes(&out, &val4, sizeof(type));			\
 		value = (unsigned long long)val4;			\
 	}								\
-	str += sizeof(type);						\
 	value;								\
 })
 
@@ -3085,16 +2986,12 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
 		case FORMAT_TYPE_STR: {
 			const char *save_str = va_arg(args, char *);
 			const char *err_msg;
-			size_t len;
 
 			err_msg = check_pointer_msg(save_str);
 			if (err_msg)
 				save_str = err_msg;
 
-			len = strlen(save_str) + 1;
-			if (str + len < end)
-				memcpy(str, save_str, len);
-			str += len;
+			pr_str(&out, save_str);
 			break;
 		}
 
@@ -3114,12 +3011,7 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
 					save_arg(void *);
 					break;
 				}
-				str = pointer(fmt, str, end, va_arg(args, void *),
-					      spec);
-				if (str + 1 < end)
-					*str++ = '\0';
-				else
-					end[-1] = '\0'; /* Must be nul terminated */
+				pointer(&out, fmt, va_arg(args, void *), spec);
 			}
 			/* skip all alphanumeric pointer suffixes */
 			while (isalnum(*fmt))
@@ -3157,7 +3049,9 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
 	}
 
 out:
-	return (u32 *)(PTR_ALIGN(str, sizeof(u32))) - bin_buf;
+	printbuf_nul_terminate(&out);
+	printbuf_align(&out, 4);
+	return out.pos;
 #undef save_arg
 }
 EXPORT_SYMBOL_GPL(vbin_printf);
@@ -3187,14 +3081,15 @@ EXPORT_SYMBOL_GPL(vbin_printf);
 int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 {
 	struct printf_spec spec = {0};
-	char *str, *end;
+	struct printbuf out = PRINTBUF_EXTERN((char *) bin_buf, size);
 	const char *args = (const char *)bin_buf;
 
 	if (WARN_ON_ONCE(size > INT_MAX))
 		return 0;
 
-	str = buf;
-	end = buf + size;
+	/* Make sure end is always >= buf */
+	if (WARN_ON_ONCE(out.buf + out.size < out.buf))
+		return 0;
 
 #define get_arg(type)							\
 ({									\
@@ -3211,12 +3106,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 	value;								\
 })
 
-	/* Make sure end is always >= buf */
-	if (end < buf) {
-		end = ((void *)-1);
-		size = end - buf;
-	}
-
 	while (*fmt) {
 		const char *old_fmt = fmt;
 		int read = format_decode(fmt, &spec);
@@ -3224,16 +3113,9 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 		fmt += read;
 
 		switch (spec.type) {
-		case FORMAT_TYPE_NONE: {
-			int copy = read;
-			if (str < end) {
-				if (copy > end - str)
-					copy = end - str;
-				memcpy(str, old_fmt, copy);
-			}
-			str += read;
+		case FORMAT_TYPE_NONE:
+			pr_bytes(&out, old_fmt, read);
 			break;
-		}
 
 		case FORMAT_TYPE_WIDTH:
 			set_field_width(&spec, get_arg(int));
@@ -3243,38 +3125,24 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 			set_precision(&spec, get_arg(int));
 			break;
 
-		case FORMAT_TYPE_CHAR: {
-			char c;
-
-			if (!(spec.flags & LEFT)) {
-				while (--spec.field_width > 0) {
-					if (str < end)
-						*str = ' ';
-					++str;
-				}
-			}
-			c = (unsigned char) get_arg(char);
-			if (str < end)
-				*str = c;
-			++str;
-			while (--spec.field_width > 0) {
-				if (str < end)
-					*str = ' ';
-				++str;
-			}
+		case FORMAT_TYPE_CHAR:
+			if (!(spec.flags & LEFT))
+				pr_chars(&out, spec.field_width, ' ');
+			__pr_char(&out, (unsigned char) get_arg(char));
+			if ((spec.flags & LEFT))
+				pr_chars(&out, spec.field_width, ' ');
 			break;
-		}
 
 		case FORMAT_TYPE_STR: {
 			const char *str_arg = args;
 			args += strlen(str_arg) + 1;
-			str = string(str, end, (char *)str_arg, spec);
+			string(&out, (char *)str_arg, spec);
 			break;
 		}
 
 		case FORMAT_TYPE_PTR: {
 			bool process = false;
-			int copy, len;
+			int len;
 			/* Non function dereferences were already done */
 			switch (*fmt) {
 			case 'S':
@@ -3290,17 +3158,12 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 					break;
 				}
 				/* Pointer dereference was already processed */
-				if (str < end) {
-					len = copy = strlen(args);
-					if (copy > end - str)
-						copy = end - str;
-					memcpy(str, args, copy);
-					str += len;
-					args += len + 1;
-				}
+				len = strlen(args);
+				pr_bytes(&out, args, len);
+				args += len + 1;
 			}
 			if (process)
-				str = pointer(fmt, str, end, get_arg(void *), spec);
+				pointer(&out, fmt, get_arg(void *), spec);
 
 			while (isalnum(*fmt))
 				fmt++;
@@ -3308,9 +3171,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 		}
 
 		case FORMAT_TYPE_PERCENT_CHAR:
-			if (str < end)
-				*str = '%';
-			++str;
+			__pr_char(&out, '%');
 			break;
 
 		case FORMAT_TYPE_INVALID:
@@ -3353,23 +3214,15 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 				num = get_arg(int);
 			}
 
-			str = number(str, end, num, spec);
+			number(&out, num, spec);
 		} /* default: */
 		} /* switch(spec.type) */
 	} /* while(*fmt) */
 
 out:
-	if (size > 0) {
-		if (str < end)
-			*str = '\0';
-		else
-			end[-1] = '\0';
-	}
-
 #undef get_arg
-
-	/* the trailing null byte doesn't count towards the total */
-	return str - buf;
+	printbuf_nul_terminate(&out);
+	return out.pos;
 }
 EXPORT_SYMBOL_GPL(bstr_printf);
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 02/28] vsprintf: Convert to printbuf Kent Overstreet
@ 2022-05-19 17:23 ` Kent Overstreet
  2022-05-19 18:33   ` Matthew Wilcox
                     ` (3 more replies)
  2022-05-19 17:23 ` [PATCH v2 04/28] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
                   ` (25 subsequent siblings)
  28 siblings, 4 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This implements two new format strings: both do the same thing, one more
compatible with current gcc format string checking, the other that we'd
like to standardize:

 %p(%p) - more compatible
 %(%p)  - more prettier

Both can take variable numbers of arguments, i.e. %(%p,%p,%p).

They're used to indicate that snprintf or pr_buf should interpret the
next argument as a pretty-printer function to call, and subsequent
arguments within the parentheses should be passed to the pretty-printer.

A pretty printer takes as its first argument a printbuf, and then zero
or more pointer arguments - integer arguments are not (currently) supported.

Example usage:

static void foo_to_text(struct printbuf *out, struct foo *foo)
{
	pr_buf(out, "bar=%u baz=%u", foo->bar, foo->baz);
}

printf("%(%p)", foo_to_text, foo);

The goal is to replace most of our %p format extensions with this
interface, and to move pretty-printers out of the core vsprintf.c code -
this will get us better organization and better discoverability (you'll
be able to cscope to pretty printer calls!), as well as eliminate a lot
of dispatch code in vsprintf.c.

Currently, we can only call pretty printers with pointer arguments. This
could be changed to also allow at least integer arguments in the future
by using libffi.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 Documentation/core-api/printk-formats.rst | 19 ++++++
 lib/test_printf.c                         | 17 +++++
 lib/vsprintf.c                            | 81 ++++++++++++++++++++++-
 3 files changed, 115 insertions(+), 2 deletions(-)

diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index 5e89497ba3..e4ed40960e 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -625,6 +625,25 @@ Examples::
 	%p4cc	Y10  little-endian (0x20303159)
 	%p4cc	NV12 big-endian (0xb231564e)
 
+Calling a pretty printer function
+---------------------------------
+
+::
+
+        %p(%p)     pretty printer function taking one argument
+        %p(%p,%p)  pretty printer function taking two arguments
+
+For calling generic pretty printers. A pretty printer is a function that takes
+as its first argument a pointer to a printbuf, and then zero or more additional
+pointer arguments. For example:
+
+        void foo_to_text(struct printbuf *out, struct foo *foo)
+        {
+                pr_buf(out, "bar=%u baz=%u", foo->bar, foo->baz);
+        }
+
+        printf("%p(%p)", foo_to_text, foo);
+
 Thanks
 ======
 
diff --git a/lib/test_printf.c b/lib/test_printf.c
index 07309c45f3..1e1604ef1a 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -783,6 +783,22 @@ test_pointer(void)
 	fourcc_pointer();
 }
 
+static void printf_test_fn(struct printbuf *out, void *p)
+{
+	int *i = p;
+
+	pr_buf(out, "%i", *i);
+}
+
+static void __init
+test_fn(void)
+{
+	int i = 1;
+
+	test("1", "%pf(%p)", printf_test_fn, &i);
+	test("1", "%(%p)", printf_test_fn, &i);
+}
+
 static void __init selftest(void)
 {
 	alloced_buffer = kmalloc(BUF_SIZE + 2*PAD_SIZE, GFP_KERNEL);
@@ -794,6 +810,7 @@ static void __init selftest(void)
 	test_number();
 	test_string();
 	test_pointer();
+	test_fn();
 
 	kfree(alloced_buffer);
 }
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 09b259e030..7fbeaf50d1 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -431,7 +431,8 @@ enum format_type {
 	FORMAT_TYPE_UINT,
 	FORMAT_TYPE_INT,
 	FORMAT_TYPE_SIZE_T,
-	FORMAT_TYPE_PTRDIFF
+	FORMAT_TYPE_PTRDIFF,
+	FORMAT_TYPE_FN,
 };
 
 struct printf_spec {
@@ -2512,7 +2513,16 @@ int format_decode(const char *fmt, struct printf_spec *spec)
 		return ++fmt - start;
 
 	case 'p':
-		spec->type = FORMAT_TYPE_PTR;
+		fmt++;
+		if (fmt[0] == 'f' &&
+		    fmt[1] == '(') {
+			fmt += 2;
+			spec->type = FORMAT_TYPE_FN;
+		} else
+			spec->type = FORMAT_TYPE_PTR;
+		return fmt - start;
+	case '(':
+		spec->type = FORMAT_TYPE_FN;
 		return ++fmt - start;
 
 	case '%':
@@ -2594,6 +2604,49 @@ set_precision(struct printf_spec *spec, int prec)
 	}
 }
 
+static void call_pr_fn(struct printbuf *out, void *fn, void **fn_args, unsigned nr_args)
+{
+	typedef void (*printf_fn_0)(struct printbuf *);
+	typedef void (*printf_fn_1)(struct printbuf *, void *);
+	typedef void (*printf_fn_2)(struct printbuf *, void *, void *);
+	typedef void (*printf_fn_3)(struct printbuf *, void *, void *, void *);
+	typedef void (*printf_fn_4)(struct printbuf *, void *, void *, void *, void *);
+	typedef void (*printf_fn_5)(struct printbuf *, void *, void *, void *, void *, void *);
+	typedef void (*printf_fn_6)(struct printbuf *, void *, void *, void *, void *, void *, void *);
+	typedef void (*printf_fn_7)(struct printbuf *, void *, void *, void *, void *, void *, void *, void *);
+	typedef void (*printf_fn_8)(struct printbuf *, void *, void *, void *, void *, void *, void *, void *, void *);
+
+	switch (nr_args) {
+	case 0:
+		((printf_fn_0)fn)(out);
+		break;
+	case 1:
+		((printf_fn_1)fn)(out, fn_args[0]);
+		break;
+	case 2:
+		((printf_fn_2)fn)(out, fn_args[0], fn_args[1]);
+		break;
+	case 3:
+		((printf_fn_3)fn)(out, fn_args[0], fn_args[1], fn_args[2]);
+		break;
+	case 4:
+		((printf_fn_4)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3]);
+		break;
+	case 5:
+		((printf_fn_5)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4]);
+		break;
+	case 6:
+		((printf_fn_6)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4], fn_args[5]);
+		break;
+	case 7:
+		((printf_fn_7)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4], fn_args[5], fn_args[6]);
+		break;
+	case 8:
+		((printf_fn_8)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4], fn_args[5], fn_args[6], fn_args[7]);
+		break;
+	}
+}
+
 /**
  * vpr_buf - Format a string, outputting to a printbuf
  * @out: The printbuf to output to
@@ -2657,6 +2710,30 @@ void vpr_buf(struct printbuf *out, const char *fmt, va_list args)
 				fmt++;
 			break;
 
+		case FORMAT_TYPE_FN: {
+			unsigned nr_args = 0;
+			void *fn_args[8];
+			void *fn = va_arg(args, void *);
+
+			while (1) {
+				if (WARN_ON_ONCE(nr_args == ARRAY_SIZE(fn_args)))
+					goto out;
+				if (*fmt++ != '%')
+					goto out;
+				if (*fmt++ != 'p')
+					goto out;
+				fn_args[nr_args++] = va_arg(args, void *);
+				if (*fmt == ')')
+					break;
+				if (*fmt++ != ',')
+					goto out;
+			}
+
+			call_pr_fn(out, fn, fn_args, nr_args);
+			fmt++; /* past trailing ) */
+			break;
+		}
+
 		case FORMAT_TYPE_PERCENT_CHAR:
 			__pr_char(out, '%');
 			break;
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 04/28] lib/string_helpers: string_get_size() now returns characters wrote
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (2 preceding siblings ...)
  2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
@ 2022-05-19 17:23 ` Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 05/28] lib/printbuf: Heap allocation Kent Overstreet
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

printbuf now needs to know the number of characters that would have been
written if the buffer was too small, like snprintf(); this changes
string_get_size() to return the the return value of snprintf().

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/string_helpers.h | 4 ++--
 lib/string_helpers.c           | 7 +++----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h
index 2e56b4ce98..fd9c150a12 100644
--- a/include/linux/string_helpers.h
+++ b/include/linux/string_helpers.h
@@ -19,8 +19,8 @@ enum string_size_units {
 	STRING_UNITS_2,		/* use binary powers of 2^10 */
 };
 
-void string_get_size(u64 size, u64 blk_size, enum string_size_units units,
-		     char *buf, int len);
+int string_get_size(u64 size, u64 blk_size, enum string_size_units units,
+		    char *buf, int len);
 
 #define UNESCAPE_SPACE		BIT(0)
 #define UNESCAPE_OCTAL		BIT(1)
diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index c890e83f59..9a08b5d774 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -33,8 +33,8 @@
  * at least 9 bytes and will always be zero terminated.
  *
  */
-void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
-		     char *buf, int len)
+int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
+		    char *buf, int len)
 {
 	static const char *const units_10[] = {
 		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
@@ -127,8 +127,7 @@ void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
 	else
 		unit = units_str[units][i];
 
-	snprintf(buf, len, "%u%s %s", (u32)size,
-		 tmp, unit);
+	return snprintf(buf, len, "%u%s %s", (u32)size, tmp, unit);
 }
 EXPORT_SYMBOL(string_get_size);
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 05/28] lib/printbuf: Heap allocation
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (3 preceding siblings ...)
  2022-05-19 17:23 ` [PATCH v2 04/28] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
@ 2022-05-19 17:23 ` Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 06/28] lib/printbuf: Tabstops, indenting Kent Overstreet
                   ` (23 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This makes printbufs optionally heap allocated: a printbuf initialized
with the PRINTBUF initializer will automatically heap allocate and
resize as needed.

Allocations are done with GFP_KERNEL: code should use e.g.
memalloc_nofs_save()/restore() as needed. Since we do not currently have
memalloc_nowait_save()/restore(), in contexts where it is not safe to
block we provide the helpers

  printbuf_atomic_inc()
  printbuf_atomic_dec()

When the atomic count is nonzero, memory allocations will be done with
GFP_NOWAIT.

On memory allocation failure, output will be truncated. Code that wishes
to check for memory allocation failure (in contexts where we should
return -ENOMEM) should check if printbuf->allocation_failure is set.
Since printbufs are expected to be typically used for log messages and
on a best effort basis, we don't return errors directly.

Other helpers provided by this patch:

 - printbuf_make_room(buf, extra)
   Reallocates if necessary to make room for @extra bytes (not including
   terminating null).

 - printbuf_str(buf)
   Returns a null terminated string equivalent to the contents of @buf.
   If @buf was never allocated (or allocation failed), returns a
   constant empty string.

 - printbuf_exit(buf)
   Releases memory allocated by a printbuf.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/printbuf.h | 118 ++++++++++++++++++++++++++++++++++-----
 lib/Makefile             |   2 +-
 lib/printbuf.c           |  70 +++++++++++++++++++++++
 3 files changed, 174 insertions(+), 16 deletions(-)
 create mode 100644 lib/printbuf.c

diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
index 40dc07040d..c114072773 100644
--- a/include/linux/printbuf.h
+++ b/include/linux/printbuf.h
@@ -4,18 +4,68 @@
 #ifndef _LINUX_PRINTBUF_H
 #define _LINUX_PRINTBUF_H
 
-#include <linux/string.h>
-
 /*
- * Printbufs: String buffer for outputting (printing) to, for vsnprintf
+ * Printbufs: Simple strings for printing to, with optional heap allocation
+ *
+ * This code has provisions for use in userspace, to aid in making other code
+ * portable between kernelspace and userspace.
+ *
+ * Basic example:
+ *   struct printbuf buf = PRINTBUF;
+ *
+ *   pr_buf(&buf, "foo=");
+ *   foo_to_text(&buf, foo);
+ *   printk("%s", buf.buf);
+ *   printbuf_exit(&buf);
+ *
+ * Or
+ *   struct printbuf buf = PRINTBUF_EXTERN(char_buf, char_buf_size)
+ *
+ * We can now write pretty printers instead of writing code that dumps
+ * everything to the kernel log buffer, and then those pretty-printers can be
+ * used by other code that outputs to kernel log, sysfs, debugfs, etc.
+ *
+ * Memory allocation: Outputing to a printbuf may allocate memory. This
+ * allocation is done with GFP_KERNEL, by default: use the newer
+ * memalloc_*_(save|restore) functions as needed.
+ *
+ * Since no equivalent yet exists for GFP_ATOMIC/GFP_NOWAIT, memory allocations
+ * will be done with GFP_NOWAIT if printbuf->atomic is nonzero.
+ *
+ * Memory allocation failures: We don't return errors directly, because on
+ * memory allocation failure we usually don't want to bail out and unwind - we
+ * want to print what we've got, on a best-effort basis. But code that does want
+ * to return -ENOMEM may check printbuf.allocation_failure.
  */
 
+#include <linux/string.h>
+
 struct printbuf {
 	char			*buf;
 	unsigned		size;
 	unsigned		pos;
+	/*
+	 * If nonzero, allocations will be done with GFP_ATOMIC:
+	 */
+	u8			atomic;
+	bool			allocation_failure:1;
+	bool			heap_allocated:1;
 };
 
+int printbuf_make_room(struct printbuf *, unsigned);
+const char *printbuf_str(const struct printbuf *);
+void printbuf_exit(struct printbuf *);
+
+/* Initializer for a heap allocated printbuf: */
+#define PRINTBUF ((struct printbuf) { .heap_allocated = true })
+
+/* Initializer a printbuf that points to an external buffer: */
+#define PRINTBUF_EXTERN(_buf, _size)			\
+((struct printbuf) {					\
+	.buf	= _buf,					\
+	.size	= _size,				\
+})
+
 static inline unsigned printbuf_remaining(struct printbuf *out)
 {
 	return out->pos < out->size ? out->size - out->pos : 0;
@@ -28,28 +78,44 @@ static inline unsigned printbuf_written(struct printbuf *out)
 
 static inline void printbuf_nul_terminate(struct printbuf *out)
 {
+	printbuf_make_room(out, 1);
+
 	if (out->pos < out->size)
 		out->buf[out->pos] = 0;
 	else if (out->size)
 		out->buf[out->size - 1] = 0;
 }
 
-static inline void pr_chars(struct printbuf *out, char c, unsigned n)
+static inline void __pr_chars_reserved(struct printbuf *out, char c, unsigned n)
 {
 	memset(out->buf + out->pos,
 	       c,
 	       min(n, printbuf_remaining(out)));
 	out->pos += n;
+}
+
+static inline void pr_chars(struct printbuf *out, char c, unsigned n)
+{
+	printbuf_make_room(out, n);
+	__pr_chars_reserved(out, c, n);
 	printbuf_nul_terminate(out);
 }
 
-static inline void __pr_char(struct printbuf *out, char c)
+/* Doesn't call printbuf_make_room(), doesn't nul terminate: */
+static inline void __pr_char_reserved(struct printbuf *out, char c)
 {
 	if (printbuf_remaining(out))
 		out->buf[out->pos] = c;
 	out->pos++;
 }
 
+/* Doesn't nul terminate: */
+static inline void __pr_char(struct printbuf *out, char c)
+{
+	printbuf_make_room(out, 1);
+	__pr_char_reserved(out, c);
+}
+
 static inline void pr_char(struct printbuf *out, char c)
 {
 	__pr_char(out, c);
@@ -58,6 +124,8 @@ static inline void pr_char(struct printbuf *out, char c)
 
 static inline void pr_bytes(struct printbuf *out, const void *b, unsigned n)
 {
+	printbuf_make_room(out, n);
+
 	memcpy(out->buf + out->pos,
 	       b,
 	       min(n, printbuf_remaining(out)));
@@ -72,23 +140,43 @@ static inline void pr_str(struct printbuf *out, const char *str)
 
 static inline void pr_hex_byte(struct printbuf *out, u8 byte)
 {
-	__pr_char(out, hex_asc_hi(byte));
-	__pr_char(out, hex_asc_lo(byte));
+	printbuf_make_room(out, 2);
+	__pr_char_reserved(out, hex_asc_hi(byte));
+	__pr_char_reserved(out, hex_asc_lo(byte));
 	printbuf_nul_terminate(out);
 }
 
 static inline void pr_hex_byte_upper(struct printbuf *out, u8 byte)
 {
-	__pr_char(out, hex_asc_upper_hi(byte));
-	__pr_char(out, hex_asc_upper_lo(byte));
+	printbuf_make_room(out, 2);
+	__pr_char_reserved(out, hex_asc_upper_hi(byte));
+	__pr_char_reserved(out, hex_asc_upper_lo(byte));
 	printbuf_nul_terminate(out);
 }
 
-#define PRINTBUF ((struct printbuf) { .si_units = PRINTBUF_UNITS_10 })
-#define PRINTBUF_EXTERN(_buf, _size)			\
-((struct printbuf) {					\
-	.buf	= _buf,					\
-	.size	= _size,				\
-})
+/**
+ * printbuf_reset - re-use a printbuf without freeing and re-initializing it:
+ */
+static inline void printbuf_reset(struct printbuf *buf)
+{
+	buf->pos		= 0;
+	buf->allocation_failure	= 0;
+}
+
+/**
+ * printbuf_atomic_inc - mark as entering an atomic section
+ */
+static inline void printbuf_atomic_inc(struct printbuf *buf)
+{
+	buf->atomic++;
+}
+
+/**
+ * printbuf_atomic_inc - mark as leaving an atomic section
+ */
+static inline void printbuf_atomic_dec(struct printbuf *buf)
+{
+	buf->atomic--;
+}
 
 #endif /* _LINUX_PRINTBUF_H */
diff --git a/lib/Makefile b/lib/Makefile
index c588a126a3..31a3904eda 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -34,7 +34,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 is_single_threaded.o plist.o decompress.o kobject_uevent.o \
 	 earlycpio.o seq_buf.o siphash.o dec_and_lock.o \
 	 nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o \
-	 buildid.o
+	 buildid.o printbuf.o
 
 lib-$(CONFIG_PRINTK) += dump_stack.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/printbuf.c b/lib/printbuf.c
new file mode 100644
index 0000000000..0093b34158
--- /dev/null
+++ b/lib/printbuf.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/* Copyright (C) 2022 Kent Overstreet */
+
+#ifdef __KERNEL__
+#include <linux/export.h>
+#include <linux/kernel.h>
+#else
+#define EXPORT_SYMBOL(x)
+#endif
+
+#include <linux/slab.h>
+#include <linux/printbuf.h>
+
+int printbuf_make_room(struct printbuf *out, unsigned extra)
+{
+	unsigned new_size;
+	char *buf;
+
+	if (!out->heap_allocated)
+		return 0;
+
+	/* Reserved space for terminating nul: */
+	extra += 1;
+
+	if (out->pos + extra < out->size)
+		return 0;
+
+	new_size = roundup_pow_of_two(out->size + extra);
+	buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT);
+
+	if (!buf) {
+		out->allocation_failure = true;
+		return -ENOMEM;
+	}
+
+	out->buf	= buf;
+	out->size	= new_size;
+	return 0;
+}
+EXPORT_SYMBOL(printbuf_make_room);
+
+/**
+ * printbuf_str - returns printbuf's buf as a C string, guaranteed to be null
+ * terminated
+ */
+const char *printbuf_str(const struct printbuf *buf)
+{
+	/*
+	 * If we've written to a printbuf then it's guaranteed to be a null
+	 * terminated string - but if we haven't, then we might not have
+	 * allocated a buffer at all:
+	 */
+	return buf->pos
+		? buf->buf
+		: "";
+}
+EXPORT_SYMBOL(printbuf_str);
+
+/**
+ * printbuf_exit - exit a printbuf, freeing memory it owns and poisoning it
+ * against accidental use.
+ */
+void printbuf_exit(struct printbuf *buf)
+{
+	if (buf->heap_allocated) {
+		kfree(buf->buf);
+		buf->buf = ERR_PTR(-EINTR); /* poison value */
+	}
+}
+EXPORT_SYMBOL(printbuf_exit);
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 06/28] lib/printbuf: Tabstops, indenting
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (4 preceding siblings ...)
  2022-05-19 17:23 ` [PATCH v2 05/28] lib/printbuf: Heap allocation Kent Overstreet
@ 2022-05-19 17:23 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 07/28] lib/printbuf: Unit specifiers Kent Overstreet
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:23 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This patch adds two new features to printbuf for structured formatting:

 - Indent level: the indent level, as a number of spaces, may be
   increased with pr_indent_add() and decreased with pr_indent_sub().

   Subsequent lines, when started with pr_newline() (not "\n", although
   that may change) will then be intended according to the current
   indent level. This helps with pretty-printers that structure a large
   amonut of data across multiple lines and multiple functions.

 - Tabstops: Tabstops may be set by assigning to the printbuf->tabstops
   array.

   Then, pr_tab() may be used to advance to the next tabstop, printing
   as many spaces as required - leaving previous output left justified
   to the previous tabstop. pr_tab_rjust() advances to the next tabstop
   but inserts the spaces just after the previous tabstop - right
   justifying the previously-outputted text to the next tabstop.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/printbuf.h |  28 +++++++++
 lib/printbuf.c           | 125 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+)

diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
index c114072773..798e067457 100644
--- a/include/linux/printbuf.h
+++ b/include/linux/printbuf.h
@@ -36,6 +36,23 @@
  * memory allocation failure we usually don't want to bail out and unwind - we
  * want to print what we've got, on a best-effort basis. But code that does want
  * to return -ENOMEM may check printbuf.allocation_failure.
+ *
+ * Indenting, tabstops:
+ *
+ * To aid is writing multi-line pretty printers spread across multiple
+ * functions, printbufs track the current indent level.
+ *
+ * pr_indent_push() and pr_indent_pop() increase and decrease the current indent
+ * level, respectively.
+ *
+ * To use tabstops, set printbuf->tabstops[]; they are in units of spaces, from
+ * start of line. Once set, pr_tab() will output spaces up to the next tabstop.
+ * pr_tab_rjust() will also advance the current line of text up to the next
+ * tabstop, but it does so by shifting text since the previous tabstop up to the
+ * next tabstop - right justifying it.
+ *
+ * Make sure you use pr_newline() instead of \n in the format string for indent
+ * level and tabstops to work corretly.
  */
 
 #include <linux/string.h>
@@ -44,18 +61,29 @@ struct printbuf {
 	char			*buf;
 	unsigned		size;
 	unsigned		pos;
+	unsigned		last_newline;
+	unsigned		last_field;
+	unsigned		indent;
 	/*
 	 * If nonzero, allocations will be done with GFP_ATOMIC:
 	 */
 	u8			atomic;
 	bool			allocation_failure:1;
 	bool			heap_allocated:1;
+	u8			tabstop;
+	u8			tabstops[4];
 };
 
 int printbuf_make_room(struct printbuf *, unsigned);
 const char *printbuf_str(const struct printbuf *);
 void printbuf_exit(struct printbuf *);
 
+void pr_newline(struct printbuf *);
+void pr_indent_add(struct printbuf *, unsigned);
+void pr_indent_sub(struct printbuf *, unsigned);
+void pr_tab(struct printbuf *);
+void pr_tab_rjust(struct printbuf *);
+
 /* Initializer for a heap allocated printbuf: */
 #define PRINTBUF ((struct printbuf) { .heap_allocated = true })
 
diff --git a/lib/printbuf.c b/lib/printbuf.c
index 0093b34158..c9f730a215 100644
--- a/lib/printbuf.c
+++ b/lib/printbuf.c
@@ -11,6 +11,11 @@
 #include <linux/slab.h>
 #include <linux/printbuf.h>
 
+static inline size_t printbuf_linelen(struct printbuf *buf)
+{
+	return buf->pos - buf->last_newline;
+}
+
 int printbuf_make_room(struct printbuf *out, unsigned extra)
 {
 	unsigned new_size;
@@ -68,3 +73,123 @@ void printbuf_exit(struct printbuf *buf)
 	}
 }
 EXPORT_SYMBOL(printbuf_exit);
+
+void pr_newline(struct printbuf *buf)
+{
+	unsigned i;
+
+	printbuf_make_room(buf, 1 + buf->indent);
+
+	__pr_char(buf, '\n');
+
+	buf->last_newline	= buf->pos;
+
+	for (i = 0; i < buf->indent; i++)
+		__pr_char(buf, ' ');
+
+	printbuf_nul_terminate(buf);
+
+	buf->last_field		= buf->pos;
+	buf->tabstop = 0;
+}
+EXPORT_SYMBOL(pr_newline);
+
+/**
+ * pr_indent_add - add to the current indent level
+ *
+ * @buf: printbuf to control
+ * @spaces: number of spaces to add to the current indent level
+ *
+ * Subsequent lines, and the current line if the output position is at the start
+ * of the current line, will be indented by @spaces more spaces.
+ */
+void pr_indent_add(struct printbuf *buf, unsigned spaces)
+{
+	if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
+		spaces = 0;
+
+	buf->indent += spaces;
+	while (spaces--)
+		pr_char(buf, ' ');
+}
+EXPORT_SYMBOL(pr_indent_add);
+
+/**
+ * pr_indent_sub - subtract from the current indent level
+ *
+ * @buf: printbuf to control
+ * @spaces: number of spaces to subtract from the current indent level
+ *
+ * Subsequent lines, and the current line if the output position is at the start
+ * of the current line, will be indented by @spaces less spaces.
+ */
+void pr_indent_sub(struct printbuf *buf, unsigned spaces)
+{
+	if (WARN_ON_ONCE(spaces > buf->indent))
+		spaces = buf->indent;
+
+	if (buf->last_newline + buf->indent == buf->pos) {
+		buf->pos -= spaces;
+		printbuf_nul_terminate(buf);
+	}
+	buf->indent -= spaces;
+}
+EXPORT_SYMBOL(pr_indent_sub);
+
+/**
+ * pr_tab - Advance printbuf to the next tabstop
+ *
+ * @buf: printbuf to control
+ *
+ * Advance output to the next tabstop by printing spaces.
+ */
+void pr_tab(struct printbuf *out)
+{
+	int spaces = max(0, out->tabstops[out->tabstop] - printbuf_linelen(out));
+
+	BUG_ON(out->tabstop > ARRAY_SIZE(out->tabstops));
+
+	pr_chars(out, ' ', spaces);
+
+	out->last_field = out->pos;
+	out->tabstop++;
+}
+EXPORT_SYMBOL(pr_tab);
+
+/**
+ * pr_tab - Advance printbuf to the next tabstop, right justifying previous
+ * output
+ *
+ * @buf: printbuf to control
+ *
+ * Advance output to the next tabstop by inserting spaces immediately after the
+ * previous tabstop, right justifying previously outputted text.
+ */
+void pr_tab_rjust(struct printbuf *buf)
+{
+	BUG_ON(buf->tabstop > ARRAY_SIZE(buf->tabstops));
+
+	if (printbuf_linelen(buf) < buf->tabstops[buf->tabstop]) {
+		unsigned move = buf->pos - buf->last_field;
+		unsigned shift = buf->tabstops[buf->tabstop] -
+			printbuf_linelen(buf);
+
+		printbuf_make_room(buf, shift);
+
+		if (buf->last_field + shift < buf->size)
+			memmove(buf->buf + buf->last_field + shift,
+				buf->buf + buf->last_field,
+				min(move, buf->size - 1 - buf->last_field - shift));
+
+		if (buf->last_field < buf->size)
+			memset(buf->buf + buf->last_field, ' ',
+			       min(shift, buf->size - buf->last_field));
+
+		buf->pos += shift;
+		printbuf_nul_terminate(buf);
+	}
+
+	buf->last_field = buf->pos;
+	buf->tabstop++;
+}
+EXPORT_SYMBOL(pr_tab_rjust);
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (5 preceding siblings ...)
  2022-05-19 17:23 ` [PATCH v2 06/28] lib/printbuf: Tabstops, indenting Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 20:21   ` Andy Shevchenko
  2022-05-19 17:24 ` [PATCH v2 08/28] lib/pretty-printers: pr_string_option(), pr_bitflags() Kent Overstreet
                   ` (21 subsequent siblings)
  28 siblings, 1 reply; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This adds options to printbuf for specifying whether units should be
printed raw (default) or with human readable units, and for controlling
whether human-readable units should be base 2 (default), or base 10.

This also adds new helpers that obey these options:

 - pr_human_readable_u64
 - pr_human_readable_s64
These obey printbuf->si_units

 - pr_units_u64
 - pr_units_s64
These obey both printbuf-human_readable_units and printbuf->si_units

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/printbuf.h | 15 +++++++++++
 lib/printbuf.c           | 57 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)

diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
index 798e067457..c7919ae721 100644
--- a/include/linux/printbuf.h
+++ b/include/linux/printbuf.h
@@ -53,10 +53,19 @@
  *
  * Make sure you use pr_newline() instead of \n in the format string for indent
  * level and tabstops to work corretly.
+ *
+ * Output units: printbuf->units exists to tell pretty-printers how to output
+ * numbers: a raw value (e.g. directly from a superblock field), as bytes, or as
+ * human readable bytes. pr_units() and pr_sectors() obey it.
  */
 
 #include <linux/string.h>
 
+enum printbuf_si {
+	PRINTBUF_UNITS_2,	/* use binary powers of 2^10 */
+	PRINTBUF_UNITS_10,	/* use powers of 10^3 (standard SI) */
+};
+
 struct printbuf {
 	char			*buf;
 	unsigned		size;
@@ -70,6 +79,8 @@ struct printbuf {
 	u8			atomic;
 	bool			allocation_failure:1;
 	bool			heap_allocated:1;
+	enum printbuf_si	si_units:1;
+	bool			human_readable_units:1;
 	u8			tabstop;
 	u8			tabstops[4];
 };
@@ -83,6 +94,10 @@ void pr_indent_add(struct printbuf *, unsigned);
 void pr_indent_sub(struct printbuf *, unsigned);
 void pr_tab(struct printbuf *);
 void pr_tab_rjust(struct printbuf *);
+void pr_human_readable_u64(struct printbuf *, u64);
+void pr_human_readable_s64(struct printbuf *, s64);
+void pr_units_u64(struct printbuf *, u64);
+void pr_units_s64(struct printbuf *, s64);
 
 /* Initializer for a heap allocated printbuf: */
 #define PRINTBUF ((struct printbuf) { .heap_allocated = true })
diff --git a/lib/printbuf.c b/lib/printbuf.c
index c9f730a215..b99d4b0dff 100644
--- a/lib/printbuf.c
+++ b/lib/printbuf.c
@@ -9,6 +9,7 @@
 #endif
 
 #include <linux/slab.h>
+#include <linux/string_helpers.h>
 #include <linux/printbuf.h>
 
 static inline size_t printbuf_linelen(struct printbuf *buf)
@@ -193,3 +194,59 @@ void pr_tab_rjust(struct printbuf *buf)
 	buf->tabstop++;
 }
 EXPORT_SYMBOL(pr_tab_rjust);
+
+/**
+ * pr_human_readable_u64 - Print out a u64 in human readable units
+ *
+ * Units of 2^10 (default) or 10^3 are controlled via @buf->si_units
+ */
+void pr_human_readable_u64(struct printbuf *buf, u64 v)
+{
+	printbuf_make_room(buf, 10);
+	buf->pos += string_get_size(v, 1, !buf->si_units,
+				    buf->buf + buf->pos,
+				    printbuf_remaining(buf));
+}
+EXPORT_SYMBOL(pr_human_readable_u64);
+
+/**
+ * pr_human_readable_s64 - Print out a s64 in human readable units
+ *
+ * Units of 2^10 (default) or 10^3 are controlled via @buf->si_units
+ */
+void pr_human_readable_s64(struct printbuf *buf, s64 v)
+{
+	if (v < 0)
+		pr_char(buf, '-');
+	pr_human_readable_u64(buf, abs(v));
+}
+EXPORT_SYMBOL(pr_human_readable_s64);
+
+/**
+ * pr_human_readable_u64 - Print out a u64 according to printbuf unit options
+ *
+ * Units are either raw (default), or human reabable units (controlled via
+ * @buf->human_readable_units)
+ */
+void pr_units_u64(struct printbuf *out, u64 v)
+{
+	if (out->human_readable_units)
+		pr_human_readable_u64(out, v);
+	else
+		pr_buf(out, "%llu", v);
+}
+EXPORT_SYMBOL(pr_units_u64);
+
+/**
+ * pr_human_readable_s64 - Print out a s64 according to printbuf unit options
+ *
+ * Units are either raw (default), or human reabable units (controlled via
+ * @buf->human_readable_units)
+ */
+void pr_units_s64(struct printbuf *out, s64 v)
+{
+	if (v < 0)
+		pr_char(out, '-');
+	pr_units_u64(out, v);
+}
+EXPORT_SYMBOL(pr_units_s64);
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 08/28] lib/pretty-printers: pr_string_option(), pr_bitflags()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (6 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 07/28] lib/printbuf: Unit specifiers Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 09/28] vsprintf: Improve number() Kent Overstreet
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/pretty-printers.h | 10 ++++++
 lib/Makefile                    |  2 +-
 lib/pretty-printers.c           | 58 +++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/pretty-printers.h
 create mode 100644 lib/pretty-printers.c

diff --git a/include/linux/pretty-printers.h b/include/linux/pretty-printers.h
new file mode 100644
index 0000000000..2e8b6b4426
--- /dev/null
+++ b/include/linux/pretty-printers.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022 Kent Overstreet */
+
+#ifndef _LINUX_PRETTY_PRINTERS_H
+#define _LINUX_PRETTY_PRINTERS_H
+
+void pr_string_option(struct printbuf *, const char * const[], size_t);
+void pr_bitflags(struct printbuf *, const char * const[], u64);
+
+#endif /* _LINUX_PRETTY_PRINTERS_H */
diff --git a/lib/Makefile b/lib/Makefile
index 31a3904eda..98e9aefe62 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -34,7 +34,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 is_single_threaded.o plist.o decompress.o kobject_uevent.o \
 	 earlycpio.o seq_buf.o siphash.o dec_and_lock.o \
 	 nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o \
-	 buildid.o printbuf.o
+	 buildid.o printbuf.o pretty-printers.o
 
 lib-$(CONFIG_PRINTK) += dump_stack.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/pretty-printers.c b/lib/pretty-printers.c
new file mode 100644
index 0000000000..d794648ef9
--- /dev/null
+++ b/lib/pretty-printers.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/* Copyright (C) 2022 Kent Overstreet */
+
+#include <linux/kernel.h>
+#include <linux/printbuf.h>
+
+/**
+ * pr_string_option - Given a list of strings, print out the list and indicate
+ * which option is selected, with square brackets (sysfs style)
+ *
+ * @out: The printbuf to output to
+ * @list: List of strings to choose from
+ * @selected: The option to highlight, with square brackets
+ */
+void pr_string_option(struct printbuf *out,
+		      const char * const list[],
+		      size_t selected)
+{
+	size_t i;
+
+	for (i = 0; list[i]; i++) {
+		if (i)
+			pr_char(out, ' ');
+		if (i == selected)
+			pr_char(out, '[');
+		pr_str(out, list[i]);
+		if (i == selected)
+			pr_char(out, ']');
+	}
+}
+EXPORT_SYMBOL(pr_string_option);
+
+/**
+ * pr_bitflags: Given a bitmap and a list of names for each bit, print out which
+ * bits are on, comma separated
+ *
+ * @out: The printbuf to output to
+ * @list: List of names for each bit
+ * @flags: Bits to print
+ */
+void pr_bitflags(struct printbuf *out,
+		 const char * const list[], u64 flags)
+{
+	unsigned bit, nr = 0;
+	bool first = true;
+
+	while (list[nr])
+		nr++;
+
+	while (flags && (bit = __ffs(flags)) < nr) {
+		if (!first)
+			pr_buf(out, ",");
+		first = false;
+		pr_str(out, list[bit]);
+		flags ^= 1 << bit;
+	}
+}
+EXPORT_SYMBOL(pr_bitflags);
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 09/28] vsprintf: Improve number()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (7 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 08/28] lib/pretty-printers: pr_string_option(), pr_bitflags() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 10/28] vsprintf: pr_u64_minwidth(), pr_u64() Kent Overstreet
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This patch refactors number() to make it a bit clearer, and it also
changes it to call printbuf_make_room() only once at the start, instead
of in the printbuf output helpers.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 84 ++++++++++++++++++++++++++------------------------
 1 file changed, 43 insertions(+), 41 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 7fbeaf50d1..d1372c0b37 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -453,92 +453,94 @@ void number(struct printbuf *out, unsigned long long num,
 {
 	/* put_dec requires 2-byte alignment of the buffer. */
 	char tmp[3 * sizeof(num)] __aligned(2);
-	char sign;
-	char locase;
+	char sign = 0;
+	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
+	 * produces same digits or (maybe lowercased) letters */
+	char locase = (spec.flags & SMALL);
 	int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
-	int i;
 	bool is_zero = num == 0LL;
 	int field_width = spec.field_width;
 	int precision = spec.precision;
+	int nr_digits = 0;
+	int output_bytes = 0;
 
-	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
-	 * produces same digits or (maybe lowercased) letters */
-	locase = (spec.flags & SMALL);
 	if (spec.flags & LEFT)
 		spec.flags &= ~ZEROPAD;
-	sign = 0;
 	if (spec.flags & SIGN) {
 		if ((signed long long)num < 0) {
 			sign = '-';
 			num = -(signed long long)num;
-			field_width--;
+			output_bytes++;
 		} else if (spec.flags & PLUS) {
 			sign = '+';
-			field_width--;
+			output_bytes++;
 		} else if (spec.flags & SPACE) {
 			sign = ' ';
-			field_width--;
+			output_bytes++;
 		}
 	}
 	if (need_pfx) {
 		if (spec.base == 16)
-			field_width -= 2;
+			output_bytes += 2;
 		else if (!is_zero)
-			field_width--;
+			output_bytes++;
 	}
 
 	/* generate full string in tmp[], in reverse order */
-	i = 0;
-	if (num < spec.base)
-		tmp[i++] = hex_asc_upper[num] | locase;
-	else if (spec.base != 10) { /* 8 or 16 */
+	if (spec.base == 10) {
+		nr_digits = put_dec(tmp, num) - tmp;
+	} else { /* 8 or 16 */
 		int mask = spec.base - 1;
-		int shift = 3;
+		int shift = ilog2((unsigned) spec.base);
 
-		if (spec.base == 16)
-			shift = 4;
 		do {
-			tmp[i++] = (hex_asc_upper[((unsigned char)num) & mask] | locase);
+			tmp[nr_digits++] = (hex_asc_upper[((unsigned char)num) & mask] | locase);
 			num >>= shift;
 		} while (num);
-	} else { /* base 10 */
-		i = put_dec(tmp, num) - tmp;
 	}
 
 	/* printing 100 using %2d gives "100", not "00" */
-	if (i > precision)
-		precision = i;
+	precision = max(nr_digits, precision);
+	output_bytes += precision;
+	field_width = max(0, field_width - output_bytes);
+
+	printbuf_make_room(out, field_width + output_bytes);
+
 	/* leading space padding */
-	field_width = max(0, field_width - precision);
 	if (!(spec.flags & (ZEROPAD | LEFT))) {
-		pr_chars(out, ' ', field_width);
+		__pr_chars_reserved(out, ' ', field_width);
 		field_width = 0;
 	}
+
 	/* sign */
 	if (sign)
-		__pr_char(out, sign);
+		__pr_char_reserved(out, sign);
+
 	/* "0x" / "0" prefix */
 	if (need_pfx) {
 		if (spec.base == 16 || !is_zero)
-			__pr_char(out, '0');
+			__pr_char_reserved(out, '0');
 		if (spec.base == 16)
-			__pr_char(out, 'X' | locase);
+			__pr_char_reserved(out, 'X' | locase);
 	}
-	/* zero or space padding */
-	if (!(spec.flags & LEFT)) {
-		char c = ' ' + (spec.flags & ZEROPAD);
 
-		pr_chars(out, c, field_width);
-		field_width = 0;
-	}
-	/* hmm even more zero padding? */
-	if (precision > i)
-		pr_chars(out, '0', precision - i);
+	/* zero padding */
+	if (!(spec.flags & LEFT))
+		__pr_chars_reserved(out, '0', field_width);
+
+	/* zero padding from precision */
+	if (precision > nr_digits)
+		__pr_chars_reserved(out, '0', precision - nr_digits);
+
 	/* actual digits of result */
-	while (--i >= 0)
-		__pr_char(out, tmp[i]);
+	while (--nr_digits >= 0)
+		__pr_char_reserved(out, tmp[nr_digits]);
+
 	/* trailing space padding */
-	pr_chars(out, ' ', field_width);
+	if (spec.flags & LEFT)
+		__pr_chars_reserved(out, ' ', field_width);
+
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 10/28] vsprintf: pr_u64_minwidth(), pr_u64()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (8 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 09/28] vsprintf: Improve number() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 11/28] vsprintf: Lift pr_hex_bytes() out from hex_string() Kent Overstreet
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This adds two new-style printbuf helpers for printing simple u64s, and
converts num_to_str() to be a simple wrapper around pr_u64_minwidth().

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 100 ++++++++++++++++++++++++-------------------------
 1 file changed, 48 insertions(+), 52 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index d1372c0b37..3f5638d27a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -363,41 +363,51 @@ char *put_dec(char *buf, unsigned long long n)
 
 #endif
 
-/*
- * Convert passed number to decimal string.
- * Returns the length of string.  On buffer overflow, returns 0.
- *
- * If speed is not important, use snprintf(). It's easy to read the code.
+/**
+ * pr_u64_minwidth - print a u64, in decimal, with zero padding
+ * @out: printbuf to output to
+ * @num: u64 to print
+ * @width: minimum width
  */
-int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
+void pr_u64_minwidth(struct printbuf *out, u64 num, unsigned width)
 {
 	/* put_dec requires 2-byte alignment of the buffer. */
 	char tmp[sizeof(num) * 3] __aligned(2);
-	int idx, len;
+	unsigned len = put_dec(tmp, num) - tmp;
 
-	/* put_dec() may work incorrectly for num = 0 (generate "", not "0") */
-	if (num <= 9) {
-		tmp[0] = '0' + num;
-		len = 1;
-	} else {
-		len = put_dec(tmp, num) - tmp;
-	}
+	printbuf_make_room(out, max(len, width));
 
-	if (len > size || width > size)
-		return 0;
+	if (width > len)
+		__pr_chars_reserved(out, '0', width - len);
 
-	if (width > len) {
-		width = width - len;
-		for (idx = 0; idx < width; idx++)
-			buf[idx] = ' ';
-	} else {
-		width = 0;
-	}
+	while (len)
+		__pr_char_reserved(out, tmp[--len]);
+	printbuf_nul_terminate(out);
+}
 
-	for (idx = 0; idx < len; ++idx)
-		buf[idx + width] = tmp[len - idx - 1];
+/**
+ * pr_u64 - print a simple u64, in decimal
+ * @out: printbuf to output to
+ * @num: u64 to print
+ */
+void pr_u64(struct printbuf *out, u64 num)
+{
+	pr_u64_minwidth(out, num, 0);
+}
 
-	return len + width;
+/*
+ * Convert passed number to decimal string.
+ * Returns the length of string.  On buffer overflow, returns 0.
+ *
+ * Consider switching to printbufs and using pr_u64() or pr_u64_minwith()
+ * instead.
+ */
+int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
+{
+	struct printbuf out = PRINTBUF_EXTERN(buf, size);
+
+	pr_u64_minwidth(&out, num, width);
+	return out.pos;
 }
 
 #define SIGN	1		/* unsigned/signed, must be 1 */
@@ -996,20 +1006,6 @@ static const struct printf_spec default_dec_spec = {
 	.precision = -1,
 };
 
-static const struct printf_spec default_dec02_spec = {
-	.base = 10,
-	.field_width = 2,
-	.precision = -1,
-	.flags = ZEROPAD,
-};
-
-static const struct printf_spec default_dec04_spec = {
-	.base = 10,
-	.field_width = 4,
-	.precision = -1,
-	.flags = ZEROPAD,
-};
-
 static noinline_for_stack
 void resource_string(struct printbuf *out, struct resource *res,
 		     struct printf_spec spec, const char *fmt)
@@ -1208,12 +1204,12 @@ void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
 			__pr_char(out, ',');
 		first = false;
 
-		number(out, rbot, default_dec_spec);
+		pr_u64(out, rbot);
 		if (rtop == rbot + 1)
 			continue;
 
-		__pr_char(out, '-');
-		number(out, rtop - 1, default_dec_spec);
+		pr_char(out, '-');
+		pr_u64(out, rtop - 1);
 	}
 }
 
@@ -1762,21 +1758,21 @@ void date_str(struct printbuf *out,
 	int year = tm->tm_year + (r ? 0 : 1900);
 	int mon = tm->tm_mon + (r ? 0 : 1);
 
-	number(out, year, default_dec04_spec);
-	__pr_char(out, '-');
-	number(out, mon, default_dec02_spec);
-	__pr_char(out, '-');
-	number(out, tm->tm_mday, default_dec02_spec);
+	pr_u64_minwidth(out, year, 4);
+	pr_char(out, '-');
+	pr_u64_minwidth(out, mon, 2);
+	pr_char(out, '-');
+	pr_u64_minwidth(out, tm->tm_mday, 2);
 }
 
 static noinline_for_stack
 void time_str(struct printbuf *out, const struct rtc_time *tm, bool r)
 {
-	number(out, tm->tm_hour, default_dec02_spec);
+	pr_u64_minwidth(out, tm->tm_hour, 2);
 	__pr_char(out, ':');
-	number(out, tm->tm_min, default_dec02_spec);
+	pr_u64_minwidth(out, tm->tm_min, 2);
 	__pr_char(out, ':');
-	number(out, tm->tm_sec, default_dec02_spec);
+	pr_u64_minwidth(out, tm->tm_sec, 2);
 }
 
 static noinline_for_stack
@@ -2054,7 +2050,7 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 			str_spec.precision = precision;
 			break;
 		case 'p':	/* phandle */
-			number(out, (unsigned int)dn->phandle, default_dec_spec);
+			pr_u64(out, (unsigned int)dn->phandle);
 			break;
 		case 'P':	/* path-spec */
 			p = fwnode_get_name(of_fwnode_handle(dn));
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 11/28] vsprintf: Lift pr_hex_bytes() out from hex_string()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (9 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 10/28] vsprintf: pr_u64_minwidth(), pr_u64() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 12/28] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This factors pr_hex_bytes(), a new printbuf-style helper, out from
hex_string and adds it to pretty-printers.c.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/pretty-printers.h |  1 +
 lib/pretty-printers.c           | 23 +++++++++++++++++++++++
 lib/vsprintf.c                  | 13 ++++---------
 3 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/include/linux/pretty-printers.h b/include/linux/pretty-printers.h
index 2e8b6b4426..ded34622e8 100644
--- a/include/linux/pretty-printers.h
+++ b/include/linux/pretty-printers.h
@@ -4,6 +4,7 @@
 #ifndef _LINUX_PRETTY_PRINTERS_H
 #define _LINUX_PRETTY_PRINTERS_H
 
+void pr_hex_bytes(struct printbuf *, const u8 *, unsigned, unsigned);
 void pr_string_option(struct printbuf *, const char * const[], size_t);
 void pr_bitflags(struct printbuf *, const char * const[], u64);
 
diff --git a/lib/pretty-printers.c b/lib/pretty-printers.c
index d794648ef9..162e6865f9 100644
--- a/lib/pretty-printers.c
+++ b/lib/pretty-printers.c
@@ -4,6 +4,29 @@
 #include <linux/kernel.h>
 #include <linux/printbuf.h>
 
+/**
+ * pr_hex_bytes - Print a string of hex bytes, with optional separator
+ *
+ * @out: The printbuf to output to
+ * @addr: Buffer to print
+ * @nr: Number of bytes to print
+ * @separator: Optional separator character between each byte
+ */
+void pr_hex_bytes(struct printbuf *out, const u8 *addr,
+		  unsigned nr, unsigned separator)
+{
+	unsigned i;
+
+	for (i = 0; i < nr; ++i) {
+		if (separator && i)
+			pr_char(out, separator);
+		pr_hex_byte(out, addr[i]);
+	}
+
+	printbuf_nul_terminate(out);
+}
+EXPORT_SYMBOL(pr_hex_bytes);
+
 /**
  * pr_string_option - Given a list of strings, print out the list and indicate
  * which option is selected, with square brackets (sysfs style)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3f5638d27a..d4293b4a40 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -52,6 +52,7 @@
 #include <asm/byteorder.h>	/* cpu_to_le16 */
 
 #include <linux/string_helpers.h>
+#include <linux/pretty-printers.h>
 #include "kstrtox.h"
 
 static noinline unsigned long long simple_strntoull(const char *startp, size_t max_chars, char **endp, unsigned int base)
@@ -1107,10 +1108,10 @@ void resource_string(struct printbuf *out, struct resource *res,
 }
 
 static noinline_for_stack
-void hex_string(struct printbuf *out, u8 *addr,
+void hex_string(struct printbuf *out, const u8 *addr,
 		struct printf_spec spec, const char *fmt)
 {
-	int i, len = 1;		/* if we pass '%ph[CDN]', field width remains
+	int len = 1;		/* if we pass '%ph[CDN]', field width remains
 				   negative value, fallback to the default */
 	char separator;
 
@@ -1139,13 +1140,7 @@ void hex_string(struct printbuf *out, u8 *addr,
 	if (spec.field_width > 0)
 		len = min_t(int, spec.field_width, 64);
 
-	for (i = 0; i < len; ++i) {
-		__pr_char(out, hex_asc_hi(addr[i]));
-		__pr_char(out, hex_asc_lo(addr[i]));
-
-		if (separator && i != len - 1)
-			__pr_char(out, separator);
-	}
+	pr_hex_bytes(out, addr, len, separator);
 }
 
 static noinline_for_stack
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 12/28] test_printf: Drop requirement that sprintf not write past nul
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (10 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 11/28] vsprintf: Lift pr_hex_bytes() out from hex_string() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 13/28] vsprintf: Start consolidating printf_spec handling Kent Overstreet
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

The current test code checks that sprintf never writes past the
terminating nul. This is a rather strange requirement, completely
separate from writing past the end of the buffer, which of course we
can't do: writing anywhere to the buffer passed to snprintf, within size
of course, should be perfectly fine.

Since this check has no documentation as to where it comes from or what
depends on it, and it's getting in the way of further refactoring
(printf_spec handling is right now scattered massively throughout the
code, and we'd like to consolidate it) - delete it.

Also, many current pretty-printers building up their output on the
stack, and then copy it to the actual output buffer - by eliminating
this requirement we can kill those extra buffers.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/test_printf.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/lib/test_printf.c b/lib/test_printf.c
index 1e1604ef1a..0804ab788d 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -78,12 +78,6 @@ do_test(int bufsize, const char *expect, int elen,
 		return 1;
 	}
 
-	if (memchr_inv(test_buffer + written + 1, FILL_CHAR, BUF_SIZE + PAD_SIZE - (written + 1))) {
-		pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote beyond the nul-terminator\n",
-			bufsize, fmt);
-		return 1;
-	}
-
 	if (memcmp(test_buffer, expect, written)) {
 		pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote '%s', expected '%.*s'\n",
 			bufsize, fmt, test_buffer, written, expect);
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 13/28] vsprintf: Start consolidating printf_spec handling
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (11 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 12/28] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 14/28] vsprintf: Refactor resource_string() Kent Overstreet
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

printf_spec is right now something of a mess - it's a grab-bag of state
that's interpreted inconsistently by different code, and it's scattered
throughout vsprintf.c.

We'd like to get it out of the pretty-printers, and have it be solely
the responsibility of vsprintf()/vpr_buf(), the code that parses and
handles format strings.

Most of the code that uses printf_spec is only using it for a minimum &
maximum field width - that can be done at the toplevel by checking how
much we just printed, and padding or truncating it as necessary. This
patch takes those "simple" uses of printf_spec and moves them as far up
the call stack as possible.

This patch also renames some helpers and creates new ones that don't
take printf_spec:
 - do_width_precision: new helper that handles with/precision of
   printf_spec
 - error_string		-> error_string_spec
 - check_pointer	-> check_pointer_spec
 - string		-> string_spec

Next patches will be reducing/eliminating uses of the *_spec versions.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 248 +++++++++++++++++++++++++++----------------------
 1 file changed, 138 insertions(+), 110 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index d4293b4a40..6bbbb52bb0 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -613,6 +613,19 @@ void widen_string(struct printbuf *out, int n,
 		pr_chars(out, ' ', spaces);
 }
 
+static void do_width_precision(struct printbuf *out, unsigned prev_pos,
+			       struct printf_spec spec)
+{
+	unsigned n = out->pos - prev_pos;
+
+	if (n > spec.precision) {
+		out->pos -= n - spec.precision;
+		n = spec.precision;
+	}
+
+	widen_string(out, n, spec);
+}
+
 /* Handle string from a well known address. */
 static void string_nocheck(struct printbuf *out,
 			   const char *s,
@@ -645,7 +658,7 @@ static void err_ptr(struct printbuf *out, void *ptr,
 }
 
 /* Be careful: error messages must fit into the given buffer. */
-static void error_string(struct printbuf *out, const char *s,
+static void error_string_spec(struct printbuf *out, const char *s,
 			 struct printf_spec spec)
 {
 	/*
@@ -675,7 +688,7 @@ static const char *check_pointer_msg(const void *ptr)
 	return NULL;
 }
 
-static int check_pointer(struct printbuf *out,
+static int check_pointer_spec(struct printbuf *out,
 			 const void *ptr,
 			 struct printf_spec spec)
 {
@@ -683,7 +696,7 @@ static int check_pointer(struct printbuf *out,
 
 	err_msg = check_pointer_msg(ptr);
 	if (err_msg) {
-		error_string(out, err_msg, spec);
+		error_string_spec(out, err_msg, spec);
 		return -EFAULT;
 	}
 
@@ -691,16 +704,47 @@ static int check_pointer(struct printbuf *out,
 }
 
 static noinline_for_stack
-void string(struct printbuf *out,
+void string_spec(struct printbuf *out,
 	    const char *s,
 	    struct printf_spec spec)
 {
-	if (check_pointer(out, s, spec))
+	if (check_pointer_spec(out, s, spec))
 		return;
 
 	string_nocheck(out, s, spec);
 }
 
+static void error_string(struct printbuf *out, const char *s)
+{
+	/*
+	 * Hard limit to avoid a completely insane messages. It actually
+	 * works pretty well because most error messages are in
+	 * the many pointer format modifiers.
+	 */
+	pr_bytes(out, s, min(strlen(s), 2 * sizeof(void *)));
+}
+
+static int check_pointer(struct printbuf *out, const void *ptr)
+{
+	const char *err_msg;
+
+	err_msg = check_pointer_msg(ptr);
+	if (err_msg) {
+		error_string(out, err_msg);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void string(struct printbuf *out, const char *s)
+{
+	if (check_pointer(out, s))
+		return;
+
+	pr_str(out, s);
+}
+
 static void pointer_string(struct printbuf *out,
 			   const void *ptr,
 			   struct printf_spec spec)
@@ -824,7 +868,7 @@ static void ptr_to_id(struct printbuf *out,
 	if (ret) {
 		spec.field_width = 2 * sizeof(ptr);
 		/* string length must be less than default_width */
-		return error_string(out, str, spec);
+		return error_string_spec(out, str, spec);
 	}
 
 	pointer_string(out, (const void *)hashval, spec);
@@ -851,7 +895,7 @@ void restricted_pointer(struct printbuf *out,
 		if (in_irq() || in_serving_softirq() || in_nmi()) {
 			if (spec.field_width == -1)
 				spec.field_width = 2 * sizeof(ptr);
-			return error_string(out, "pK-error", spec);
+			return error_string_spec(out, "pK-error", spec);
 		}
 
 		/*
@@ -881,14 +925,12 @@ void restricted_pointer(struct printbuf *out,
 }
 
 static noinline_for_stack
-void dentry_name(struct printbuf *out,
-		 const struct dentry *d, struct printf_spec spec,
+void dentry_name(struct printbuf *out, const struct dentry *d,
 		 const char *fmt)
 {
-	const char *array[4], *s;
+	const char *array[4];
 	const struct dentry *p;
-	int depth;
-	int i, n;
+	int i, depth;
 
 	switch (fmt[1]) {
 		case '2': case '3': case '4':
@@ -900,7 +942,7 @@ void dentry_name(struct printbuf *out,
 
 	rcu_read_lock();
 	for (i = 0; i < depth; i++, d = p) {
-		if (check_pointer(out, d, spec)) {
+		if (check_pointer(out, d)) {
 			rcu_read_unlock();
 			return;
 		}
@@ -914,55 +956,46 @@ void dentry_name(struct printbuf *out,
 			break;
 		}
 	}
-	s = array[--i];
-	for (n = 0; n != spec.precision; n++) {
-		char c = *s++;
-		if (!c) {
-			if (!i)
-				break;
-			c = '/';
-			s = array[--i];
-		}
-		__pr_char(out, c);
+	while (1) {
+		pr_str(out, array[--i]);
+		if (!i)
+			break;
+		__pr_char(out, '/');
 	}
 	rcu_read_unlock();
-	widen_string(out, n, spec);
 }
 
 static noinline_for_stack
-void file_dentry_name(struct printbuf *out,
-		      const struct file *f,
-		      struct printf_spec spec, const char *fmt)
+void file_dentry_name(struct printbuf *out, const struct file *f,
+		      const char *fmt)
 {
-	if (check_pointer(out, f, spec))
+	if (check_pointer(out, f))
 		return;
 
-	return dentry_name(out, f->f_path.dentry, spec, fmt);
+	return dentry_name(out, f->f_path.dentry, fmt);
 }
 #ifdef CONFIG_BLOCK
 static noinline_for_stack
-void bdev_name(struct printbuf *out,
-	       struct block_device *bdev,
-	       struct printf_spec spec, const char *fmt)
+void bdev_name(struct printbuf *out, struct block_device *bdev)
 {
 	struct gendisk *hd;
 
-	if (check_pointer(out, bdev, spec))
+	if (check_pointer(out, bdev))
 		return;
 
 	hd = bdev->bd_disk;
-	string(out, hd->disk_name, spec);
+	string(out, hd->disk_name);
 	if (bdev->bd_partno) {
 		if (isdigit(hd->disk_name[strlen(hd->disk_name)-1]))
 			__pr_char(out, 'p');
-		number(out, bdev->bd_partno, spec);
+		pr_u64(out, bdev->bd_partno);
 	}
 }
 #endif
 
 static noinline_for_stack
 void symbol_string(struct printbuf *out, void *ptr,
-		   struct printf_spec spec, const char *fmt)
+		   const char *fmt)
 {
 	unsigned long value;
 #ifdef CONFIG_KALLSYMS
@@ -985,17 +1018,12 @@ void symbol_string(struct printbuf *out, void *ptr,
 	else
 		sprint_symbol_no_offset(sym, value);
 
-	string_nocheck(out, sym, spec);
+	pr_str(out, sym);
 #else
 	special_hex_number(out, value, sizeof(void *));
 #endif
 }
 
-static const struct printf_spec default_str_spec = {
-	.field_width = -1,
-	.precision = -1,
-};
-
 static const struct printf_spec default_flag_spec = {
 	.base = 16,
 	.precision = -1,
@@ -1054,7 +1082,7 @@ void resource_string(struct printbuf *out, struct resource *res,
 	int decode = (fmt[0] == 'R') ? 1 : 0;
 	const struct printf_spec *specp;
 
-	if (check_pointer(out, res, spec))
+	if (check_pointer_spec(out, res, spec))
 		return;
 
 	__pr_char(&sym, '[');
@@ -1119,7 +1147,7 @@ void hex_string(struct printbuf *out, const u8 *addr,
 		/* nothing to print */
 		return;
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer_spec(out, addr, spec))
 		return;
 
 	switch (fmt[1]) {
@@ -1152,7 +1180,7 @@ void bitmap_string(struct printbuf *out, unsigned long *bitmap,
 	int i, chunksz;
 	bool first = true;
 
-	if (check_pointer(out, bitmap, spec))
+	if (check_pointer_spec(out, bitmap, spec))
 		return;
 
 	/* reused to print numbers */
@@ -1191,7 +1219,7 @@ void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
 	bool first = true;
 	int rbot, rtop;
 
-	if (check_pointer(out, bitmap, spec))
+	if (check_pointer_spec(out, bitmap, spec))
 		return ;
 
 	for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) {
@@ -1218,7 +1246,7 @@ void mac_address_string(struct printbuf *out, u8 *addr,
 	char separator;
 	bool reversed = false;
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer_spec(out, addr, spec))
 		return;
 
 	switch (fmt[1]) {
@@ -1295,8 +1323,6 @@ void ip4_string(struct printbuf *out,
 			__pr_char(out, '.');
 		index += step;
 	}
-
-	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
@@ -1525,7 +1551,7 @@ void ip_addr_string(struct printbuf *out, const void *ptr,
 {
 	char *err_fmt_msg;
 
-	if (check_pointer(out, ptr, spec))
+	if (check_pointer_spec(out, ptr, spec))
 		return;
 
 	switch (fmt[1]) {
@@ -1546,12 +1572,12 @@ void ip_addr_string(struct printbuf *out, const void *ptr,
 		case AF_INET6:
 			return ip6_addr_string_sa(out, &sa->v6, spec, fmt);
 		default:
-			return error_string(out, "(einval)", spec);
+			return error_string_spec(out, "(einval)", spec);
 		}}
 	}
 
 	err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)";
-	return error_string(out, err_fmt_msg, spec);
+	return error_string_spec(out, err_fmt_msg, spec);
 }
 
 static noinline_for_stack
@@ -1566,7 +1592,7 @@ void escaped_string(struct printbuf *out, u8 *addr,
 	if (spec.field_width == 0)
 		return;				/* nothing to print */
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer_spec(out, addr, spec))
 		return;
 
 	do {
@@ -1611,7 +1637,7 @@ static void va_format(struct printbuf *out,
 {
 	va_list va;
 
-	if (check_pointer(out, va_fmt, spec))
+	if (check_pointer_spec(out, va_fmt, spec))
 		return;
 
 	va_copy(va, *va_fmt->va);
@@ -1620,16 +1646,13 @@ static void va_format(struct printbuf *out,
 }
 
 static noinline_for_stack
-void uuid_string(struct printbuf *out, const u8 *addr,
-		 struct printf_spec spec, const char *fmt)
+void uuid_string(struct printbuf *out, const u8 *addr, const char *fmt)
 {
-	char uuid_buf[UUID_STRING_LEN + 1];
-	struct printbuf uuid = PRINTBUF_EXTERN(uuid_buf, sizeof(uuid_buf));
 	int i;
 	const u8 *index = uuid_index;
 	bool uc = false;
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer(out, addr))
 		return;
 
 	switch (*(++fmt)) {
@@ -1646,32 +1669,28 @@ void uuid_string(struct printbuf *out, const u8 *addr,
 
 	for (i = 0; i < 16; i++) {
 		if (uc)
-			pr_hex_byte_upper(&uuid, addr[index[i]]);
+			pr_hex_byte_upper(out, addr[index[i]]);
 		else
-			pr_hex_byte(&uuid, addr[index[i]]);
+			pr_hex_byte(out, addr[index[i]]);
 		switch (i) {
 		case 3:
 		case 5:
 		case 7:
 		case 9:
-			__pr_char(&uuid, '-');
+			__pr_char(out, '-');
 			break;
 		}
 	}
-
-	printbuf_nul_terminate(&uuid);
-
-	string_nocheck(out, uuid_buf, spec);
 }
 
 static noinline_for_stack
 void netdev_bits(struct printbuf *out, const void *addr,
-		 struct printf_spec spec,  const char *fmt)
+		 const char *fmt)
 {
 	unsigned long long num;
 	int size;
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer(out, addr))
 		return;
 
 	switch (fmt[1]) {
@@ -1681,7 +1700,7 @@ void netdev_bits(struct printbuf *out, const void *addr,
 		special_hex_number(out, num, size);
 		break;
 	default:
-		error_string(out, "(%pN?)", spec);
+		error_string(out, "(%pN?)");
 		break;
 	}
 }
@@ -1696,9 +1715,9 @@ void fourcc_string(struct printbuf *out, const u32 *fourcc,
 	u32 val;
 
 	if (fmt[1] != 'c' || fmt[2] != 'c')
-		return error_string(out, "(%p4?)", spec);
+		return error_string_spec(out, "(%p4?)", spec);
 
-	if (check_pointer(out, fourcc, spec))
+	if (check_pointer_spec(out, fourcc, spec))
 		return;
 
 	val = *fourcc & ~BIT(31);
@@ -1718,17 +1737,17 @@ void fourcc_string(struct printbuf *out, const u32 *fourcc,
 	__pr_char(&output, ')');
 	printbuf_nul_terminate(&output);
 
-	string(out, output_buf, spec);
+	string_spec(out, output_buf, spec);
 }
 
 static noinline_for_stack
 void address_val(struct printbuf *out, const void *addr,
-		 struct printf_spec spec, const char *fmt)
+		 const char *fmt)
 {
 	unsigned long long num;
 	int size;
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer(out, addr))
 		return;
 
 	switch (fmt[1]) {
@@ -1779,7 +1798,7 @@ void rtc_str(struct printbuf *out, const struct rtc_time *tm,
 	bool found = true;
 	int count = 2;
 
-	if (check_pointer(out, tm, spec))
+	if (check_pointer_spec(out, tm, spec))
 		return;
 
 	switch (fmt[count]) {
@@ -1849,7 +1868,7 @@ void time_and_date(struct printbuf *out,
 	case 'T':
 		return time64_str(out, *(const time64_t *)ptr, spec, fmt);
 	default:
-		return error_string(out, "(%pt?)", spec);
+		return error_string_spec(out, "(%pt?)", spec);
 	}
 }
 
@@ -1858,16 +1877,16 @@ void clock(struct printbuf *out, struct clk *clk,
 	   struct printf_spec spec, const char *fmt)
 {
 	if (!IS_ENABLED(CONFIG_HAVE_CLK))
-		return error_string(out, "(%pC?)", spec);
+		return error_string_spec(out, "(%pC?)", spec);
 
-	if (check_pointer(out, clk, spec))
+	if (check_pointer_spec(out, clk, spec))
 		return;
 
 	switch (fmt[1]) {
 	case 'n':
 	default:
 #ifdef CONFIG_COMMON_CLK
-		return string(out, __clk_get_name(clk), spec);
+		return string_spec(out, __clk_get_name(clk), spec);
 #else
 		return ptr_to_id(out, clk, spec);
 #endif
@@ -1885,7 +1904,7 @@ void format_flags(struct printbuf *out, unsigned long flags,
 		if ((flags & mask) != mask)
 			continue;
 
-		string(out, names->name, default_str_spec);
+		string(out, names->name);
 
 		flags &= ~mask;
 		if (flags)
@@ -1943,7 +1962,7 @@ void format_page_flags(struct printbuf *out, unsigned long flags)
 		if (append)
 			__pr_char(out, '|');
 
-		string(out, pff[i].name, default_str_spec);
+		string(out, pff[i].name);
 		__pr_char(out, '=');
 		number(out, (flags >> pff[i].shift) & pff[i].mask, *pff[i].spec);
 
@@ -1959,7 +1978,7 @@ void flags_string(struct printbuf *out, void *flags_ptr,
 	unsigned long flags;
 	const struct trace_print_flags *names;
 
-	if (check_pointer(out, flags_ptr, spec))
+	if (check_pointer_spec(out, flags_ptr, spec))
 		return;
 
 	switch (fmt[1]) {
@@ -1974,7 +1993,7 @@ void flags_string(struct printbuf *out, void *flags_ptr,
 		names = gfpflag_names;
 		break;
 	default:
-		return error_string(out, "(%pG?)", spec);
+		return error_string_spec(out, "(%pG?)", spec);
 	}
 
 	return format_flags(out, flags, names);
@@ -1991,10 +2010,8 @@ void fwnode_full_name_string(struct printbuf *out,
 		struct fwnode_handle *__fwnode =
 			fwnode_get_nth_parent(fwnode, depth);
 
-		string(out, fwnode_get_name_prefix(__fwnode),
-		       default_str_spec);
-		string(out, fwnode_get_name(__fwnode),
-		       default_str_spec);
+		string(out, fwnode_get_name_prefix(__fwnode));
+		string(out, fwnode_get_name(__fwnode));
 
 		fwnode_handle_put(__fwnode);
 	}
@@ -2015,12 +2032,12 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 	str_spec.field_width = -1;
 
 	if (fmt[0] != 'F')
-		return error_string(out, "(%pO?)", spec);
+		return error_string_spec(out, "(%pO?)", spec);
 
 	if (!IS_ENABLED(CONFIG_OF))
-		return error_string(out, "(%pOF?)", spec);
+		return error_string_spec(out, "(%pOF?)", spec);
 
-	if (check_pointer(out, dn, spec))
+	if (check_pointer_spec(out, dn, spec))
 		return;
 
 	/* simple case without anything any more format specifiers */
@@ -2041,7 +2058,7 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 			p = fwnode_get_name(of_fwnode_handle(dn));
 			precision = str_spec.precision;
 			str_spec.precision = strchrnul(p, '@') - p;
-			string(out, p, str_spec);
+			string_spec(out, p, str_spec);
 			str_spec.precision = precision;
 			break;
 		case 'p':	/* phandle */
@@ -2051,7 +2068,7 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 			p = fwnode_get_name(of_fwnode_handle(dn));
 			if (!p[1])
 				p = "/";
-			string(out, p, str_spec);
+			string_spec(out, p, str_spec);
 			break;
 		case 'F':	/* flags */
 			tbuf[0] = of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-';
@@ -2061,18 +2078,18 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 			tbuf[4] = 0;
 			string_nocheck(out, tbuf, str_spec);
 			break;
-		case 'c':	/* major compatible string */
+		case 'c':	/* major compatible string_spec */
 			ret = of_property_read_string(dn, "compatible", &p);
 			if (!ret)
-				string(out, p, str_spec);
+				string_spec(out, p, str_spec);
 			break;
-		case 'C':	/* full compatible string */
+		case 'C':	/* full compatible string_spec */
 			has_mult = false;
 			of_property_for_each_string(dn, "compatible", prop, p) {
 				if (has_mult)
 					string_nocheck(out, ",", str_spec);
 				string_nocheck(out, "\"", str_spec);
-				string(out, p, str_spec);
+				string_spec(out, p, str_spec);
 				string_nocheck(out, "\"", str_spec);
 
 				has_mult = true;
@@ -2097,16 +2114,16 @@ void fwnode_string(struct printbuf *out,
 	str_spec.field_width = -1;
 
 	if (*fmt != 'w')
-		return error_string(out, "(%pf?)", spec);
+		return error_string_spec(out, "(%pf?)", spec);
 
-	if (check_pointer(out, fwnode, spec))
+	if (check_pointer_spec(out, fwnode, spec))
 		return;
 
 	fmt++;
 
 	switch (*fmt) {
 	case 'P':	/* name */
-		string(out, fwnode_get_name(fwnode), str_spec);
+		string_spec(out, fwnode_get_name(fwnode), str_spec);
 		break;
 	case 'f':	/* full_name */
 	default:
@@ -2277,13 +2294,16 @@ static noinline_for_stack
 void pointer(struct printbuf *out, const char *fmt,
 	     void *ptr, struct printf_spec spec)
 {
+	unsigned prev_pos = out->pos;
+
 	switch (*fmt) {
 	case 'S':
 	case 's':
 		ptr = dereference_symbol_descriptor(ptr);
 		fallthrough;
 	case 'B':
-		return symbol_string(out, ptr, spec, fmt);
+		symbol_string(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'R':
 	case 'r':
 		return resource_string(out, ptr, spec, fmt);
@@ -2314,28 +2334,34 @@ void pointer(struct printbuf *out, const char *fmt,
 	case 'E':
 		return escaped_string(out, ptr, spec, fmt);
 	case 'U':
-		return uuid_string(out, ptr, spec, fmt);
+		uuid_string(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'V':
 		return va_format(out, ptr, spec, fmt);
 	case 'K':
 		return restricted_pointer(out, ptr, spec);
 	case 'N':
-		return netdev_bits(out, ptr, spec, fmt);
+		netdev_bits(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case '4':
 		return fourcc_string(out, ptr, spec, fmt);
 	case 'a':
-		return address_val(out, ptr, spec, fmt);
+		address_val(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'd':
-		return dentry_name(out, ptr, spec, fmt);
+		dentry_name(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 't':
 		return time_and_date(out, ptr, spec, fmt);
 	case 'C':
 		return clock(out, ptr, spec, fmt);
 	case 'D':
-		return file_dentry_name(out, ptr, spec, fmt);
+		file_dentry_name(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 #ifdef CONFIG_BLOCK
 	case 'g':
-		return bdev_name(out, ptr, spec, fmt);
+		bdev_name(out, ptr);
+		return do_width_precision(out, prev_pos, spec);
 #endif
 
 	case 'G':
@@ -2355,9 +2381,9 @@ void pointer(struct printbuf *out, const char *fmt,
 	case 'k':
 		switch (fmt[1]) {
 		case 's':
-			return string(out, ptr, spec);
+			return string_spec(out, ptr, spec);
 		default:
-			return error_string(out, "(einval)", spec);
+			return error_string_spec(out, "(einval)", spec);
 		}
 	}
 
@@ -2665,6 +2691,7 @@ void vpr_buf(struct printbuf *out, const char *fmt, va_list args)
 
 	while (*fmt) {
 		const char *old_fmt = fmt;
+		unsigned prev_pos = out->pos;
 		int read = format_decode(fmt, &spec);
 
 		fmt += read;
@@ -2694,7 +2721,8 @@ void vpr_buf(struct printbuf *out, const char *fmt, va_list args)
 			break;
 
 		case FORMAT_TYPE_STR:
-			string(out, va_arg(args, char *), spec);
+			string(out, va_arg(args, char *));
+			do_width_precision(out, prev_pos, spec);
 			break;
 
 		case FORMAT_TYPE_PTR:
@@ -3206,7 +3234,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 		case FORMAT_TYPE_STR: {
 			const char *str_arg = args;
 			args += strlen(str_arg) + 1;
-			string(&out, (char *)str_arg, spec);
+			string_spec(&out, (char *)str_arg, spec);
 			break;
 		}
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 14/28] vsprintf: Refactor resource_string()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (12 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 13/28] vsprintf: Start consolidating printf_spec handling Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 15/28] vsprintf: Refactor fourcc_string() Kent Overstreet
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

Two changes:
 - We're attempting to consolidate printf_spec and format string
   handling in the top level vpr_buf(), this changes resource_string to
   not take printf_spec

 - With the new printbuf helpers there's no need to use a separate stack
   allocated buffer, so this patch deletes it.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 52 +++++++++++++++++++++++---------------------------
 1 file changed, 24 insertions(+), 28 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 6bbbb52bb0..dd4bbb28e7 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1037,7 +1037,7 @@ static const struct printf_spec default_dec_spec = {
 
 static noinline_for_stack
 void resource_string(struct printbuf *out, struct resource *res,
-		     struct printf_spec spec, const char *fmt)
+		     int decode)
 {
 #ifndef IO_RSRC_PRINTK_SIZE
 #define IO_RSRC_PRINTK_SIZE	6
@@ -1076,63 +1076,58 @@ void resource_string(struct printbuf *out, struct resource *res,
 #define FLAG_BUF_SIZE		(2 * sizeof(res->flags))
 #define DECODED_BUF_SIZE	sizeof("[mem - 64bit pref window disabled]")
 #define RAW_BUF_SIZE		sizeof("[mem - flags 0x]")
-	char sym_buf[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
-		     2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
-	struct printbuf sym = PRINTBUF_EXTERN(sym_buf, sizeof(sym_buf));
-	int decode = (fmt[0] == 'R') ? 1 : 0;
 	const struct printf_spec *specp;
 
-	if (check_pointer_spec(out, res, spec))
+	if (check_pointer(out, res))
 		return;
 
-	__pr_char(&sym, '[');
+	__pr_char(out, '[');
 	if (res->flags & IORESOURCE_IO) {
-		string_nocheck(&sym, "io  ", str_spec);
+		string_nocheck(out, "io  ", str_spec);
 		specp = &io_spec;
 	} else if (res->flags & IORESOURCE_MEM) {
-		string_nocheck(&sym, "mem ", str_spec);
+		string_nocheck(out, "mem ", str_spec);
 		specp = &mem_spec;
 	} else if (res->flags & IORESOURCE_IRQ) {
-		string_nocheck(&sym, "irq ", str_spec);
+		string_nocheck(out, "irq ", str_spec);
 		specp = &default_dec_spec;
 	} else if (res->flags & IORESOURCE_DMA) {
-		string_nocheck(&sym, "dma ", str_spec);
+		string_nocheck(out, "dma ", str_spec);
 		specp = &default_dec_spec;
 	} else if (res->flags & IORESOURCE_BUS) {
-		string_nocheck(&sym, "bus ", str_spec);
+		string_nocheck(out, "bus ", str_spec);
 		specp = &bus_spec;
 	} else {
-		string_nocheck(&sym, "??? ", str_spec);
+		string_nocheck(out, "??? ", str_spec);
 		specp = &mem_spec;
 		decode = 0;
 	}
 	if (decode && res->flags & IORESOURCE_UNSET) {
-		string_nocheck(&sym, "size ", str_spec);
-		number(&sym, resource_size(res), *specp);
+		string_nocheck(out, "size ", str_spec);
+		number(out, resource_size(res), *specp);
 	} else {
-		number(&sym, res->start, *specp);
+		number(out, res->start, *specp);
 		if (res->start != res->end) {
-			__pr_char(&sym, '-');
-			number(&sym, res->end, *specp);
+			__pr_char(out, '-');
+			number(out, res->end, *specp);
 		}
 	}
 	if (decode) {
 		if (res->flags & IORESOURCE_MEM_64)
-			string_nocheck(&sym, " 64bit", str_spec);
+			string_nocheck(out, " 64bit", str_spec);
 		if (res->flags & IORESOURCE_PREFETCH)
-			string_nocheck(&sym, " pref", str_spec);
+			string_nocheck(out, " pref", str_spec);
 		if (res->flags & IORESOURCE_WINDOW)
-			string_nocheck(&sym, " window", str_spec);
+			string_nocheck(out, " window", str_spec);
 		if (res->flags & IORESOURCE_DISABLED)
-			string_nocheck(&sym, " disabled", str_spec);
+			string_nocheck(out, " disabled", str_spec);
 	} else {
-		string_nocheck(&sym, " flags ", str_spec);
-		number(&sym, res->flags, default_flag_spec);
+		string_nocheck(out, " flags ", str_spec);
+		number(out, res->flags, default_flag_spec);
 	}
-	__pr_char(&sym, ']');
-	printbuf_nul_terminate(&sym);
+	__pr_char(out, ']');
 
-	string_nocheck(out, sym_buf, spec);
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
@@ -2306,7 +2301,8 @@ void pointer(struct printbuf *out, const char *fmt,
 		return do_width_precision(out, prev_pos, spec);
 	case 'R':
 	case 'r':
-		return resource_string(out, ptr, spec, fmt);
+		resource_string(out, ptr, fmt[0] == 'R');
+		return do_width_precision(out, prev_pos, spec);
 	case 'h':
 		return hex_string(out, ptr, spec, fmt);
 	case 'b':
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 15/28] vsprintf: Refactor fourcc_string()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (13 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 14/28] vsprintf: Refactor resource_string() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 16/28] vsprintf: Refactor ip_addr_string() Kent Overstreet
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

 - We're attempting to consolidate printf_spec and format string
   handling in the top level vpr_buf(), this changes fourcc_string() to
   not take printf_spec

 - With the new printbuf helpers there's no need to use a separate stack
   allocated buffer, so this patch deletes it.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index dd4bbb28e7..3d17ddad31 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1702,17 +1702,15 @@ void netdev_bits(struct printbuf *out, const void *addr,
 
 static noinline_for_stack
 void fourcc_string(struct printbuf *out, const u32 *fourcc,
-		   struct printf_spec spec, const char *fmt)
+		   const char *fmt)
 {
-	char output_buf[sizeof("0123 little-endian (0x01234567)")];
-	struct printbuf output = PRINTBUF_EXTERN(output_buf, sizeof(output_buf));
 	unsigned int i;
 	u32 val;
 
 	if (fmt[1] != 'c' || fmt[2] != 'c')
-		return error_string_spec(out, "(%p4?)", spec);
+		return error_string(out, "(%p4?)");
 
-	if (check_pointer_spec(out, fourcc, spec))
+	if (check_pointer(out, fourcc))
 		return;
 
 	val = *fourcc & ~BIT(31);
@@ -1721,18 +1719,17 @@ void fourcc_string(struct printbuf *out, const u32 *fourcc,
 		unsigned char c = val >> (i * 8);
 
 		/* Print non-control ASCII characters as-is, dot otherwise */
-		__pr_char(&output, isascii(c) && isprint(c) ? c : '.');
+		__pr_char(out, isascii(c) && isprint(c) ? c : '.');
 	}
 
-	pr_str(&output, *fourcc & BIT(31) ? " big-endian" : " little-endian");
+	pr_str(out, *fourcc & BIT(31) ? " big-endian" : " little-endian");
 
-	__pr_char(&output, ' ');
-	__pr_char(&output, '(');
-	special_hex_number(&output, *fourcc, sizeof(u32));
-	__pr_char(&output, ')');
-	printbuf_nul_terminate(&output);
+	__pr_char(out, ' ');
+	__pr_char(out, '(');
+	special_hex_number(out, *fourcc, sizeof(u32));
+	__pr_char(out, ')');
 
-	string_spec(out, output_buf, spec);
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
@@ -2340,7 +2337,8 @@ void pointer(struct printbuf *out, const char *fmt,
 		netdev_bits(out, ptr, fmt);
 		return do_width_precision(out, prev_pos, spec);
 	case '4':
-		return fourcc_string(out, ptr, spec, fmt);
+		fourcc_string(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'a':
 		address_val(out, ptr, fmt);
 		return do_width_precision(out, prev_pos, spec);
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 16/28] vsprintf: Refactor ip_addr_string()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (14 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 15/28] vsprintf: Refactor fourcc_string() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 17/28] vsprintf: Refactor mac_address_string() Kent Overstreet
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

 - We're attempting to consolidate printf_spec and format string
   handling in the top level vpr_buf(), this changes ip_addr_string() to
   not take printf_spec

 - With the new printbuf helpers there's no need to use a separate stack
   allocated buffer, so this patch deletes it.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 116 ++++++++++++++++---------------------------------
 1 file changed, 37 insertions(+), 79 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3d17ddad31..62a221522c 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1273,13 +1273,13 @@ void mac_address_string(struct printbuf *out, u8 *addr,
 }
 
 static noinline_for_stack
-void ip4_string(struct printbuf *out,
-		const u8 *addr, const char *fmt)
+void ip4_string(struct printbuf *out, const u8 *addr, const char *fmt)
 {
-	int i;
-	bool leading_zeros = (fmt[0] == 'i');
-	int index;
-	int step;
+	struct printf_spec spec = default_dec_spec;
+	int i, index, step;
+
+	if (fmt[0] == 'i')
+		spec.precision = 3;
 
 	switch (fmt[2]) {
 	case 'h':
@@ -1303,19 +1303,9 @@ void ip4_string(struct printbuf *out,
 		break;
 	}
 	for (i = 0; i < 4; i++) {
-		char temp[4] __aligned(2);	/* hold each IP quad in reverse order */
-		int digits = put_dec_trunc8(temp, addr[index]) - temp;
-		if (leading_zeros) {
-			if (digits < 3)
-				__pr_char(out, '0');
-			if (digits < 2)
-				__pr_char(out, '0');
-		}
-		/* reverse the digits in the quad */
-		while (digits--)
-			__pr_char(out, temp[digits]);
-		if (i < 3)
+		if (i)
 			__pr_char(out, '.');
+		number(out, addr[index], spec);
 		index += step;
 	}
 }
@@ -1398,8 +1388,6 @@ void ip6_compressed_string(struct printbuf *out, const char *addr)
 			__pr_char(out, ':');
 		ip4_string(out, &in6.s6_addr[12], "I4");
 	}
-
-	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
@@ -1419,41 +1407,20 @@ void ip6_string(struct printbuf *out, const char *addr, const char *fmt)
 
 static noinline_for_stack
 void ip6_addr_string(struct printbuf *out, const u8 *addr,
-		     struct printf_spec spec, const char *fmt)
+		     const char *fmt)
 {
-	char ip6_addr_buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
-	struct printbuf ip6_addr = PRINTBUF_EXTERN(ip6_addr_buf, sizeof(ip6_addr_buf));
-
 	if (fmt[0] == 'I' && fmt[2] == 'c')
-		ip6_compressed_string(&ip6_addr, addr);
+		ip6_compressed_string(out, addr);
 	else
-		ip6_string(&ip6_addr, addr, fmt);
-
-	string_nocheck(out, ip6_addr_buf, spec);
-}
-
-static noinline_for_stack
-void ip4_addr_string(struct printbuf *out, const u8 *addr,
-		     struct printf_spec spec, const char *fmt)
-{
-	char ip4_addr_buf[sizeof("255.255.255.255")];
-	struct printbuf ip4_addr = PRINTBUF_EXTERN(ip4_addr_buf, sizeof(ip4_addr_buf));
-
-	ip4_string(&ip4_addr, addr, fmt);
-
-	string_nocheck(out, ip4_addr_buf, spec);
+		ip6_string(out, addr, fmt);
 }
 
 static noinline_for_stack
 void ip6_addr_string_sa(struct printbuf *out,
 			const struct sockaddr_in6 *sa,
-			struct printf_spec spec, const char *fmt)
+			const char *fmt)
 {
 	bool have_p = false, have_s = false, have_f = false, have_c = false;
-	char ip6_addr_buf[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
-		sizeof(":12345") + sizeof("/123456789") +
-		sizeof("%1234567890")];
-	struct printbuf ip6_addr = PRINTBUF_EXTERN(ip6_addr_buf, sizeof(ip6_addr_buf));
 	const u8 *addr = (const u8 *) &sa->sin6_addr;
 	char fmt6[2] = { fmt[0], '6' };
 
@@ -1476,42 +1443,35 @@ void ip6_addr_string_sa(struct printbuf *out,
 	}
 
 	if (have_p || have_s || have_f)
-		__pr_char(&ip6_addr, '[');
+		__pr_char(out, '[');
 
 	if (fmt6[0] == 'I' && have_c)
-		ip6_compressed_string(&ip6_addr, addr);
+		ip6_compressed_string(out, addr);
 	else
-		ip6_string(&ip6_addr, addr, fmt6);
+		ip6_string(out, addr, fmt6);
 
 	if (have_p || have_s || have_f)
-		__pr_char(&ip6_addr, ']');
+		__pr_char(out, ']');
 
 	if (have_p) {
-		__pr_char(&ip6_addr, ':');
-		number(&ip6_addr, ntohs(sa->sin6_port), spec);
+		__pr_char(out, ':');
+		pr_u64(out, ntohs(sa->sin6_port));
 	}
 	if (have_f) {
-		__pr_char(&ip6_addr, '/');
-		number(&ip6_addr, ntohl(sa->sin6_flowinfo &
-					IPV6_FLOWINFO_MASK), spec);
+		__pr_char(out, '/');
+		pr_u64(out, ntohl(sa->sin6_flowinfo & IPV6_FLOWINFO_MASK));
 	}
 	if (have_s) {
-		__pr_char(&ip6_addr, '%');
-		number(&ip6_addr, sa->sin6_scope_id, spec);
+		__pr_char(out, '%');
+		pr_u64(out, sa->sin6_scope_id);
 	}
-	printbuf_nul_terminate(&ip6_addr);
-
-	string_nocheck(out, ip6_addr_buf, spec);
 }
 
 static noinline_for_stack
-void ip4_addr_string_sa(struct printbuf *out,
-			const struct sockaddr_in *sa,
-			struct printf_spec spec, const char *fmt)
+void ip4_addr_string_sa(struct printbuf *out, const struct sockaddr_in *sa,
+			const char *fmt)
 {
 	bool have_p = false;
-	char ip4_addr_buf[sizeof("255.255.255.255") + sizeof(":12345")];
-	struct printbuf ip4_addr = PRINTBUF_EXTERN(ip4_addr_buf, sizeof(ip4_addr_buf));
 	const u8 *addr = (const u8 *) &sa->sin_addr.s_addr;
 	char fmt4[3] = { fmt[0], '4', 0 };
 
@@ -1530,30 +1490,27 @@ void ip4_addr_string_sa(struct printbuf *out,
 		}
 	}
 
-	ip4_string(&ip4_addr, addr, fmt4);
+	ip4_string(out, addr, fmt4);
 	if (have_p) {
-		__pr_char(&ip4_addr, ':');
-		number(&ip4_addr, ntohs(sa->sin_port), spec);
+		__pr_char(out, ':');
+		pr_u64(out, ntohs(sa->sin_port));
 	}
-	printbuf_nul_terminate(&ip4_addr);
-
-	string_nocheck(out, ip4_addr_buf, spec);
 }
 
 static noinline_for_stack
 void ip_addr_string(struct printbuf *out, const void *ptr,
-		    struct printf_spec spec, const char *fmt)
+		    const char *fmt)
 {
 	char *err_fmt_msg;
 
-	if (check_pointer_spec(out, ptr, spec))
+	if (check_pointer(out, ptr))
 		return;
 
 	switch (fmt[1]) {
 	case '6':
-		return ip6_addr_string(out, ptr, spec, fmt);
+		return ip6_addr_string(out, ptr, fmt);
 	case '4':
-		return ip4_addr_string(out, ptr, spec, fmt);
+		return ip4_string(out, ptr, fmt);
 	case 'S': {
 		const union {
 			struct sockaddr		raw;
@@ -1563,16 +1520,16 @@ void ip_addr_string(struct printbuf *out, const void *ptr,
 
 		switch (sa->raw.sa_family) {
 		case AF_INET:
-			return ip4_addr_string_sa(out, &sa->v4, spec, fmt);
+			return ip4_addr_string_sa(out, &sa->v4, fmt);
 		case AF_INET6:
-			return ip6_addr_string_sa(out, &sa->v6, spec, fmt);
+			return ip6_addr_string_sa(out, &sa->v6, fmt);
 		default:
-			return error_string_spec(out, "(einval)", spec);
+			return error_string(out, "(einval)");
 		}}
 	}
 
 	err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)";
-	return error_string_spec(out, err_fmt_msg, spec);
+	error_string(out, err_fmt_msg);
 }
 
 static noinline_for_stack
@@ -2323,7 +2280,8 @@ void pointer(struct printbuf *out, const char *fmt,
 					 * 4:	001.002.003.004
 					 * 6:   000102...0f
 					 */
-		return ip_addr_string(out, ptr, spec, fmt);
+		ip_addr_string(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'E':
 		return escaped_string(out, ptr, spec, fmt);
 	case 'U':
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 17/28] vsprintf: Refactor mac_address_string()
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (15 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 16/28] vsprintf: Refactor ip_addr_string() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 18/28] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

 - We're attempting to consolidate printf_spec and format string
   handling in the top level vpr_buf(), this changes mac_address_string() to
   not take printf_spec

 - With the new printbuf helpers there's no need to use a separate stack
   allocated buffer, so this patch deletes it.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 62a221522c..76947e0d30 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1233,15 +1233,13 @@ void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
 
 static noinline_for_stack
 void mac_address_string(struct printbuf *out, u8 *addr,
-			struct printf_spec spec, const char *fmt)
+			const char *fmt)
 {
-	char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")];
-	char *p = mac_addr;
 	int i;
 	char separator;
 	bool reversed = false;
 
-	if (check_pointer_spec(out, addr, spec))
+	if (check_pointer(out, addr))
 		return;
 
 	switch (fmt[1]) {
@@ -1260,16 +1258,13 @@ void mac_address_string(struct printbuf *out, u8 *addr,
 
 	for (i = 0; i < 6; i++) {
 		if (reversed)
-			p = hex_byte_pack(p, addr[5 - i]);
+			pr_hex_byte(out, addr[5 - i]);
 		else
-			p = hex_byte_pack(p, addr[i]);
+			pr_hex_byte(out, addr[i]);
 
 		if (fmt[0] == 'M' && i != 5)
-			*p++ = separator;
+			pr_char(out, separator);
 	}
-	*p = '\0';
-
-	string_nocheck(out, mac_addr, spec);
 }
 
 static noinline_for_stack
@@ -2270,7 +2265,8 @@ void pointer(struct printbuf *out, const char *fmt,
 	case 'm':			/* Contiguous: 000102030405 */
 					/* [mM]F (FDDI) */
 					/* [mM]R (Reverse order; Bluetooth) */
-		return mac_address_string(out, ptr, spec, fmt);
+		mac_address_string(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'I':			/* Formatted IP supported
 					 * 4:	1.2.3.4
 					 * 6:	0001:0203:...:0708
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 18/28] vsprintf: time_and_date() no longer takes printf_spec
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (16 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 17/28] vsprintf: Refactor mac_address_string() Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 19/28] vsprintf: flags_string() " Kent Overstreet
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

We're attempting to consolidate printf_spec and format string handling
in the top level vpr_buf(), this changes time_and_date() to not
take printf_spec.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 76947e0d30..837a3f967e 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1735,14 +1735,14 @@ void time_str(struct printbuf *out, const struct rtc_time *tm, bool r)
 
 static noinline_for_stack
 void rtc_str(struct printbuf *out, const struct rtc_time *tm,
-	     struct printf_spec spec, const char *fmt)
+	     const char *fmt)
 {
 	bool have_t = true, have_d = true;
 	bool raw = false, iso8601_separator = true;
 	bool found = true;
 	int count = 2;
 
-	if (check_pointer_spec(out, tm, spec))
+	if (check_pointer(out, tm))
 		return;
 
 	switch (fmt[count]) {
@@ -1780,7 +1780,7 @@ void rtc_str(struct printbuf *out, const struct rtc_time *tm,
 
 static noinline_for_stack
 void time64_str(struct printbuf *out, const time64_t time,
-		struct printf_spec spec, const char *fmt)
+		const char *fmt)
 {
 	struct rtc_time rtc_time;
 	struct tm tm;
@@ -1798,21 +1798,20 @@ void time64_str(struct printbuf *out, const time64_t time,
 
 	rtc_time.tm_isdst = 0;
 
-	rtc_str(out, &rtc_time, spec, fmt);
+	rtc_str(out, &rtc_time, fmt);
 }
 
 static noinline_for_stack
-void time_and_date(struct printbuf *out,
-		   void *ptr, struct printf_spec spec,
+void time_and_date(struct printbuf *out, void *ptr,
 		   const char *fmt)
 {
 	switch (fmt[1]) {
 	case 'R':
-		return rtc_str(out, (const struct rtc_time *)ptr, spec, fmt);
+		return rtc_str(out, (const struct rtc_time *)ptr, fmt);
 	case 'T':
-		return time64_str(out, *(const time64_t *)ptr, spec, fmt);
+		return time64_str(out, *(const time64_t *)ptr, fmt);
 	default:
-		return error_string_spec(out, "(%pt?)", spec);
+		return error_string(out, "(%pt?)");
 	}
 }
 
@@ -2300,7 +2299,8 @@ void pointer(struct printbuf *out, const char *fmt,
 		dentry_name(out, ptr, fmt);
 		return do_width_precision(out, prev_pos, spec);
 	case 't':
-		return time_and_date(out, ptr, spec, fmt);
+		time_and_date(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'C':
 		return clock(out, ptr, spec, fmt);
 	case 'D':
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 19/28] vsprintf: flags_string() no longer takes printf_spec
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (17 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 18/28] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 20/28] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

We're attempting to consolidate printf_spec and format string handling
in the top level vpr_buf(), this changes time_and_date() to not
take printf_spec.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 837a3f967e..971cf17d33 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1916,12 +1916,12 @@ void format_page_flags(struct printbuf *out, unsigned long flags)
 
 static noinline_for_stack
 void flags_string(struct printbuf *out, void *flags_ptr,
-		  struct printf_spec spec, const char *fmt)
+		  const char *fmt)
 {
 	unsigned long flags;
 	const struct trace_print_flags *names;
 
-	if (check_pointer_spec(out, flags_ptr, spec))
+	if (check_pointer(out, flags_ptr))
 		return;
 
 	switch (fmt[1]) {
@@ -1936,7 +1936,7 @@ void flags_string(struct printbuf *out, void *flags_ptr,
 		names = gfpflag_names;
 		break;
 	default:
-		return error_string_spec(out, "(%pG?)", spec);
+		return error_string(out, "(%pG?)");
 	}
 
 	return format_flags(out, flags, names);
@@ -2313,7 +2313,8 @@ void pointer(struct printbuf *out, const char *fmt,
 #endif
 
 	case 'G':
-		return flags_string(out, ptr, spec, fmt);
+		flags_string(out, ptr, fmt);
+		return do_width_precision(out, prev_pos, spec);
 	case 'O':
 		return device_node_string(out, ptr, spec, fmt + 1);
 	case 'f':
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 20/28] vsprintf: Refactor device_node_string, fwnode_string
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (18 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 19/28] vsprintf: flags_string() " Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 21/28] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
                   ` (8 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

 - eliminate on-stack buffer in device_node_string
 - eliminate unnecessary uses of printf_spec, lift format string
   precision/field width to pointer()

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 75 ++++++++++++++++++++------------------------------
 1 file changed, 30 insertions(+), 45 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 971cf17d33..ef8b06a25a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1962,25 +1962,20 @@ void fwnode_full_name_string(struct printbuf *out,
 
 static noinline_for_stack
 void device_node_string(struct printbuf *out, struct device_node *dn,
-			struct printf_spec spec, const char *fmt)
+			const char *fmt)
 {
-	char tbuf[sizeof("xxxx") + 1];
 	const char *p;
 	int ret;
-	unsigned start = out->pos;
 	struct property *prop;
 	bool has_mult, pass;
 
-	struct printf_spec str_spec = spec;
-	str_spec.field_width = -1;
-
 	if (fmt[0] != 'F')
-		return error_string_spec(out, "(%pO?)", spec);
+		return error_string(out, "(%pO?)");
 
 	if (!IS_ENABLED(CONFIG_OF))
-		return error_string_spec(out, "(%pOF?)", spec);
+		return error_string(out, "(%pOF?)");
 
-	if (check_pointer_spec(out, dn, spec))
+	if (check_pointer(out, dn))
 		return;
 
 	/* simple case without anything any more format specifiers */
@@ -1989,51 +1984,48 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 		fmt = "f";
 
 	for (pass = false; strspn(fmt,"fnpPFcC"); fmt++, pass = true) {
-		int precision;
 		if (pass)
-			__pr_char(out, ':');
+			pr_char(out, ':');
 
 		switch (*fmt) {
 		case 'f':	/* full_name */
 			fwnode_full_name_string(out, of_fwnode_handle(dn));
 			break;
-		case 'n':	/* name */
-			p = fwnode_get_name(of_fwnode_handle(dn));
-			precision = str_spec.precision;
-			str_spec.precision = strchrnul(p, '@') - p;
-			string_spec(out, p, str_spec);
-			str_spec.precision = precision;
+		case 'n': {	/* name */
+			const char *name = fwnode_get_name(of_fwnode_handle(dn));
+			unsigned len = strchrnul(name, '@') - name;
+
+			pr_bytes(out, name, len);
 			break;
+		}
 		case 'p':	/* phandle */
-			pr_u64(out, (unsigned int)dn->phandle);
+			pr_u64(out, dn->phandle);
 			break;
 		case 'P':	/* path-spec */
 			p = fwnode_get_name(of_fwnode_handle(dn));
 			if (!p[1])
 				p = "/";
-			string_spec(out, p, str_spec);
+			string(out, p);
 			break;
 		case 'F':	/* flags */
-			tbuf[0] = of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-';
-			tbuf[1] = of_node_check_flag(dn, OF_DETACHED) ? 'd' : '-';
-			tbuf[2] = of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-';
-			tbuf[3] = of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-';
-			tbuf[4] = 0;
-			string_nocheck(out, tbuf, str_spec);
+			pr_char(out, of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-');
+			pr_char(out, of_node_check_flag(dn, OF_DETACHED) ? 'd' : '-');
+			pr_char(out, of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-');
+			pr_char(out, of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-');
 			break;
 		case 'c':	/* major compatible string_spec */
 			ret = of_property_read_string(dn, "compatible", &p);
 			if (!ret)
-				string_spec(out, p, str_spec);
+				string(out, p);
 			break;
 		case 'C':	/* full compatible string_spec */
 			has_mult = false;
 			of_property_for_each_string(dn, "compatible", prop, p) {
 				if (has_mult)
-					string_nocheck(out, ",", str_spec);
-				string_nocheck(out, "\"", str_spec);
-				string_spec(out, p, str_spec);
-				string_nocheck(out, "\"", str_spec);
+					pr_char(out, ',');
+				pr_char(out, '\"');
+				string(out, p);
+				pr_char(out, '\"');
 
 				has_mult = true;
 			}
@@ -2042,39 +2034,30 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 			break;
 		}
 	}
-
-	widen_string(out, out->pos - start, spec);
 }
 
 static noinline_for_stack
 void fwnode_string(struct printbuf *out,
 		   struct fwnode_handle *fwnode,
-		   struct printf_spec spec, const char *fmt)
+		   const char *fmt)
 {
-	struct printf_spec str_spec = spec;
-	unsigned start = out->pos;
-
-	str_spec.field_width = -1;
-
 	if (*fmt != 'w')
-		return error_string_spec(out, "(%pf?)", spec);
+		return error_string(out, "(%pf?)");
 
-	if (check_pointer_spec(out, fwnode, spec))
+	if (check_pointer(out, fwnode))
 		return;
 
 	fmt++;
 
 	switch (*fmt) {
 	case 'P':	/* name */
-		string_spec(out, fwnode_get_name(fwnode), str_spec);
+		string(out, fwnode_get_name(fwnode));
 		break;
 	case 'f':	/* full_name */
 	default:
 		fwnode_full_name_string(out, fwnode);
 		break;
 	}
-
-	widen_string(out, out->pos - start, spec);
 }
 
 /* Disable pointer hashing if requested */
@@ -2316,9 +2299,11 @@ void pointer(struct printbuf *out, const char *fmt,
 		flags_string(out, ptr, fmt);
 		return do_width_precision(out, prev_pos, spec);
 	case 'O':
-		return device_node_string(out, ptr, spec, fmt + 1);
+		device_node_string(out, ptr, fmt + 1);
+		return do_width_precision(out, prev_pos, spec);
 	case 'f':
-		return fwnode_string(out, ptr, spec, fmt + 1);
+		fwnode_string(out, ptr, fmt + 1);
+		return do_width_precision(out, prev_pos, spec);
 	case 'x':
 		return pointer_string(out, ptr, spec);
 	case 'e':
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 21/28] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (19 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 20/28] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This patch cleans up printf_spec handling: these functions only use
spec.field_width and they do not interpret it in the normal way -
instead it's a number of bits/bytes passed in to print, so these
functions are changed to take that parameter directly.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 lib/vsprintf.c | 47 +++++++++++++++++++++++++----------------------
 1 file changed, 25 insertions(+), 22 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index ef8b06a25a..be2fa6053c 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1132,17 +1132,22 @@ void resource_string(struct printbuf *out, struct resource *res,
 
 static noinline_for_stack
 void hex_string(struct printbuf *out, const u8 *addr,
-		struct printf_spec spec, const char *fmt)
+		int len, const char *fmt)
 {
-	int len = 1;		/* if we pass '%ph[CDN]', field width remains
-				   negative value, fallback to the default */
 	char separator;
 
-	if (spec.field_width == 0)
-		/* nothing to print */
+	/* nothing to print */
+	if (len == 0)
 		return;
 
-	if (check_pointer_spec(out, addr, spec))
+	/* if we pass '%ph[CDN]', field width remains
+	   negative value, fallback to the default */
+	if (len < 0)
+		len = 1;
+
+	len = min(len, 64);
+
+	if (check_pointer(out, addr))
 		return;
 
 	switch (fmt[1]) {
@@ -1160,26 +1165,21 @@ void hex_string(struct printbuf *out, const u8 *addr,
 		break;
 	}
 
-	if (spec.field_width > 0)
-		len = min_t(int, spec.field_width, 64);
-
 	pr_hex_bytes(out, addr, len, separator);
 }
 
 static noinline_for_stack
-void bitmap_string(struct printbuf *out, unsigned long *bitmap,
-		   struct printf_spec spec, const char *fmt)
+void bitmap_string(struct printbuf *out, unsigned long *bitmap, int nr_bits)
 {
+	struct printf_spec spec = { .flags = SMALL | ZEROPAD, .base = 16 };
 	const int CHUNKSZ = 32;
-	int nr_bits = max_t(int, spec.field_width, 0);
 	int i, chunksz;
 	bool first = true;
 
-	if (check_pointer_spec(out, bitmap, spec))
-		return;
+	nr_bits = max(nr_bits, 0);
 
-	/* reused to print numbers */
-	spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 };
+	if (check_pointer(out, bitmap))
+		return;
 
 	chunksz = nr_bits & (CHUNKSZ - 1);
 	if (chunksz == 0)
@@ -1208,13 +1208,14 @@ void bitmap_string(struct printbuf *out, unsigned long *bitmap,
 
 static noinline_for_stack
 void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
-			struct printf_spec spec, const char *fmt)
+			int nr_bits)
 {
-	int nr_bits = max_t(int, spec.field_width, 0);
 	bool first = true;
 	int rbot, rtop;
 
-	if (check_pointer_spec(out, bitmap, spec))
+	nr_bits = max(nr_bits, 0);
+
+	if (check_pointer(out, bitmap))
 		return ;
 
 	for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) {
@@ -2235,13 +2236,15 @@ void pointer(struct printbuf *out, const char *fmt,
 		resource_string(out, ptr, fmt[0] == 'R');
 		return do_width_precision(out, prev_pos, spec);
 	case 'h':
-		return hex_string(out, ptr, spec, fmt);
+		/* Uses field_width but _not_ as field size */
+		return hex_string(out, ptr, spec.field_width, fmt);
 	case 'b':
+		/* Uses field_width but _not_ as field size */
 		switch (fmt[1]) {
 		case 'l':
-			return bitmap_list_string(out, ptr, spec, fmt);
+			return bitmap_list_string(out, ptr, spec.field_width);
 		default:
-			return bitmap_string(out, ptr, spec, fmt);
+			return bitmap_string(out, ptr, spec.field_width);
 		}
 	case 'M':			/* Colon separated: 00:01:02:03:04:05 */
 	case 'm':			/* Contiguous: 000102030405 */
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (20 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 21/28] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 20:04   ` Andy Shevchenko
  2022-05-19 17:24 ` [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf Kent Overstreet
                   ` (6 subsequent siblings)
  28 siblings, 1 reply; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

seq_buf is being deprecated, this converts to printbuf which is similar
but heap allocates the string buffer.

This means we have to consider memory allocation context & failure: Here
we're in device initialization so GFP_KERNEL should be fine, and also as
we're in device initialization returning -ENOMEM is fine.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 drivers/input/joystick/analog.c | 37 ++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index 3088c5b829..72e1e30d19 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -19,7 +19,7 @@
 #include <linux/input.h>
 #include <linux/gameport.h>
 #include <linux/jiffies.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/timex.h>
 #include <linux/timekeeping.h>
 
@@ -337,26 +337,32 @@ static void analog_calibrate_timer(struct analog_port *port)
  * analog_name() constructs a name for an analog joystick.
  */
 
-static void analog_name(struct analog *analog)
+static int analog_name(struct analog *analog)
 {
-	struct seq_buf s;
+	struct printbuf buf = PRINTBUF;
+	int ret = 0;
 
-	seq_buf_init(&s, analog->name, sizeof(analog->name));
-	seq_buf_printf(&s, "Analog %d-axis %d-button",
-		 hweight8(analog->mask & ANALOG_AXES_STD),
-		 hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
-		 hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
+	pr_buf(&buf, "Analog %d-axis %d-button",
+	       hweight8(analog->mask & ANALOG_AXES_STD),
+	       hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
+	       hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
 
 	if (analog->mask & ANALOG_HATS_ALL)
-		seq_buf_printf(&s, " %d-hat",
-			       hweight16(analog->mask & ANALOG_HATS_ALL));
+		pr_buf(&buf, " %d-hat",
+		       hweight16(analog->mask & ANALOG_HATS_ALL));
 
 	if (analog->mask & ANALOG_HAT_FCS)
-		seq_buf_printf(&s, " FCS");
+		pr_buf(&buf, " FCS");
 	if (analog->mask & ANALOG_ANY_CHF)
-		seq_buf_printf(&s, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
+		pr_buf(&buf, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
 
-	seq_buf_printf(&s, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
+	pr_buf(&buf, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
+
+	ret = buf.allocation_failure ? -ENOMEM : 0;
+	if (!ret)
+		strlcpy(analog->name, buf.buf, sizeof(analog->name));
+	printbuf_exit(&buf);
+	return ret;
 }
 
 /*
@@ -369,7 +375,10 @@ static int analog_init_device(struct analog_port *port, struct analog *analog, i
 	int i, j, t, v, w, x, y, z;
 	int error;
 
-	analog_name(analog);
+	error = analog_name(analog);
+	if (error)
+		return error;
+
 	snprintf(analog->phys, sizeof(analog->phys),
 		 "%s/input%d", port->gameport->phys, index);
 	analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (21 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-20  7:59   ` Michal Hocko
  2022-05-19 17:24 ` [PATCH v2 24/28] clk: tegra: bpmp: " Kent Overstreet
                   ` (5 subsequent siblings)
  28 siblings, 1 reply; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This converts memory_stat_format() from seq_buf to printbuf. Printbuf is
simalar to seq_buf except that it heap allocates the string buffer:
here, we were already heap allocating the buffer with kmalloc() so the
conversion is trivial.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 mm/memcontrol.c | 68 ++++++++++++++++++++++++-------------------------
 1 file changed, 33 insertions(+), 35 deletions(-)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 36e9f38c91..4cb0b7bc1c 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -61,7 +61,7 @@
 #include <linux/file.h>
 #include <linux/tracehook.h>
 #include <linux/psi.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include "internal.h"
 #include <net/sock.h>
 #include <net/ip.h>
@@ -1436,13 +1436,9 @@ static inline unsigned long memcg_page_state_output(struct mem_cgroup *memcg,
 
 static char *memory_stat_format(struct mem_cgroup *memcg)
 {
-	struct seq_buf s;
+	struct printbuf buf = PRINTBUF;
 	int i;
 
-	seq_buf_init(&s, kmalloc(PAGE_SIZE, GFP_KERNEL), PAGE_SIZE);
-	if (!s.buffer)
-		return NULL;
-
 	/*
 	 * Provide statistics on the state of the memory subsystem as
 	 * well as cumulative event counters that show past behavior.
@@ -1459,49 +1455,51 @@ static char *memory_stat_format(struct mem_cgroup *memcg)
 		u64 size;
 
 		size = memcg_page_state_output(memcg, memory_stats[i].idx);
-		seq_buf_printf(&s, "%s %llu\n", memory_stats[i].name, size);
+		pr_buf(&buf, "%s %llu\n", memory_stats[i].name, size);
 
 		if (unlikely(memory_stats[i].idx == NR_SLAB_UNRECLAIMABLE_B)) {
 			size += memcg_page_state_output(memcg,
 							NR_SLAB_RECLAIMABLE_B);
-			seq_buf_printf(&s, "slab %llu\n", size);
+			pr_buf(&buf, "slab %llu\n", size);
 		}
 	}
 
 	/* Accumulated memory events */
 
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGFAULT),
-		       memcg_events(memcg, PGFAULT));
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGMAJFAULT),
-		       memcg_events(memcg, PGMAJFAULT));
-	seq_buf_printf(&s, "%s %lu\n",  vm_event_name(PGREFILL),
-		       memcg_events(memcg, PGREFILL));
-	seq_buf_printf(&s, "pgscan %lu\n",
-		       memcg_events(memcg, PGSCAN_KSWAPD) +
-		       memcg_events(memcg, PGSCAN_DIRECT));
-	seq_buf_printf(&s, "pgsteal %lu\n",
-		       memcg_events(memcg, PGSTEAL_KSWAPD) +
-		       memcg_events(memcg, PGSTEAL_DIRECT));
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGACTIVATE),
-		       memcg_events(memcg, PGACTIVATE));
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGDEACTIVATE),
-		       memcg_events(memcg, PGDEACTIVATE));
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGLAZYFREE),
-		       memcg_events(memcg, PGLAZYFREE));
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(PGLAZYFREED),
-		       memcg_events(memcg, PGLAZYFREED));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(PGFAULT),
+	       memcg_events(memcg, PGFAULT));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(PGMAJFAULT),
+	       memcg_events(memcg, PGMAJFAULT));
+	pr_buf(&buf, "%s %lu\n",  vm_event_name(PGREFILL),
+	       memcg_events(memcg, PGREFILL));
+	pr_buf(&buf, "pgscan %lu\n",
+	       memcg_events(memcg, PGSCAN_KSWAPD) +
+	       memcg_events(memcg, PGSCAN_DIRECT));
+	pr_buf(&buf, "pgsteal %lu\n",
+	       memcg_events(memcg, PGSTEAL_KSWAPD) +
+	       memcg_events(memcg, PGSTEAL_DIRECT));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(PGACTIVATE),
+	       memcg_events(memcg, PGACTIVATE));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(PGDEACTIVATE),
+	       memcg_events(memcg, PGDEACTIVATE));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(PGLAZYFREE),
+	       memcg_events(memcg, PGLAZYFREE));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(PGLAZYFREED),
+	       memcg_events(memcg, PGLAZYFREED));
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(THP_FAULT_ALLOC),
-		       memcg_events(memcg, THP_FAULT_ALLOC));
-	seq_buf_printf(&s, "%s %lu\n", vm_event_name(THP_COLLAPSE_ALLOC),
-		       memcg_events(memcg, THP_COLLAPSE_ALLOC));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(THP_FAULT_ALLOC),
+	       memcg_events(memcg, THP_FAULT_ALLOC));
+	pr_buf(&buf, "%s %lu\n", vm_event_name(THP_COLLAPSE_ALLOC),
+	       memcg_events(memcg, THP_COLLAPSE_ALLOC));
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-	/* The above should easily fit into one page */
-	WARN_ON_ONCE(seq_buf_has_overflowed(&s));
+	if (buf.allocation_failure) {
+		printbuf_exit(&buf);
+		return NULL;
+	}
 
-	return s.buffer;
+	return buf.buf;
 }
 
 #define K(x) ((x) << (PAGE_SHIFT-10))
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 24/28] clk: tegra: bpmp: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (22 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 25/28] tools/testing/nvdimm: " Kent Overstreet
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy

This converts from seq_buf to printbuf, which is similar but heap
allocates the string buffer.

Previously in this code the string buffer was allocated on the stack;
this means we've added a new potential memory allocation failure. This
is fine though since it's only for a dev_printk() message.

Memory allocation context: printbuf doesn't take gfp flags, instead we
prefer the new memalloc_no*_(save|restore) interfaces to be used. Here
the surrounding code is already allocating with GFP_KERNEL, so
everything is fine.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 drivers/clk/tegra/clk-bpmp.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
index 6ecf18f71c..77a8c47806 100644
--- a/drivers/clk/tegra/clk-bpmp.c
+++ b/drivers/clk/tegra/clk-bpmp.c
@@ -5,7 +5,7 @@
 
 #include <linux/clk-provider.h>
 #include <linux/device.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/slab.h>
 
 #include <soc/tegra/bpmp.h>
@@ -360,39 +360,38 @@ static void tegra_bpmp_clk_info_dump(struct tegra_bpmp *bpmp,
 				     const struct tegra_bpmp_clk_info *info)
 {
 	const char *prefix = "";
-	struct seq_buf buf;
+	struct printbuf buf = PRINTBUF;
 	unsigned int i;
-	char flags[64];
-
-	seq_buf_init(&buf, flags, sizeof(flags));
 
 	if (info->flags)
-		seq_buf_printf(&buf, "(");
+		pr_buf(&buf, "(");
 
 	if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
-		seq_buf_printf(&buf, "%smux", prefix);
+		pr_buf(&buf, "%smux", prefix);
 		prefix = ", ";
 	}
 
 	if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
-		seq_buf_printf(&buf, "%sfixed", prefix);
+		pr_buf(&buf, "%sfixed", prefix);
 		prefix = ", ";
 	}
 
 	if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
-		seq_buf_printf(&buf, "%sroot", prefix);
+		pr_buf(&buf, "%sroot", prefix);
 		prefix = ", ";
 	}
 
 	if (info->flags)
-		seq_buf_printf(&buf, ")");
+		pr_buf(&buf, ")");
 
 	dev_printk(level, bpmp->dev, "%03u: %s\n", info->id, info->name);
-	dev_printk(level, bpmp->dev, "  flags: %lx %s\n", info->flags, flags);
+	dev_printk(level, bpmp->dev, "  flags: %lx %s\n", info->flags, printbuf_str(&buf));
 	dev_printk(level, bpmp->dev, "  parents: %u\n", info->num_parents);
 
 	for (i = 0; i < info->num_parents; i++)
 		dev_printk(level, bpmp->dev, "    %03u\n", info->parents[i]);
+
+	printbuf_exit(&buf);
 }
 
 static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 25/28] tools/testing/nvdimm: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (23 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 24/28] clk: tegra: bpmp: " Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24   ` Kent Overstreet
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy, Dan Williams,
	Dave Hansen, nvdimm

This converts from seq_buf to printbuf. Here we're using printbuf with
an external buffer, meaning it's a direct conversion.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: nvdimm@lists.linux.dev
---
 tools/testing/nvdimm/test/ndtest.c | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/tools/testing/nvdimm/test/ndtest.c b/tools/testing/nvdimm/test/ndtest.c
index 3ca7c32e93..e9b642f7f8 100644
--- a/tools/testing/nvdimm/test/ndtest.c
+++ b/tools/testing/nvdimm/test/ndtest.c
@@ -12,7 +12,7 @@
 #include <linux/ndctl.h>
 #include <nd-core.h>
 #include <linux/printk.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 
 #include "../watermark.h"
 #include "nfit_test.h"
@@ -797,32 +797,30 @@ static ssize_t flags_show(struct device *dev,
 {
 	struct nvdimm *nvdimm = to_nvdimm(dev);
 	struct ndtest_dimm *dimm = nvdimm_provider_data(nvdimm);
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	u64 flags;
 
 	flags = dimm->flags;
 
-	seq_buf_init(&s, buf, PAGE_SIZE);
 	if (flags & PAPR_PMEM_UNARMED_MASK)
-		seq_buf_printf(&s, "not_armed ");
+		pr_buf(&s, "not_armed ");
 
 	if (flags & PAPR_PMEM_BAD_SHUTDOWN_MASK)
-		seq_buf_printf(&s, "flush_fail ");
+		pr_buf(&s, "flush_fail ");
 
 	if (flags & PAPR_PMEM_BAD_RESTORE_MASK)
-		seq_buf_printf(&s, "restore_fail ");
+		pr_buf(&s, "restore_fail ");
 
 	if (flags & PAPR_PMEM_SAVE_MASK)
-		seq_buf_printf(&s, "save_fail ");
+		pr_buf(&s, "save_fail ");
 
 	if (flags & PAPR_PMEM_SMART_EVENT_MASK)
-		seq_buf_printf(&s, "smart_notify ");
+		pr_buf(&s, "smart_notify ");
 
+	if (printbuf_written(&s))
+		pr_buf(&s, "\n");
 
-	if (seq_buf_used(&s))
-		seq_buf_printf(&s, "\n");
-
-	return seq_buf_used(&s);
+	return printbuf_written(&s);
 }
 static DEVICE_ATTR_RO(flags);
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 26/28] powerpc: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
@ 2022-05-19 17:24   ` Kent Overstreet
  2022-05-19 17:23 ` [PATCH v2 02/28] vsprintf: Convert to printbuf Kent Overstreet
                     ` (27 subsequent siblings)
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy, linuxppc-dev

This converts from seq_buf to printbuf. We're using printbuf in external
buffer mode, so it's a direct conversion, aside from some trivial
refactoring in cpu_show_meltdown() to make the code more consistent.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Cc: linuxppc-dev@lists.ozlabs.org
---
 arch/powerpc/kernel/process.c             | 16 +++--
 arch/powerpc/kernel/security.c            | 75 ++++++++++-------------
 arch/powerpc/platforms/pseries/papr_scm.c | 34 +++++-----
 3 files changed, 57 insertions(+), 68 deletions(-)

diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 984813a4d5..f6f7804516 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -39,7 +39,7 @@
 #include <linux/uaccess.h>
 #include <linux/elf-randomize.h>
 #include <linux/pkeys.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 
 #include <asm/interrupt.h>
 #include <asm/io.h>
@@ -1399,32 +1399,30 @@ void show_user_instructions(struct pt_regs *regs)
 {
 	unsigned long pc;
 	int n = NR_INSN_TO_PRINT;
-	struct seq_buf s;
 	char buf[96]; /* enough for 8 times 9 + 2 chars */
+	struct printbuf s = PRINTBUF_EXTERN(buf, sizeof(buf));
 
 	pc = regs->nip - (NR_INSN_TO_PRINT * 3 / 4 * sizeof(int));
 
-	seq_buf_init(&s, buf, sizeof(buf));
-
 	while (n) {
 		int i;
 
-		seq_buf_clear(&s);
+		printbuf_reset(&s);
 
 		for (i = 0; i < 8 && n; i++, n--, pc += sizeof(int)) {
 			int instr;
 
 			if (copy_from_user_nofault(&instr, (void __user *)pc,
 					sizeof(instr))) {
-				seq_buf_printf(&s, "XXXXXXXX ");
+				pr_buf(&s, "XXXXXXXX ");
 				continue;
 			}
-			seq_buf_printf(&s, regs->nip == pc ? "<%08x> " : "%08x ", instr);
+			pr_buf(&s, regs->nip == pc ? "<%08x> " : "%08x ", instr);
 		}
 
-		if (!seq_buf_has_overflowed(&s))
+		if (printbuf_remaining(&s))
 			pr_info("%s[%d]: code: %s\n", current->comm,
-				current->pid, s.buffer);
+				current->pid, s.buf);
 	}
 }
 
diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c
index e159d4093d..5c9bad138c 100644
--- a/arch/powerpc/kernel/security.c
+++ b/arch/powerpc/kernel/security.c
@@ -10,7 +10,7 @@
 #include <linux/memblock.h>
 #include <linux/nospec.h>
 #include <linux/prctl.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/debugfs.h>
 
 #include <asm/asm-prototypes.h>
@@ -144,31 +144,28 @@ void __init setup_spectre_v2(void)
 #ifdef CONFIG_PPC_BOOK3S_64
 ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
 {
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	bool thread_priv;
 
 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
 
 	if (rfi_flush) {
-		struct seq_buf s;
-		seq_buf_init(&s, buf, PAGE_SIZE - 1);
 
-		seq_buf_printf(&s, "Mitigation: RFI Flush");
+		pr_buf(&s, "Mitigation: RFI Flush");
 		if (thread_priv)
-			seq_buf_printf(&s, ", L1D private per thread");
-
-		seq_buf_printf(&s, "\n");
-
-		return s.len;
+			pr_buf(&s, ", L1D private per thread");
+
+		pr_buf(&s, "\n");
+	} else if (thread_priv) {
+		pr_buf(&s, "Vulnerable: L1D private per thread\n");
+	} else if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
+		   !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) {
+		pr_buf(&s, "Not affected\n");
+	} else {
+		pr_buf(&s, "Vulnerable\n");
 	}
 
-	if (thread_priv)
-		return sprintf(buf, "Vulnerable: L1D private per thread\n");
-
-	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
-	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
-		return sprintf(buf, "Not affected\n");
-
-	return sprintf(buf, "Vulnerable\n");
+	return printbuf_written(&s);
 }
 
 ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf)
@@ -179,70 +176,66 @@ ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *b
 
 ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	struct seq_buf s;
-
-	seq_buf_init(&s, buf, PAGE_SIZE - 1);
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 
 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
 		if (barrier_nospec_enabled)
-			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
+			pr_buf(&s, "Mitigation: __user pointer sanitization");
 		else
-			seq_buf_printf(&s, "Vulnerable");
+			pr_buf(&s, "Vulnerable");
 
 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
-			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
+			pr_buf(&s, ", ori31 speculation barrier enabled");
 
-		seq_buf_printf(&s, "\n");
+		pr_buf(&s, "\n");
 	} else
-		seq_buf_printf(&s, "Not affected\n");
+		pr_buf(&s, "Not affected\n");
 
-	return s.len;
+	return printbuf_written(&s);
 }
 
 ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	bool bcs, ccd;
 
-	seq_buf_init(&s, buf, PAGE_SIZE - 1);
-
 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
 
 	if (bcs || ccd) {
-		seq_buf_printf(&s, "Mitigation: ");
+		pr_buf(&s, "Mitigation: ");
 
 		if (bcs)
-			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
+			pr_buf(&s, "Indirect branch serialisation (kernel only)");
 
 		if (bcs && ccd)
-			seq_buf_printf(&s, ", ");
+			pr_buf(&s, ", ");
 
 		if (ccd)
-			seq_buf_printf(&s, "Indirect branch cache disabled");
+			pr_buf(&s, "Indirect branch cache disabled");
 
 	} else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
-		seq_buf_printf(&s, "Mitigation: Software count cache flush");
+		pr_buf(&s, "Mitigation: Software count cache flush");
 
 		if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			pr_buf(&s, " (hardware accelerated)");
 
 	} else if (btb_flush_enabled) {
-		seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
+		pr_buf(&s, "Mitigation: Branch predictor state flush");
 	} else {
-		seq_buf_printf(&s, "Vulnerable");
+		pr_buf(&s, "Vulnerable");
 	}
 
 	if (bcs || ccd || count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
 		if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE)
-			seq_buf_printf(&s, ", Software link stack flush");
+			pr_buf(&s, ", Software link stack flush");
 		if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			pr_buf(&s, " (hardware accelerated)");
 	}
 
-	seq_buf_printf(&s, "\n");
+	pr_buf(&s, "\n");
 
-	return s.len;
+	return printbuf_written(&s);
 }
 
 #ifdef CONFIG_PPC_BOOK3S_64
diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index f48e87ac89..e55a9af397 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -12,7 +12,7 @@
 #include <linux/libnvdimm.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/nd.h>
 
 #include <asm/plpar_wrappers.h>
@@ -843,7 +843,7 @@ static ssize_t perf_stats_show(struct device *dev,
 {
 	int index;
 	ssize_t rc;
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	struct papr_scm_perf_stat *stat;
 	struct papr_scm_perf_stats *stats;
 	struct nvdimm *dimm = to_nvdimm(dev);
@@ -866,18 +866,17 @@ static ssize_t perf_stats_show(struct device *dev,
 	 * values. Since stat_id is essentially a char string of
 	 * 8 bytes, simply use the string format specifier to print it.
 	 */
-	seq_buf_init(&s, buf, PAGE_SIZE);
 	for (index = 0, stat = stats->scm_statistic;
 	     index < be32_to_cpu(stats->num_statistics);
 	     ++index, ++stat) {
-		seq_buf_printf(&s, "%.8s = 0x%016llX\n",
-			       stat->stat_id,
-			       be64_to_cpu(stat->stat_val));
+		pr_buf(&s, "%.8s = 0x%016llX\n",
+		       stat->stat_id,
+		       be64_to_cpu(stat->stat_val));
 	}
 
 free_stats:
 	kfree(stats);
-	return rc ? rc : (ssize_t)seq_buf_used(&s);
+	return rc ?: printbuf_written(&s);
 }
 static DEVICE_ATTR_ADMIN_RO(perf_stats);
 
@@ -886,7 +885,7 @@ static ssize_t flags_show(struct device *dev,
 {
 	struct nvdimm *dimm = to_nvdimm(dev);
 	struct papr_scm_priv *p = nvdimm_provider_data(dimm);
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	u64 health;
 	int rc;
 
@@ -897,29 +896,28 @@ static ssize_t flags_show(struct device *dev,
 	/* Copy health_bitmap locally, check masks & update out buffer */
 	health = READ_ONCE(p->health_bitmap);
 
-	seq_buf_init(&s, buf, PAGE_SIZE);
 	if (health & PAPR_PMEM_UNARMED_MASK)
-		seq_buf_printf(&s, "not_armed ");
+		pr_buf(&s, "not_armed ");
 
 	if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK)
-		seq_buf_printf(&s, "flush_fail ");
+		pr_buf(&s, "flush_fail ");
 
 	if (health & PAPR_PMEM_BAD_RESTORE_MASK)
-		seq_buf_printf(&s, "restore_fail ");
+		pr_buf(&s, "restore_fail ");
 
 	if (health & PAPR_PMEM_ENCRYPTED)
-		seq_buf_printf(&s, "encrypted ");
+		pr_buf(&s, "encrypted ");
 
 	if (health & PAPR_PMEM_SMART_EVENT_MASK)
-		seq_buf_printf(&s, "smart_notify ");
+		pr_buf(&s, "smart_notify ");
 
 	if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED)
-		seq_buf_printf(&s, "scrubbed locked ");
+		pr_buf(&s, "scrubbed locked ");
 
-	if (seq_buf_used(&s))
-		seq_buf_printf(&s, "\n");
+	if (printbuf_written(&s))
+		pr_buf(&s, "\n");
 
-	return seq_buf_used(&s);
+	return printbuf_written(&s);
 }
 DEVICE_ATTR_RO(flags);
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 26/28] powerpc: Convert to printbuf
@ 2022-05-19 17:24   ` Kent Overstreet
  0 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: andriy.shevchenko, linuxppc-dev, Kent Overstreet, willy

This converts from seq_buf to printbuf. We're using printbuf in external
buffer mode, so it's a direct conversion, aside from some trivial
refactoring in cpu_show_meltdown() to make the code more consistent.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Cc: linuxppc-dev@lists.ozlabs.org
---
 arch/powerpc/kernel/process.c             | 16 +++--
 arch/powerpc/kernel/security.c            | 75 ++++++++++-------------
 arch/powerpc/platforms/pseries/papr_scm.c | 34 +++++-----
 3 files changed, 57 insertions(+), 68 deletions(-)

diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 984813a4d5..f6f7804516 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -39,7 +39,7 @@
 #include <linux/uaccess.h>
 #include <linux/elf-randomize.h>
 #include <linux/pkeys.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 
 #include <asm/interrupt.h>
 #include <asm/io.h>
@@ -1399,32 +1399,30 @@ void show_user_instructions(struct pt_regs *regs)
 {
 	unsigned long pc;
 	int n = NR_INSN_TO_PRINT;
-	struct seq_buf s;
 	char buf[96]; /* enough for 8 times 9 + 2 chars */
+	struct printbuf s = PRINTBUF_EXTERN(buf, sizeof(buf));
 
 	pc = regs->nip - (NR_INSN_TO_PRINT * 3 / 4 * sizeof(int));
 
-	seq_buf_init(&s, buf, sizeof(buf));
-
 	while (n) {
 		int i;
 
-		seq_buf_clear(&s);
+		printbuf_reset(&s);
 
 		for (i = 0; i < 8 && n; i++, n--, pc += sizeof(int)) {
 			int instr;
 
 			if (copy_from_user_nofault(&instr, (void __user *)pc,
 					sizeof(instr))) {
-				seq_buf_printf(&s, "XXXXXXXX ");
+				pr_buf(&s, "XXXXXXXX ");
 				continue;
 			}
-			seq_buf_printf(&s, regs->nip == pc ? "<%08x> " : "%08x ", instr);
+			pr_buf(&s, regs->nip == pc ? "<%08x> " : "%08x ", instr);
 		}
 
-		if (!seq_buf_has_overflowed(&s))
+		if (printbuf_remaining(&s))
 			pr_info("%s[%d]: code: %s\n", current->comm,
-				current->pid, s.buffer);
+				current->pid, s.buf);
 	}
 }
 
diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c
index e159d4093d..5c9bad138c 100644
--- a/arch/powerpc/kernel/security.c
+++ b/arch/powerpc/kernel/security.c
@@ -10,7 +10,7 @@
 #include <linux/memblock.h>
 #include <linux/nospec.h>
 #include <linux/prctl.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/debugfs.h>
 
 #include <asm/asm-prototypes.h>
@@ -144,31 +144,28 @@ void __init setup_spectre_v2(void)
 #ifdef CONFIG_PPC_BOOK3S_64
 ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
 {
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	bool thread_priv;
 
 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
 
 	if (rfi_flush) {
-		struct seq_buf s;
-		seq_buf_init(&s, buf, PAGE_SIZE - 1);
 
-		seq_buf_printf(&s, "Mitigation: RFI Flush");
+		pr_buf(&s, "Mitigation: RFI Flush");
 		if (thread_priv)
-			seq_buf_printf(&s, ", L1D private per thread");
-
-		seq_buf_printf(&s, "\n");
-
-		return s.len;
+			pr_buf(&s, ", L1D private per thread");
+
+		pr_buf(&s, "\n");
+	} else if (thread_priv) {
+		pr_buf(&s, "Vulnerable: L1D private per thread\n");
+	} else if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
+		   !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) {
+		pr_buf(&s, "Not affected\n");
+	} else {
+		pr_buf(&s, "Vulnerable\n");
 	}
 
-	if (thread_priv)
-		return sprintf(buf, "Vulnerable: L1D private per thread\n");
-
-	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
-	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
-		return sprintf(buf, "Not affected\n");
-
-	return sprintf(buf, "Vulnerable\n");
+	return printbuf_written(&s);
 }
 
 ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf)
@@ -179,70 +176,66 @@ ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *b
 
 ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	struct seq_buf s;
-
-	seq_buf_init(&s, buf, PAGE_SIZE - 1);
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 
 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
 		if (barrier_nospec_enabled)
-			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
+			pr_buf(&s, "Mitigation: __user pointer sanitization");
 		else
-			seq_buf_printf(&s, "Vulnerable");
+			pr_buf(&s, "Vulnerable");
 
 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
-			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
+			pr_buf(&s, ", ori31 speculation barrier enabled");
 
-		seq_buf_printf(&s, "\n");
+		pr_buf(&s, "\n");
 	} else
-		seq_buf_printf(&s, "Not affected\n");
+		pr_buf(&s, "Not affected\n");
 
-	return s.len;
+	return printbuf_written(&s);
 }
 
 ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	bool bcs, ccd;
 
-	seq_buf_init(&s, buf, PAGE_SIZE - 1);
-
 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
 
 	if (bcs || ccd) {
-		seq_buf_printf(&s, "Mitigation: ");
+		pr_buf(&s, "Mitigation: ");
 
 		if (bcs)
-			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
+			pr_buf(&s, "Indirect branch serialisation (kernel only)");
 
 		if (bcs && ccd)
-			seq_buf_printf(&s, ", ");
+			pr_buf(&s, ", ");
 
 		if (ccd)
-			seq_buf_printf(&s, "Indirect branch cache disabled");
+			pr_buf(&s, "Indirect branch cache disabled");
 
 	} else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
-		seq_buf_printf(&s, "Mitigation: Software count cache flush");
+		pr_buf(&s, "Mitigation: Software count cache flush");
 
 		if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			pr_buf(&s, " (hardware accelerated)");
 
 	} else if (btb_flush_enabled) {
-		seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
+		pr_buf(&s, "Mitigation: Branch predictor state flush");
 	} else {
-		seq_buf_printf(&s, "Vulnerable");
+		pr_buf(&s, "Vulnerable");
 	}
 
 	if (bcs || ccd || count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
 		if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE)
-			seq_buf_printf(&s, ", Software link stack flush");
+			pr_buf(&s, ", Software link stack flush");
 		if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			pr_buf(&s, " (hardware accelerated)");
 	}
 
-	seq_buf_printf(&s, "\n");
+	pr_buf(&s, "\n");
 
-	return s.len;
+	return printbuf_written(&s);
 }
 
 #ifdef CONFIG_PPC_BOOK3S_64
diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index f48e87ac89..e55a9af397 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -12,7 +12,7 @@
 #include <linux/libnvdimm.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/nd.h>
 
 #include <asm/plpar_wrappers.h>
@@ -843,7 +843,7 @@ static ssize_t perf_stats_show(struct device *dev,
 {
 	int index;
 	ssize_t rc;
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	struct papr_scm_perf_stat *stat;
 	struct papr_scm_perf_stats *stats;
 	struct nvdimm *dimm = to_nvdimm(dev);
@@ -866,18 +866,17 @@ static ssize_t perf_stats_show(struct device *dev,
 	 * values. Since stat_id is essentially a char string of
 	 * 8 bytes, simply use the string format specifier to print it.
 	 */
-	seq_buf_init(&s, buf, PAGE_SIZE);
 	for (index = 0, stat = stats->scm_statistic;
 	     index < be32_to_cpu(stats->num_statistics);
 	     ++index, ++stat) {
-		seq_buf_printf(&s, "%.8s = 0x%016llX\n",
-			       stat->stat_id,
-			       be64_to_cpu(stat->stat_val));
+		pr_buf(&s, "%.8s = 0x%016llX\n",
+		       stat->stat_id,
+		       be64_to_cpu(stat->stat_val));
 	}
 
 free_stats:
 	kfree(stats);
-	return rc ? rc : (ssize_t)seq_buf_used(&s);
+	return rc ?: printbuf_written(&s);
 }
 static DEVICE_ATTR_ADMIN_RO(perf_stats);
 
@@ -886,7 +885,7 @@ static ssize_t flags_show(struct device *dev,
 {
 	struct nvdimm *dimm = to_nvdimm(dev);
 	struct papr_scm_priv *p = nvdimm_provider_data(dimm);
-	struct seq_buf s;
+	struct printbuf s = PRINTBUF_EXTERN(buf, PAGE_SIZE);
 	u64 health;
 	int rc;
 
@@ -897,29 +896,28 @@ static ssize_t flags_show(struct device *dev,
 	/* Copy health_bitmap locally, check masks & update out buffer */
 	health = READ_ONCE(p->health_bitmap);
 
-	seq_buf_init(&s, buf, PAGE_SIZE);
 	if (health & PAPR_PMEM_UNARMED_MASK)
-		seq_buf_printf(&s, "not_armed ");
+		pr_buf(&s, "not_armed ");
 
 	if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK)
-		seq_buf_printf(&s, "flush_fail ");
+		pr_buf(&s, "flush_fail ");
 
 	if (health & PAPR_PMEM_BAD_RESTORE_MASK)
-		seq_buf_printf(&s, "restore_fail ");
+		pr_buf(&s, "restore_fail ");
 
 	if (health & PAPR_PMEM_ENCRYPTED)
-		seq_buf_printf(&s, "encrypted ");
+		pr_buf(&s, "encrypted ");
 
 	if (health & PAPR_PMEM_SMART_EVENT_MASK)
-		seq_buf_printf(&s, "smart_notify ");
+		pr_buf(&s, "smart_notify ");
 
 	if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED)
-		seq_buf_printf(&s, "scrubbed locked ");
+		pr_buf(&s, "scrubbed locked ");
 
-	if (seq_buf_used(&s))
-		seq_buf_printf(&s, "\n");
+	if (printbuf_written(&s))
+		pr_buf(&s, "\n");
 
-	return seq_buf_used(&s);
+	return printbuf_written(&s);
 }
 DEVICE_ATTR_RO(flags);
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 27/28] x86/resctrl: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (25 preceding siblings ...)
  2022-05-19 17:24   ` Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-19 17:24 ` [PATCH v2 28/28] PCI/P2PDMA: " Kent Overstreet
  2022-05-26 14:44 ` [PATCH v2 00/28] Printbufs (now with more printbufs!) Petr Mladek
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy, x86

This converts from seq_buf to printbuf. We're using printbuf in external
buffer mode, so it's a direct conversion, aside from some trivial
refactoring in cpu_show_meltdown() to make the code more consistent.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Cc: x86@kernel.org
---
 arch/x86/kernel/cpu/resctrl/rdtgroup.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kernel/cpu/resctrl/rdtgroup.c b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
index b57b3db9a6..8e4f7773b7 100644
--- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c
+++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
@@ -19,7 +19,7 @@
 #include <linux/fs_parser.h>
 #include <linux/sysfs.h>
 #include <linux/kernfs.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/seq_file.h>
 #include <linux/sched/signal.h>
 #include <linux/sched/task.h>
@@ -51,7 +51,7 @@ static struct kernfs_node *kn_mongrp;
 /* Kernel fs node for "mon_data" directory under root */
 static struct kernfs_node *kn_mondata;
 
-static struct seq_buf last_cmd_status;
+static struct printbuf last_cmd_status;
 static char last_cmd_status_buf[512];
 
 struct dentry *debugfs_resctrl;
@@ -59,13 +59,13 @@ struct dentry *debugfs_resctrl;
 void rdt_last_cmd_clear(void)
 {
 	lockdep_assert_held(&rdtgroup_mutex);
-	seq_buf_clear(&last_cmd_status);
+	printbuf_reset(&last_cmd_status);
 }
 
 void rdt_last_cmd_puts(const char *s)
 {
 	lockdep_assert_held(&rdtgroup_mutex);
-	seq_buf_puts(&last_cmd_status, s);
+	pr_str(&last_cmd_status, s);
 }
 
 void rdt_last_cmd_printf(const char *fmt, ...)
@@ -74,7 +74,7 @@ void rdt_last_cmd_printf(const char *fmt, ...)
 
 	va_start(ap, fmt);
 	lockdep_assert_held(&rdtgroup_mutex);
-	seq_buf_vprintf(&last_cmd_status, fmt, ap);
+	vpr_buf(&last_cmd_status, fmt, ap);
 	va_end(ap);
 }
 
@@ -833,7 +833,7 @@ static int rdt_last_cmd_status_show(struct kernfs_open_file *of,
 	int len;
 
 	mutex_lock(&rdtgroup_mutex);
-	len = seq_buf_used(&last_cmd_status);
+	len = printbuf_written(&last_cmd_status);
 	if (len)
 		seq_printf(seq, "%.*s", len, last_cmd_status_buf);
 	else
@@ -3248,8 +3248,8 @@ int __init rdtgroup_init(void)
 {
 	int ret = 0;
 
-	seq_buf_init(&last_cmd_status, last_cmd_status_buf,
-		     sizeof(last_cmd_status_buf));
+	last_cmd_status = PRINTBUF_EXTERN(last_cmd_status_buf,
+					  sizeof(last_cmd_status_buf));
 
 	ret = rdtgroup_setup_root();
 	if (ret)
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v2 28/28] PCI/P2PDMA: Convert to printbuf
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (26 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 27/28] x86/resctrl: " Kent Overstreet
@ 2022-05-19 17:24 ` Kent Overstreet
  2022-05-26 14:44 ` [PATCH v2 00/28] Printbufs (now with more printbufs!) Petr Mladek
  28 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 17:24 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: Kent Overstreet, andriy.shevchenko, willy, linux-pci

This converts from seq_buf to printbuf. We're using printbuf in external
buffer mode, so it's a direct conversion, aside from some trivial
refactoring in cpu_show_meltdown() to make the code more consistent.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Cc: linux-pci@vger.kernel.org
---
 drivers/pci/p2pdma.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 1015274bd2..0d5c993386 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -17,7 +17,7 @@
 #include <linux/memremap.h>
 #include <linux/percpu-refcount.h>
 #include <linux/random.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/xarray.h>
 
 enum pci_p2pdma_map_type {
@@ -281,12 +281,9 @@ static int pci_bridge_has_acs_redir(struct pci_dev *pdev)
 	return 0;
 }
 
-static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
+static void pr_bus_devfn(struct printbuf *buf, struct pci_dev *pdev)
 {
-	if (!buf)
-		return;
-
-	seq_buf_printf(buf, "%s;", pci_name(pdev));
+	pr_buf(buf, "%s;", pci_name(pdev));
 }
 
 static bool cpu_supports_p2pdma(void)
@@ -454,13 +451,11 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
 	struct pci_dev *a = provider, *b = client, *bb;
 	bool acs_redirects = false;
 	struct pci_p2pdma *p2pdma;
-	struct seq_buf acs_list;
 	int acs_cnt = 0;
 	int dist_a = 0;
 	int dist_b = 0;
 	char buf[128];
-
-	seq_buf_init(&acs_list, buf, sizeof(buf));
+	struct printbuf acs_list = PRINTBUF_EXTERN(buf, sizeof(buf));
 
 	/*
 	 * Note, we don't need to take references to devices returned by
@@ -471,7 +466,7 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
 		dist_b = 0;
 
 		if (pci_bridge_has_acs_redir(a)) {
-			seq_buf_print_bus_devfn(&acs_list, a);
+			pr_bus_devfn(&acs_list, a);
 			acs_cnt++;
 		}
 
@@ -500,7 +495,7 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
 			break;
 
 		if (pci_bridge_has_acs_redir(bb)) {
-			seq_buf_print_bus_devfn(&acs_list, bb);
+			pr_bus_devfn(&acs_list, bb);
 			acs_cnt++;
 		}
 
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
@ 2022-05-19 18:21   ` Matthew Wilcox
  2022-05-20  4:44     ` Kent Overstreet
  2022-05-26 15:06   ` Petr Mladek
  1 sibling, 1 reply; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-19 18:21 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, andriy.shevchenko

On Thu, May 19, 2022 at 01:23:54PM -0400, Kent Overstreet wrote:
> This adds printbufs: a printbuf points to a char * buffer and knows the
> size of the output buffer as well as the current output position.
> 
> Future patches will be adding more features to printbuf, but initially
> printbufs are targeted at refactoring and improving our existing code in
> lib/vsprintf.c - so this initial printbuf patch has the features
> required for that.
> 
> Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>

Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>

My only complaint ...

> +#define PRINTBUF ((struct printbuf) { .si_units = PRINTBUF_UNITS_10 })

si_units doesn't exist yet :-)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
@ 2022-05-19 18:33   ` Matthew Wilcox
  2022-05-20  4:43     ` Kent Overstreet
  2022-05-19 21:06   ` David Laight
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-19 18:33 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, andriy.shevchenko

On Thu, May 19, 2022 at 01:23:56PM -0400, Kent Overstreet wrote:
> +++ b/Documentation/core-api/printk-formats.rst
> @@ -625,6 +625,25 @@ Examples::
>  	%p4cc	Y10  little-endian (0x20303159)
>  	%p4cc	NV12 big-endian (0xb231564e)
>  
> +Calling a pretty printer function
> +---------------------------------
> +
> +::
> +
> +        %p(%p)     pretty printer function taking one argument
> +        %p(%p,%p)  pretty printer function taking two arguments

Needs to be updated to the %pf change.

> +        printf("%p(%p)", foo_to_text, foo);

Likewise

Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf
  2022-05-19 17:24 ` [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
@ 2022-05-19 20:04   ` Andy Shevchenko
  2022-05-19 20:19     ` Kent Overstreet
  0 siblings, 1 reply; 63+ messages in thread
From: Andy Shevchenko @ 2022-05-19 20:04 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Thu, May 19, 2022 at 01:24:15PM -0400, Kent Overstreet wrote:
> seq_buf is being deprecated, this converts to printbuf which is similar
> but heap allocates the string buffer.
> 
> This means we have to consider memory allocation context & failure: Here
> we're in device initialization so GFP_KERNEL should be fine, and also as
> we're in device initialization returning -ENOMEM is fine.

...

> +       int ret = 0;

Redundant assignment.

...

> -		seq_buf_printf(&s, " %d-hat",
> -			       hweight16(analog->mask & ANALOG_HATS_ALL));
> +		pr_buf(&buf, " %d-hat",
> +		       hweight16(analog->mask & ANALOG_HATS_ALL));

Now you may put it on one line here and in similar cases.

...

> +	ret = buf.allocation_failure ? -ENOMEM : 0;
> +	if (!ret)
> +		strlcpy(analog->name, buf.buf, sizeof(analog->name));
> +	printbuf_exit(&buf);
> +	return ret;

Looks like anti-pattern. On top a bit twisted error code manipulation before
checking for error codes, but what about

static int printbuf_export(*buf, *out, size)
{
	...
}

	ret = printbuf_export(&buf, analog->name, sizeof(analog->name));
	printbuf_exit(&buf);
	return ret;

?

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf
  2022-05-19 20:04   ` Andy Shevchenko
@ 2022-05-19 20:19     ` Kent Overstreet
  2022-05-19 21:09       ` Andy Shevchenko
  0 siblings, 1 reply; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 20:19 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Thu, May 19, 2022 at 11:04:27PM +0300, Andy Shevchenko wrote:
> On Thu, May 19, 2022 at 01:24:15PM -0400, Kent Overstreet wrote:
> > seq_buf is being deprecated, this converts to printbuf which is similar
> > but heap allocates the string buffer.
> > 
> > This means we have to consider memory allocation context & failure: Here
> > we're in device initialization so GFP_KERNEL should be fine, and also as
> > we're in device initialization returning -ENOMEM is fine.
> 
> ...
> 
> > +       int ret = 0;
> 
> Redundant assignment.
> 
> ...
> 
> > -		seq_buf_printf(&s, " %d-hat",
> > -			       hweight16(analog->mask & ANALOG_HATS_ALL));
> > +		pr_buf(&buf, " %d-hat",
> > +		       hweight16(analog->mask & ANALOG_HATS_ALL));
> 
> Now you may put it on one line here and in similar cases.
> 
> ...
> 
> > +	ret = buf.allocation_failure ? -ENOMEM : 0;
> > +	if (!ret)
> > +		strlcpy(analog->name, buf.buf, sizeof(analog->name));
> > +	printbuf_exit(&buf);
> > +	return ret;
> 
> Looks like anti-pattern. On top a bit twisted error code manipulation before
> checking for error codes, but what about

This conversion predated my adding external buffers to printbufs - now the patch
is more of a direct conversion:

-- >8 --
Subject: [PATCH] Input/joystick/analog: Convert from seq_buf -> printbuf

seq_buf is being deprecated, this converts to printbuf.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 drivers/input/joystick/analog.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index 3088c5b829..1dde969b29 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -19,7 +19,7 @@
 #include <linux/input.h>
 #include <linux/gameport.h>
 #include <linux/jiffies.h>
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 #include <linux/timex.h>
 #include <linux/timekeeping.h>
 
@@ -339,24 +339,21 @@ static void analog_calibrate_timer(struct analog_port *port)
 
 static void analog_name(struct analog *analog)
 {
-	struct seq_buf s;
+	struct printbuf buf = PRINTBUF_EXTERN(analog->name, sizeof(analog->name));
 
-	seq_buf_init(&s, analog->name, sizeof(analog->name));
-	seq_buf_printf(&s, "Analog %d-axis %d-button",
-		 hweight8(analog->mask & ANALOG_AXES_STD),
-		 hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
-		 hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
+	pr_buf(&buf, "Analog %d-axis %d-button",
+	       hweight8(analog->mask & ANALOG_AXES_STD),
+	       hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
+	       hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
 
 	if (analog->mask & ANALOG_HATS_ALL)
-		seq_buf_printf(&s, " %d-hat",
-			       hweight16(analog->mask & ANALOG_HATS_ALL));
-
+		pr_buf(&buf, " %d-hat", hweight16(analog->mask & ANALOG_HATS_ALL));
 	if (analog->mask & ANALOG_HAT_FCS)
-		seq_buf_printf(&s, " FCS");
+		pr_buf(&buf, " FCS");
 	if (analog->mask & ANALOG_ANY_CHF)
-		seq_buf_printf(&s, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
+		pr_buf(&buf, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
 
-	seq_buf_printf(&s, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
+	pr_buf(&buf, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
 }
 
 /*
-- 
2.36.0


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 17:24 ` [PATCH v2 07/28] lib/printbuf: Unit specifiers Kent Overstreet
@ 2022-05-19 20:21   ` Andy Shevchenko
  2022-05-19 20:26     ` Kent Overstreet
  0 siblings, 1 reply; 63+ messages in thread
From: Andy Shevchenko @ 2022-05-19 20:21 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Thu, May 19, 2022 at 01:24:00PM -0400, Kent Overstreet wrote:
> This adds options to printbuf for specifying whether units should be
> printed raw (default) or with human readable units, and for controlling
> whether human-readable units should be base 2 (default), or base 10.
> 
> This also adds new helpers that obey these options:
> 
>  - pr_human_readable_u64
>  - pr_human_readable_s64
> These obey printbuf->si_units
> 
>  - pr_units_u64
>  - pr_units_s64
> These obey both printbuf-human_readable_units and printbuf->si_units

...

> +void pr_human_readable_s64(struct printbuf *buf, s64 v)
> +{
> +	if (v < 0)
> +		pr_char(buf, '-');
> +	pr_human_readable_u64(buf, abs(v));

Wouldn't -v work?

> +}

...

> + * pr_human_readable_u64 - Print out a u64 according to printbuf unit options

Have you ever compile this? We have kernel doc validator running when compiling
the code...

> + * Units are either raw (default), or human reabable units (controlled via
> + * @buf->human_readable_units)
> + */
> +void pr_units_u64(struct printbuf *out, u64 v)
> +{
> +	if (out->human_readable_units)
> +		pr_human_readable_u64(out, v);
> +	else
> +		pr_buf(out, "%llu", v);
> +}
> +EXPORT_SYMBOL(pr_units_u64);
> +
> +/**
> + * pr_human_readable_s64 - Print out a s64 according to printbuf unit options

Ditto.

> + * Units are either raw (default), or human reabable units (controlled via
> + * @buf->human_readable_units)
> + */
> +void pr_units_s64(struct printbuf *out, s64 v)
> +{
> +	if (v < 0)
> +		pr_char(out, '-');
> +	pr_units_u64(out, v);

Please, start with test cases.

> +}

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 20:21   ` Andy Shevchenko
@ 2022-05-19 20:26     ` Kent Overstreet
  2022-05-19 21:11       ` Matthew Wilcox
  2022-05-19 21:16       ` Andy Shevchenko
  0 siblings, 2 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-19 20:26 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Thu, May 19, 2022 at 11:21:41PM +0300, Andy Shevchenko wrote:
> On Thu, May 19, 2022 at 01:24:00PM -0400, Kent Overstreet wrote:
> > +void pr_human_readable_s64(struct printbuf *buf, s64 v)
> > +{
> > +	if (v < 0)
> > +		pr_char(buf, '-');
> > +	pr_human_readable_u64(buf, abs(v));
> 
> Wouldn't -v work?

This is a bit terser

> > + * pr_human_readable_u64 - Print out a u64 according to printbuf unit options
> 
> Have you ever compile this? We have kernel doc validator running when compiling
> the code...

Yes I have, but I've never seen the kernel doc validator - can you point me to
something so I can get that working?

> > +void pr_units_s64(struct printbuf *out, s64 v)
> > +{
> > +	if (v < 0)
> > +		pr_char(out, '-');
> > +	pr_units_u64(out, v);
> 
> Please, start with test cases.

I suppose now that we've got kunit I should learn how to use it :) And thanks,
good catch

^ permalink raw reply	[flat|nested] 63+ messages in thread

* RE: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
  2022-05-19 18:33   ` Matthew Wilcox
@ 2022-05-19 21:06   ` David Laight
  2022-05-19 21:15     ` Matthew Wilcox
  2022-05-20  4:49     ` Kent Overstreet
  2022-05-19 21:20   ` Matthew Wilcox
  2022-05-20  7:40   ` Michal Hocko
  3 siblings, 2 replies; 63+ messages in thread
From: David Laight @ 2022-05-19 21:06 UTC (permalink / raw)
  To: 'Kent Overstreet',
	linux-kernel, linux-mm, pmladek, rostedt, senozhatsky
  Cc: andriy.shevchenko, willy

From: Kent Overstreet
> Sent: 19 May 2022 18:24
> 
> This implements two new format strings: both do the same thing, one more
> compatible with current gcc format string checking, the other that we'd
> like to standardize:
> 
>  %p(%p) - more compatible
>  %(%p)  - more prettier
> 
> Both can take variable numbers of arguments, i.e. %(%p,%p,%p).
> 
> They're used to indicate that snprintf or pr_buf should interpret the
> next argument as a pretty-printer function to call, and subsequent
> arguments within the parentheses should be passed to the pretty-printer.

I suspect this a very good way to blow the kernel stack.
The highest stack use is already very likely to be inside
the printf code in an error path somewhere.

...
> The goal is to replace most of our %p format extensions with this
> interface, and to move pretty-printers out of the core vsprintf.c code -

One advantage of the current scheme is that is reasonably safe
and easy to use.
Perhaps too many extra formats have been added recently.
This all seems like a recipe for disaster with functions being
called with the wrong number of parameters
(I can't think how you can compile-time check it).

Double copying using a temporary buffer isn't the end of the world.
It is only a problem because pr_cont() is basically impossible.
But since kernel printf ought to be formatted to reasonable
line length that isn't really an issue.
printf() is expensive an extra memory copy is probably noise.

...
> Currently, we can only call pretty printers with pointer arguments. This
> could be changed to also allow at least integer arguments in the future
> by using libffi.

I'm sure I remember something else trying to use that.
IIRC it is basically broken by design.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf
  2022-05-19 20:19     ` Kent Overstreet
@ 2022-05-19 21:09       ` Andy Shevchenko
  0 siblings, 0 replies; 63+ messages in thread
From: Andy Shevchenko @ 2022-05-19 21:09 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Thu, May 19, 2022 at 04:19:27PM -0400, Kent Overstreet wrote:
> On Thu, May 19, 2022 at 11:04:27PM +0300, Andy Shevchenko wrote:
> > On Thu, May 19, 2022 at 01:24:15PM -0400, Kent Overstreet wrote:

..

> This conversion predated my adding external buffers to printbufs - now the patch
> is more of a direct conversion:
> 
> -- >8 --
> Subject: [PATCH] Input/joystick/analog: Convert from seq_buf -> printbuf

This looks much better.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 20:26     ` Kent Overstreet
@ 2022-05-19 21:11       ` Matthew Wilcox
  2022-05-20  4:40         ` Kent Overstreet
  2022-05-19 21:16       ` Andy Shevchenko
  1 sibling, 1 reply; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-19 21:11 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: Andy Shevchenko, linux-kernel, linux-mm, pmladek, rostedt, senozhatsky

On Thu, May 19, 2022 at 04:26:26PM -0400, Kent Overstreet wrote:
> On Thu, May 19, 2022 at 11:21:41PM +0300, Andy Shevchenko wrote:
> > On Thu, May 19, 2022 at 01:24:00PM -0400, Kent Overstreet wrote:
> > > +void pr_human_readable_s64(struct printbuf *buf, s64 v)
> > > +{
> > > +	if (v < 0)
> > > +		pr_char(buf, '-');
> > > +	pr_human_readable_u64(buf, abs(v));
> > 
> > Wouldn't -v work?
> 
> This is a bit terser

How about:

	if (v < 0) {
		pr_char(buf, '-');
		v = -v;
	}
	pr_human_readable_u64(buf, v);

(some pedantic compilers might warn about the behaviour of S64_MIN, but
I think we're OK)

> > > + * pr_human_readable_u64 - Print out a u64 according to printbuf unit options
> > 
> > Have you ever compile this? We have kernel doc validator running when compiling
> > the code...
> 
> Yes I have, but I've never seen the kernel doc validator - can you point me to
> something so I can get that working?

You have to run 'make W=1' to get it, so Andy is being a bit overly
harsh here.  Most people use neither W=1, nor C=1.


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 21:06   ` David Laight
@ 2022-05-19 21:15     ` Matthew Wilcox
  2022-05-20  8:03       ` David Laight
  2022-05-20  4:49     ` Kent Overstreet
  1 sibling, 1 reply; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-19 21:15 UTC (permalink / raw)
  To: David Laight
  Cc: 'Kent Overstreet',
	linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko

On Thu, May 19, 2022 at 09:06:24PM +0000, David Laight wrote:
> I suspect this a very good way to blow the kernel stack.
> The highest stack use is already very likely to be inside
> the printf code in an error path somewhere.

...

> Double copying using a temporary buffer isn't the end of the world.

How can you hold both of these positions simultaneously?

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 20:26     ` Kent Overstreet
  2022-05-19 21:11       ` Matthew Wilcox
@ 2022-05-19 21:16       ` Andy Shevchenko
  2022-05-19 21:22         ` Matthew Wilcox
  2022-05-20  4:36         ` Kent Overstreet
  1 sibling, 2 replies; 63+ messages in thread
From: Andy Shevchenko @ 2022-05-19 21:16 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Thu, May 19, 2022 at 04:26:26PM -0400, Kent Overstreet wrote:
> On Thu, May 19, 2022 at 11:21:41PM +0300, Andy Shevchenko wrote:
> > On Thu, May 19, 2022 at 01:24:00PM -0400, Kent Overstreet wrote:

...

> > > +	if (v < 0)
> > > +		pr_char(buf, '-');
> > > +	pr_human_readable_u64(buf, abs(v));
> > 
> > Wouldn't -v work?
> 
> This is a bit terser

Have you checked assembly? Basically the question here is does the compiler see
the same conditional and avoid duplicating it?

...

> > > + * pr_human_readable_u64 - Print out a u64 according to printbuf unit options
> > 
> > Have you ever compile this? We have kernel doc validator running when compiling
> > the code...
> 
> Yes I have, but I've never seen the kernel doc validator - can you point me to
> something so I can get that working?

I run `make W=1`, but I think the kernel doc validator is run anyway.

You always can run it manually

	scripts/kernel-doc -v -none lib/printbuf.c

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
  2022-05-19 18:33   ` Matthew Wilcox
  2022-05-19 21:06   ` David Laight
@ 2022-05-19 21:20   ` Matthew Wilcox
  2022-05-20  7:40   ` Michal Hocko
  3 siblings, 0 replies; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-19 21:20 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, andriy.shevchenko

On Thu, May 19, 2022 at 01:23:56PM -0400, Kent Overstreet wrote:
> +			while (1) {
> +				if (WARN_ON_ONCE(nr_args == ARRAY_SIZE(fn_args)))
> +					goto out;
> +				if (*fmt++ != '%')
> +					goto out;
> +				if (*fmt++ != 'p')
> +					goto out;
> +				fn_args[nr_args++] = va_arg(args, void *);
> +				if (*fmt == ')')
> +					break;
> +				if (*fmt++ != ',')
> +					goto out;
> +			}
> +
> +			call_pr_fn(out, fn, fn_args, nr_args);
> +			fmt++; /* past trailing ) */

It's not a big thing, but why not make the loop do:

				c = *fmt++;
				if (c == ')')
					break;
				if (c != ',')
					goto out;

and then you can drop the fmt++ line outside the loop.

(it's obviosuly awkward; it needed a comment)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 21:16       ` Andy Shevchenko
@ 2022-05-19 21:22         ` Matthew Wilcox
  2022-05-20  4:36         ` Kent Overstreet
  1 sibling, 0 replies; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-19 21:22 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Kent Overstreet, linux-kernel, linux-mm, pmladek, rostedt, senozhatsky

On Fri, May 20, 2022 at 12:16:41AM +0300, Andy Shevchenko wrote:
> I run `make W=1`, but I think the kernel doc validator is run anyway.

No, it's too slow and used to emit too many warnings to run it at any
time.  That's why I made it opt-in with W=1 (and even that caused
screeches from people who were compiling with W=1 in their CI frameworks
;-)


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 21:16       ` Andy Shevchenko
  2022-05-19 21:22         ` Matthew Wilcox
@ 2022-05-20  4:36         ` Kent Overstreet
  1 sibling, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20  4:36 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, willy

On Fri, May 20, 2022 at 12:16:41AM +0300, Andy Shevchenko wrote:
> On Thu, May 19, 2022 at 04:26:26PM -0400, Kent Overstreet wrote:
> > On Thu, May 19, 2022 at 11:21:41PM +0300, Andy Shevchenko wrote:
> > > On Thu, May 19, 2022 at 01:24:00PM -0400, Kent Overstreet wrote:
> 
> ...
> 
> > > > +	if (v < 0)
> > > > +		pr_char(buf, '-');
> > > > +	pr_human_readable_u64(buf, abs(v));
> > > 
> > > Wouldn't -v work?
> > 
> > This is a bit terser
> 
> Have you checked assembly? Basically the question here is does the compiler see
> the same conditional and avoid duplicating it?

No. I check the assembly for code where it matters - for this, I'm going to
write it the way I find it most readable.

> I run `make W=1`, but I think the kernel doc validator is run anyway.

Got it, and fixed the doc warnings.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 07/28] lib/printbuf: Unit specifiers
  2022-05-19 21:11       ` Matthew Wilcox
@ 2022-05-20  4:40         ` Kent Overstreet
  0 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20  4:40 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Andy Shevchenko, linux-kernel, linux-mm, pmladek, rostedt, senozhatsky

On Thu, May 19, 2022 at 10:11:40PM +0100, Matthew Wilcox wrote:
> How about:
> 
> 	if (v < 0) {
> 		pr_char(buf, '-');
> 		v = -v;
> 	}
> 	pr_human_readable_u64(buf, v);
> 
> (some pedantic compilers might warn about the behaviour of S64_MIN, but
> I think we're OK)

Yes, since S64_MAX == -S64_MIN and S64_MIN == S64_MAX + 1, the code is correct
for v == S64_MIN.

But I still prefer my way :)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 18:33   ` Matthew Wilcox
@ 2022-05-20  4:43     ` Kent Overstreet
  0 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20  4:43 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, andriy.shevchenko

On Thu, May 19, 2022 at 07:33:32PM +0100, Matthew Wilcox wrote:
> On Thu, May 19, 2022 at 01:23:56PM -0400, Kent Overstreet wrote:
> > +++ b/Documentation/core-api/printk-formats.rst
> > @@ -625,6 +625,25 @@ Examples::
> >  	%p4cc	Y10  little-endian (0x20303159)
> >  	%p4cc	NV12 big-endian (0xb231564e)
> >  
> > +Calling a pretty printer function
> > +---------------------------------
> > +
> > +::
> > +
> > +        %p(%p)     pretty printer function taking one argument
> > +        %p(%p,%p)  pretty printer function taking two arguments
> 
> Needs to be updated to the %pf change.
> 
> > +        printf("%p(%p)", foo_to_text, foo);
> 
> Likewise
> 
> Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>

Thanks - fixed.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-19 18:21   ` Matthew Wilcox
@ 2022-05-20  4:44     ` Kent Overstreet
  0 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20  4:44 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky, andriy.shevchenko

On Thu, May 19, 2022 at 07:21:00PM +0100, Matthew Wilcox wrote:
> On Thu, May 19, 2022 at 01:23:54PM -0400, Kent Overstreet wrote:
> > This adds printbufs: a printbuf points to a char * buffer and knows the
> > size of the output buffer as well as the current output position.
> > 
> > Future patches will be adding more features to printbuf, but initially
> > printbufs are targeted at refactoring and improving our existing code in
> > lib/vsprintf.c - so this initial printbuf patch has the features
> > required for that.
> > 
> > Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
> 
> Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> 
> My only complaint ...
> 
> > +#define PRINTBUF ((struct printbuf) { .si_units = PRINTBUF_UNITS_10 })
> 
> si_units doesn't exist yet :-)

Thanks, also fixed :)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 21:06   ` David Laight
  2022-05-19 21:15     ` Matthew Wilcox
@ 2022-05-20  4:49     ` Kent Overstreet
  2022-05-20 14:17       ` andriy.shevchenko
  1 sibling, 1 reply; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20  4:49 UTC (permalink / raw)
  To: David Laight
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko, willy

On Thu, May 19, 2022 at 09:06:24PM +0000, David Laight wrote:
> From: Kent Overstreet
> > Sent: 19 May 2022 18:24
> > 
> > This implements two new format strings: both do the same thing, one more
> > compatible with current gcc format string checking, the other that we'd
> > like to standardize:
> > 
> >  %p(%p) - more compatible
> >  %(%p)  - more prettier
> > 
> > Both can take variable numbers of arguments, i.e. %(%p,%p,%p).
> > 
> > They're used to indicate that snprintf or pr_buf should interpret the
> > next argument as a pretty-printer function to call, and subsequent
> > arguments within the parentheses should be passed to the pretty-printer.
> 
> I suspect this a very good way to blow the kernel stack.
> The highest stack use is already very likely to be inside
> the printf code in an error path somewhere.

By getting rid of stack allocated buffers, I've been _reducing_ stack usage.
Also, the new printbuf calling convention reduces stack usage as well.

It's true that we'll want to keep the stack usage of pr_buf -> pretty printer ->
pr_buf again minimal, but I don't see any difficulties there the way the code is
structured now. 

> 
> ...
> > The goal is to replace most of our %p format extensions with this
> > interface, and to move pretty-printers out of the core vsprintf.c code -
> 
> One advantage of the current scheme is that is reasonably safe
> and easy to use.
> Perhaps too many extra formats have been added recently.
> This all seems like a recipe for disaster with functions being
> called with the wrong number of parameters
> (I can't think how you can compile-time check it).

We can't check it at compile time yet, it's true - printf format checking will
need to be extended. But we're already talking about doing that.

> Double copying using a temporary buffer isn't the end of the world.
> It is only a problem because pr_cont() is basically impossible.
> But since kernel printf ought to be formatted to reasonable
> line length that isn't really an issue.
> printf() is expensive an extra memory copy is probably noise.
> 
> ...
> > Currently, we can only call pretty printers with pointer arguments. This
> > could be changed to also allow at least integer arguments in the future
> > by using libffi.
> 
> I'm sure I remember something else trying to use that.
> IIRC it is basically broken by design.

Hmm? libffi is the standard for calling C from a lot of languages. If it's
broken by design, that's some real news. And it does constructed function calls,
which is exactly what we need here.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
                     ` (2 preceding siblings ...)
  2022-05-19 21:20   ` Matthew Wilcox
@ 2022-05-20  7:40   ` Michal Hocko
  2022-05-20 15:01     ` Kent Overstreet
  2022-05-20 17:56     ` Kent Overstreet
  3 siblings, 2 replies; 63+ messages in thread
From: Michal Hocko @ 2022-05-20  7:40 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko, willy

On Thu 19-05-22 13:23:56, Kent Overstreet wrote:
[...]
> The goal is to replace most of our %p format extensions with this
> interface, and to move pretty-printers out of the core vsprintf.c code -
> this will get us better organization and better discoverability (you'll
> be able to cscope to pretty printer calls!), as well as eliminate a lot
> of dispatch code in vsprintf.c.

Is this really something that we want? While I do see arguments about
our existing %p$FOO mess there is at least one good argument to have all
those "pretty printers" at a single location. That approach allows to do
a proper review whether those printers are safe from the printk point of
view. If we allow any random callback to be called from the printk
context we just give a free ticket to anybody to do whatever from there
without understanding of all potential consequences. 

Maybe that is less of a concern these days when printk locking has been
reworked a lot but I still do remember how frustrating it is to debug
issues related to printk getting stuck...
-- 
Michal Hocko
SUSE Labs

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf
  2022-05-19 17:24 ` [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf Kent Overstreet
@ 2022-05-20  7:59   ` Michal Hocko
  0 siblings, 0 replies; 63+ messages in thread
From: Michal Hocko @ 2022-05-20  7:59 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko, willy

On Thu 19-05-22 13:24:16, Kent Overstreet wrote:
> This converts memory_stat_format() from seq_buf to printbuf. Printbuf is
> simalar to seq_buf except that it heap allocates the string buffer:
> here, we were already heap allocating the buffer with kmalloc() so the
> conversion is trivial.

I have asked when you posted this last time around but I do not remember
any response. Let me try again. Why do we want this?
-- 
Michal Hocko
SUSE Labs

^ permalink raw reply	[flat|nested] 63+ messages in thread

* RE: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-19 21:15     ` Matthew Wilcox
@ 2022-05-20  8:03       ` David Laight
  2022-05-20 13:08         ` Matthew Wilcox
  0 siblings, 1 reply; 63+ messages in thread
From: David Laight @ 2022-05-20  8:03 UTC (permalink / raw)
  To: 'Matthew Wilcox'
  Cc: 'Kent Overstreet',
	linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko

From: Matthew Wilcox
> Sent: 19 May 2022 22:15
> 
> On Thu, May 19, 2022 at 09:06:24PM +0000, David Laight wrote:
> > I suspect this a very good way to blow the kernel stack.
> > The highest stack use is already very likely to be inside
> > the printf code in an error path somewhere.
> 
> ...
> 
> > Double copying using a temporary buffer isn't the end of the world.
> 
> How can you hold both of these positions simultaneously?

Mostly you only need a short(ish) buffer to format a single item.
But even a full line buffer is probably less stack than a
nested printf call.
It certainly makes it possible for static analysis to determine
maximum stack depth.

That made me realise that one of the problems doing static
analysis of stack depth is indirect calls.
The hashes that (IIRC) fine-ibt add to indirect calls
and functions make it possible to tie these together.
So you can determine the worst one of the called functions
and use it for the stack depth at the call sites.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-20  8:03       ` David Laight
@ 2022-05-20 13:08         ` Matthew Wilcox
  0 siblings, 0 replies; 63+ messages in thread
From: Matthew Wilcox @ 2022-05-20 13:08 UTC (permalink / raw)
  To: David Laight
  Cc: 'Kent Overstreet',
	linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko

On Fri, May 20, 2022 at 08:03:29AM +0000, David Laight wrote:
> From: Matthew Wilcox
> > Sent: 19 May 2022 22:15
> > 
> > On Thu, May 19, 2022 at 09:06:24PM +0000, David Laight wrote:
> > > I suspect this a very good way to blow the kernel stack.
> > > The highest stack use is already very likely to be inside
> > > the printf code in an error path somewhere.
> > 
> > ...
> > 
> > > Double copying using a temporary buffer isn't the end of the world.
> > 
> > How can you hold both of these positions simultaneously?
> 
> Mostly you only need a short(ish) buffer to format a single item.
> But even a full line buffer is probably less stack than a
> nested printf call.
> It certainly makes it possible for static analysis to determine
> maximum stack depth.

There are a lot of words in this response.  Numbers, please.

> That made me realise that one of the problems doing static
> analysis of stack depth is indirect calls.
> The hashes that (IIRC) fine-ibt add to indirect calls
> and functions make it possible to tie these together.
> So you can determine the worst one of the called functions
> and use it for the stack depth at the call sites.
> 
> 	David
> 
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
> Registration No: 1397386 (Wales)
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-20  4:49     ` Kent Overstreet
@ 2022-05-20 14:17       ` andriy.shevchenko
  0 siblings, 0 replies; 63+ messages in thread
From: andriy.shevchenko @ 2022-05-20 14:17 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: David Laight, linux-kernel, linux-mm, pmladek, rostedt,
	senozhatsky, willy

On Fri, May 20, 2022 at 12:49:24AM -0400, Kent Overstreet wrote:
> On Thu, May 19, 2022 at 09:06:24PM +0000, David Laight wrote:

...

> > > The goal is to replace most of our %p format extensions with this
> > > interface, and to move pretty-printers out of the core vsprintf.c code -
> > 
> > One advantage of the current scheme is that is reasonably safe
> > and easy to use.
> > Perhaps too many extra formats have been added recently.
> > This all seems like a recipe for disaster with functions being
> > called with the wrong number of parameters
> > (I can't think how you can compile-time check it).
> 
> We can't check it at compile time yet, it's true - printf format checking will
> need to be extended. But we're already talking about doing that.

I have heard about GCC plugin, which also may check the %p extension usages.


-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-20  7:40   ` Michal Hocko
@ 2022-05-20 15:01     ` Kent Overstreet
  2022-05-20 17:56     ` Kent Overstreet
  1 sibling, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20 15:01 UTC (permalink / raw)
  To: Michal Hocko
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko, willy

On Fri, May 20, 2022 at 09:40:24AM +0200, Michal Hocko wrote:
> On Thu 19-05-22 13:23:56, Kent Overstreet wrote:
> [...]
> > The goal is to replace most of our %p format extensions with this
> > interface, and to move pretty-printers out of the core vsprintf.c code -
> > this will get us better organization and better discoverability (you'll
> > be able to cscope to pretty printer calls!), as well as eliminate a lot
> > of dispatch code in vsprintf.c.
> 
> Is this really something that we want? While I do see arguments about
> our existing %p$FOO mess there is at least one good argument to have all
> those "pretty printers" at a single location. That approach allows to do
> a proper review whether those printers are safe from the printk point of
> view. If we allow any random callback to be called from the printk
> context we just give a free ticket to anybody to do whatever from there
> without understanding of all potential consequences. 
> 
> Maybe that is less of a concern these days when printk locking has been
> reworked a lot but I still do remember how frustrating it is to debug
> issues related to printk getting stuck...

Thanks for raising this, but I see this as an argument for documenting and
perhaps improving printk locking rules. Curiously, there was no such
documentation in vsprintf.c, nor anywhere else I've looked yet.

We may want to consider double buffering - it would get rid of any locking
concerns if we wrote to a separate buffer while building up the printk message.
However, this would require allocating memory, so it'd require MM folks to get
over their fear of allocating memory... :)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 03/28] vsprintf: %pf(%p)
  2022-05-20  7:40   ` Michal Hocko
  2022-05-20 15:01     ` Kent Overstreet
@ 2022-05-20 17:56     ` Kent Overstreet
  1 sibling, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-20 17:56 UTC (permalink / raw)
  To: Michal Hocko
  Cc: linux-kernel, linux-mm, pmladek, rostedt, senozhatsky,
	andriy.shevchenko, willy

On Fri, May 20, 2022 at 09:40:24AM +0200, Michal Hocko wrote:
> On Thu 19-05-22 13:23:56, Kent Overstreet wrote:
> [...]
> > The goal is to replace most of our %p format extensions with this
> > interface, and to move pretty-printers out of the core vsprintf.c code -
> > this will get us better organization and better discoverability (you'll
> > be able to cscope to pretty printer calls!), as well as eliminate a lot
> > of dispatch code in vsprintf.c.
> 
> Is this really something that we want? While I do see arguments about
> our existing %p$FOO mess there is at least one good argument to have all
> those "pretty printers" at a single location. That approach allows to do
> a proper review whether those printers are safe from the printk point of
> view. If we allow any random callback to be called from the printk
> context we just give a free ticket to anybody to do whatever from there
> without understanding of all potential consequences. 
> 
> Maybe that is less of a concern these days when printk locking has been
> reworked a lot but I still do remember how frustrating it is to debug
> issues related to printk getting stuck...

So for now, I added a note in the documentation that pretty-printers may not
sleep if passed to printk() - but if they're just passed to pr_buf() or
sprintf() it's completely fine.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 00/28] Printbufs (now with more printbufs!)
  2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
                   ` (27 preceding siblings ...)
  2022-05-19 17:24 ` [PATCH v2 28/28] PCI/P2PDMA: " Kent Overstreet
@ 2022-05-26 14:44 ` Petr Mladek
  2022-05-26 15:11   ` Kent Overstreet
  28 siblings, 1 reply; 63+ messages in thread
From: Petr Mladek @ 2022-05-26 14:44 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Thu 2022-05-19 13:23:53, Kent Overstreet wrote:
> So there's a lot of new stuff since the first posting:

>  - Printbufs have been broken up into multiple patches that each add distinct
>    functionality - this is intended to make it easier to review and to see
>    what's used for what

It is great that it is split. Also it is great to see all the ideas.
But I would really prefer to somehow split it to make it easier
for review and rebasing.

I see the following "independent" parts:

  1. Add simple API that allows to replace @len, @buf, @end in vsprintf.c
     by @printbuf. I agree that the code looks better and more safe.

  2. Clean up of printf_spec. It would be great. But I do not like
     some parts. For example, si_units, human_readable_units
     are not property of the buffer. They are specific for a
     particular substring.

  3. New %p(%p) format. It really needs deep thinking. It is a
     ticket for potential big troubles. It is one patch that
     might be introduced and discussed anytime once we have
     the simple buffer API.

  3. Replace seq_buf. Steven Rostedt has to agree with it. Honestly,
     I do not see any improvement. The patches mostly do 1:1 replacement
     of one API with another.

  4. Heap allocated buffer. I am not sure if it is really needed.
     The patchset adds 3 users. IMHO, small static buffer would be
     perfectly fine for 2 of them. I personally do not like the error
     handling and the need to call exit.

  5. All the fancy stuff (pr_tab(), pr_string_option()). The patchset
    does not add any user for them.


I am going to comment the particular patches. It might take some
time. The patchset is really huge. It would really help to split it.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
  2022-05-19 18:21   ` Matthew Wilcox
@ 2022-05-26 15:06   ` Petr Mladek
  2022-05-26 15:21     ` Kent Overstreet
  1 sibling, 1 reply; 63+ messages in thread
From: Petr Mladek @ 2022-05-26 15:06 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Thu 2022-05-19 13:23:54, Kent Overstreet wrote:
> This adds printbufs: a printbuf points to a char * buffer and knows the
> size of the output buffer as well as the current output position.
> 
> Future patches will be adding more features to printbuf, but initially
> printbufs are targeted at refactoring and improving our existing code in
> lib/vsprintf.c - so this initial printbuf patch has the features
> required for that.

> diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
> new file mode 100644
> index 0000000000..40dc07040d
> --- /dev/null
> +++ b/include/linux/printbuf.h
> +static inline void pr_chars(struct printbuf *out, char c, unsigned n)
> +{
> +	memset(out->buf + out->pos,
> +	       c,
> +	       min(n, printbuf_remaining(out)));
> +	out->pos += n;
> +	printbuf_nul_terminate(out);
> +}

This function is not later used. Please, do not add API
that will not have users in the same patchset.

There are several other cases. I am not going to comment
all of them.


> +static inline void __pr_char(struct printbuf *out, char c)
> +{
> +	if (printbuf_remaining(out))
> +		out->buf[out->pos] = c;
> +	out->pos++;
> +}
> +
> +static inline void pr_char(struct printbuf *out, char c)
> +{
> +	__pr_char(out, c);
> +	printbuf_nul_terminate(out);
> +}

The "pr_" prefix is a nightmare for me because the same prefix
is used also for printk() API ;-)

Could we please use "pb_" instead?

Note that "prb_" prefix is already used by the lockless printk
ringbuffer, see kernel/printk/printk_ringbuffer.h.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 00/28] Printbufs (now with more printbufs!)
  2022-05-26 14:44 ` [PATCH v2 00/28] Printbufs (now with more printbufs!) Petr Mladek
@ 2022-05-26 15:11   ` Kent Overstreet
  0 siblings, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-26 15:11 UTC (permalink / raw)
  To: Petr Mladek
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Thu, May 26, 2022 at 04:44:20PM +0200, Petr Mladek wrote:
> On Thu 2022-05-19 13:23:53, Kent Overstreet wrote:
> > So there's a lot of new stuff since the first posting:
> 
> >  - Printbufs have been broken up into multiple patches that each add distinct
> >    functionality - this is intended to make it easier to review and to see
> >    what's used for what
> 
> It is great that it is split. Also it is great to see all the ideas.
> But I would really prefer to somehow split it to make it easier
> for review and rebasing.
> 
> I see the following "independent" parts:
> 
>   1. Add simple API that allows to replace @len, @buf, @end in vsprintf.c
>      by @printbuf. I agree that the code looks better and more safe.
> 
>   2. Clean up of printf_spec. It would be great. But I do not like
>      some parts. For example, si_units, human_readable_units
>      are not property of the buffer. They are specific for a
>      particular substring.

Not in conventional usage - these are properties that are set globally when
building up a string: consider an -h flag to a userspace utility.

> 
>   3. New %p(%p) format. It really needs deep thinking. It is a
>      ticket for potential big troubles. It is one patch that
>      might be introduced and discussed anytime once we have
>      the simple buffer API.

It's split out into a separate patch - discuss away!

>   3. Replace seq_buf. Steven Rostedt has to agree with it. Honestly,
>      I do not see any improvement. The patches mostly do 1:1 replacement
>      of one API with another.

It was necessary to avoid code duplication, which Christoph didn't like, and
seq_buf wasn't quite right for what I was trying to do and Steven didn't want to
change it. (read_pos is tracing specific and doesn't have anything to do with
printing, some of the semantics had to be tweaked to support snprintf, and the
name was wrong... :)

>   4. Heap allocated buffer. I am not sure if it is really needed.
>      The patchset adds 3 users. IMHO, small static buffer would be
>      perfectly fine for 2 of them. I personally do not like the error
>      handling and the need to call exit.
> 
>   5. All the fancy stuff (pr_tab(), pr_string_option()). The patchset
>     does not add any user for them.

Heap allocated buffers and tabstops: these are things that I've been using in
bcachefs, and have quickly become necessities - I included them because I think
others will soon find them valuable (e.g. /proc has a lot of files that would be
more readable if formatted with tabstops).

pr_string_option(): this isn't anything really new, we've got a lot of pretty
printers and string helpers of this nature that need to be better organized and
given a common calling convention, I included this as code I've got that's been
useful and I think does it cleanly. I have more future plans for pretty printer
cleanup.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-26 15:06   ` Petr Mladek
@ 2022-05-26 15:21     ` Kent Overstreet
  2022-05-26 15:36       ` Joe Perches
  2022-05-27 10:29       ` Petr Mladek
  0 siblings, 2 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-26 15:21 UTC (permalink / raw)
  To: Petr Mladek
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Thu, May 26, 2022 at 05:06:15PM +0200, Petr Mladek wrote:
> On Thu 2022-05-19 13:23:54, Kent Overstreet wrote:
> > This adds printbufs: a printbuf points to a char * buffer and knows the
> > size of the output buffer as well as the current output position.
> > 
> > Future patches will be adding more features to printbuf, but initially
> > printbufs are targeted at refactoring and improving our existing code in
> > lib/vsprintf.c - so this initial printbuf patch has the features
> > required for that.
> 
> > diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
> > new file mode 100644
> > index 0000000000..40dc07040d
> > --- /dev/null
> > +++ b/include/linux/printbuf.h
> > +static inline void pr_chars(struct printbuf *out, char c, unsigned n)
> > +{
> > +	memset(out->buf + out->pos,
> > +	       c,
> > +	       min(n, printbuf_remaining(out)));
> > +	out->pos += n;
> > +	printbuf_nul_terminate(out);
> > +}
> 
> This function is not later used. Please, do not add API
> that will not have users in the same patchset.
> 
> There are several other cases. I am not going to comment
> all of them.

It is used in this patchset, in lib/vsnprintf.c. You sure about the other cases?

> > +static inline void __pr_char(struct printbuf *out, char c)
> > +{
> > +	if (printbuf_remaining(out))
> > +		out->buf[out->pos] = c;
> > +	out->pos++;
> > +}
> > +
> > +static inline void pr_char(struct printbuf *out, char c)
> > +{
> > +	__pr_char(out, c);
> > +	printbuf_nul_terminate(out);
> > +}
> 
> The "pr_" prefix is a nightmare for me because the same prefix
> is used also for printk() API ;-)
> 
> Could we please use "pb_" instead?

I'm not entirely against that, but I see printbufs as already in this patchset
tightly coupled to vsprintf.c and thus quite related to printk, as well - and
there aren't that many different pr_ things. So I think the shared prefix makes
some sense, I'd like to hear what others think before making that change.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-26 15:21     ` Kent Overstreet
@ 2022-05-26 15:36       ` Joe Perches
  2022-05-27 10:29       ` Petr Mladek
  1 sibling, 0 replies; 63+ messages in thread
From: Joe Perches @ 2022-05-26 15:36 UTC (permalink / raw)
  To: Kent Overstreet, Petr Mladek
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Thu, 2022-05-26 at 11:21 -0400, Kent Overstreet wrote:
> On Thu, May 26, 2022 at 05:06:15PM +0200, Petr Mladek wrote:
> > The "pr_" prefix is a nightmare for me because the same prefix
> > is used also for printk() API ;-)
> > 
> > Could we please use "pb_" instead?
> 
> I'm not entirely against that, but I see printbufs as already in this patchset
> tightly coupled to vsprintf.c and thus quite related to printk, as well - and
> there aren't that many different pr_ things. So I think the shared prefix makes
> some sense, I'd like to hear what others think before making that change.

I think the reused prefix is not good.
bufs are not printks.




^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-26 15:21     ` Kent Overstreet
  2022-05-26 15:36       ` Joe Perches
@ 2022-05-27 10:29       ` Petr Mladek
  2022-05-29 18:15         ` Kent Overstreet
  2022-05-31 22:03         ` Kent Overstreet
  1 sibling, 2 replies; 63+ messages in thread
From: Petr Mladek @ 2022-05-27 10:29 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Thu 2022-05-26 11:21:27, Kent Overstreet wrote:
> On Thu, May 26, 2022 at 05:06:15PM +0200, Petr Mladek wrote:
> > On Thu 2022-05-19 13:23:54, Kent Overstreet wrote:
> > > This adds printbufs: a printbuf points to a char * buffer and knows the
> > > size of the output buffer as well as the current output position.
> > > 
> > > Future patches will be adding more features to printbuf, but initially
> > > printbufs are targeted at refactoring and improving our existing code in
> > > lib/vsprintf.c - so this initial printbuf patch has the features
> > > required for that.
> > 
> > > diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
> > > new file mode 100644
> > > index 0000000000..40dc07040d
> > > --- /dev/null
> > > +++ b/include/linux/printbuf.h
> > > +static inline void pr_chars(struct printbuf *out, char c, unsigned n)
> > > +{
> > > +	memset(out->buf + out->pos,
> > > +	       c,
> > > +	       min(n, printbuf_remaining(out)));
> > > +	out->pos += n;
> > > +	printbuf_nul_terminate(out);
> > > +}
> > 
> > This function is not later used. Please, do not add API
> > that will not have users in the same patchset.
> > 
> > There are several other cases. I am not going to comment
> > all of them.
> 
> It is used in this patchset, in lib/vsnprintf.c. You sure about the other cases?

Ah, I used outdated cscope. This was bad example.

> > > +static inline void __pr_char(struct printbuf *out, char c)
> > > +{
> > > +	if (printbuf_remaining(out))
> > > +		out->buf[out->pos] = c;
> > > +	out->pos++;
> > > +}
> > > +
> > > +static inline void pr_char(struct printbuf *out, char c)
> > > +{
> > > +	__pr_char(out, c);
> > > +	printbuf_nul_terminate(out);
> > > +}
> > 
> > The "pr_" prefix is a nightmare for me because the same prefix
> > is used also for printk() API ;-)
> > 
> > Could we please use "pb_" instead?
> 
> I'm not entirely against that, but I see printbufs as already in this patchset
> tightly coupled to vsprintf.c and thus quite related to printk, as well - and
> there aren't that many different pr_ things. So I think the shared prefix makes
> some sense, I'd like to hear what others think before making that change.

I would really like to keep the three APIs separated and easy to
distinguish. They are principally different:

1. pr_*() API:

       + wrapper to printk(). They makes the messages available on
	 console and for user-space log daemons while printf()

      + the various pr_*() variants are used to define kernel
	specific features and behavior, for example:
	loglevel, ratelimit, only once. deferred console handling.

       + uses implicit (system) buffer

       + The message format is defined by the 1st parameter. It
	 is the same way as printf() in user-space.

       + It is inspired by printf() from user-space that prints
	 the messages to the standard output.


2. *s*printf() APIs:

       + basically duplicate the same user-space API. It supports
	 some extra %p modifiers. There might be few more
	 incompatibilities.

       + use simple "char *" buffer provided as the 1st parameter

       + the messages format is defined the same way as in
	 the user-space counterparts.


3. printbuf API:

       + append messages into the given printbuf by small pieces

       + format defined by the suffix, for example, _char(),
	 bytes(), units_64(), _tab(), indent()

       + allows to do special operations on the buffer,
	 for example, _reset(), make_room(), atomic_inc()

       + it will be used as low-level API for vscnprinf()
	 implementation, pretty printing API, or
	 stand alone uses.

       + I wonder if there will be variant that will allow
	 to pass the format in the printf() way, e.g.
	 int pb_printf(printbuf *buf, const char *fmt, ...);

       + is there any user space counter part?


Now, it is clear that printfbuf API must be distinguished by another
prefix:

       + it must be clear that it stores the output into printbuf.
	 It is similar to dprintf(), fprintf(), sprintf().

       + It can't be done by the suffix because it is already used
	 to define format of the appended string or extra operation.

       + It must be clear what is low-level API used to implement
	 vsprintf() and high-level API that uses vsprintf().
	 I mean pb_char() vs. pb_printf().


Best Regards,
Petr

PS: I probably won't find time to write more comments on this patchset
    today. I'll continue the following week. It seems that it will
    be a long journey.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-27 10:29       ` Petr Mladek
@ 2022-05-29 18:15         ` Kent Overstreet
  2022-05-31 22:03         ` Kent Overstreet
  1 sibling, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-29 18:15 UTC (permalink / raw)
  To: Petr Mladek
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Fri, May 27, 2022 at 12:29:20PM +0200, Petr Mladek wrote:
> I would really like to keep the three APIs separated and easy to
> distinguish. They are principally different:
> 
> 1. pr_*() API:
> 
>        + wrapper to printk(). They makes the messages available on
> 	 console and for user-space log daemons while printf()
> 
>       + the various pr_*() variants are used to define kernel
> 	specific features and behavior, for example:
> 	loglevel, ratelimit, only once. deferred console handling.
> 
>        + uses implicit (system) buffer
> 
>        + The message format is defined by the 1st parameter. It
> 	 is the same way as printf() in user-space.
> 
>        + It is inspired by printf() from user-space that prints
> 	 the messages to the standard output.
> 
> 
> 2. *s*printf() APIs:
> 
>        + basically duplicate the same user-space API. It supports
> 	 some extra %p modifiers. There might be few more
> 	 incompatibilities.
> 
>        + use simple "char *" buffer provided as the 1st parameter
> 
>        + the messages format is defined the same way as in
> 	 the user-space counterparts.

After printbufs are merged, I think we should consider formally deprecating
sprintf/snprintf, certainly for new code. As you saw from the vsprintf.c
cleanup, printbufs are _much_ nicer than passing around char */length - it's
2022, we shouldn't be doing that anymore!

> 
> 
> 3. printbuf API:
> 
>        + append messages into the given printbuf by small pieces
> 
>        + format defined by the suffix, for example, _char(),
> 	 bytes(), units_64(), _tab(), indent()
> 
>        + allows to do special operations on the buffer,
> 	 for example, _reset(), make_room(), atomic_inc()

atomic_inc() should not exist in the long term - we _really_ need
memalloc_nowait_(save|restore), that's the correct way to do this.

>        + it will be used as low-level API for vscnprinf()
> 	 implementation, pretty printing API, or
> 	 stand alone uses.
> 
>        + I wonder if there will be variant that will allow
> 	 to pass the format in the printf() way, e.g.
> 	 int pb_printf(printbuf *buf, const char *fmt, ...);

That's pr_buf()/vpr_buf(), and I heavily use pr_buf() in my own code.

snprintf() is just a wrapper around pr_buf() now.

>        + is there any user space counter part?

I've been using the previous version of this code in userspace that was part of
bcachefs, and my intention is very much for this code to also be used in
userspace as well.

Bringing the base printbuf API to userspace is trivial - i.e. doing it as a
wrapper around snprintf(), which is how printbufs started. However, the %(%p)
format string extension for calling pretty-printers directly - which I badly
want and think is far superior to what glibc has [1], will also require patching
glibc (and gcc, to get the format string that we want).

So that'll be a little ways off.

> Now, it is clear that printfbuf API must be distinguished by another
> prefix:
> 
>        + it must be clear that it stores the output into printbuf.
> 	 It is similar to dprintf(), fprintf(), sprintf().
> 
>        + It can't be done by the suffix because it is already used
> 	 to define format of the appended string or extra operation.
> 
>        + It must be clear what is low-level API used to implement
> 	 vsprintf() and high-level API that uses vsprintf().
> 	 I mean pb_char() vs. pb_printf().

I'm coming around to the pb_* naming idea. pbprintf() doesn't roll off the
tongue in the same way that pr_buf() does, but I guess I can live with that.

1: https://www.gnu.org/software/libc/manual/html_node/Printf-Extension-Example.html

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v2 01/28] lib/printbuf: New data structure for printing strings
  2022-05-27 10:29       ` Petr Mladek
  2022-05-29 18:15         ` Kent Overstreet
@ 2022-05-31 22:03         ` Kent Overstreet
  1 sibling, 0 replies; 63+ messages in thread
From: Kent Overstreet @ 2022-05-31 22:03 UTC (permalink / raw)
  To: Petr Mladek
  Cc: linux-kernel, linux-mm, rostedt, senozhatsky, andriy.shevchenko, willy

On Fri, May 27, 2022 at 12:29:20PM +0200, Petr Mladek wrote:
> I would really like to keep the three APIs separated and easy to
> distinguish. They are principally different:
> 
> 1. pr_*() API:
> 
>        + wrapper to printk(). They makes the messages available on
> 	 console and for user-space log daemons while printf()
> 
>       + the various pr_*() variants are used to define kernel
> 	specific features and behavior, for example:
> 	loglevel, ratelimit, only once. deferred console handling.
> 
>        + uses implicit (system) buffer
> 
>        + The message format is defined by the 1st parameter. It
> 	 is the same way as printf() in user-space.
> 
>        + It is inspired by printf() from user-space that prints
> 	 the messages to the standard output.
> 
> 
> 2. *s*printf() APIs:
> 
>        + basically duplicate the same user-space API. It supports
> 	 some extra %p modifiers. There might be few more
> 	 incompatibilities.
> 
>        + use simple "char *" buffer provided as the 1st parameter
> 
>        + the messages format is defined the same way as in
> 	 the user-space counterparts.

I'd like to get sprintf() style functions - anything that outputs to raw char *
pointers - deprecated. That's going to mean a _lot_ of refactoring (so I don't
know that I'll be the one to do it), but it's mostly easy refactoring.

> 3. printbuf API:
> 
>        + append messages into the given printbuf by small pieces
> 
>        + format defined by the suffix, for example, _char(),
> 	 bytes(), units_64(), _tab(), indent()
> 
>        + allows to do special operations on the buffer,
> 	 for example, _reset(), make_room(), atomic_inc()
> 
>        + it will be used as low-level API for vscnprinf()
> 	 implementation, pretty printing API, or
> 	 stand alone uses.
> 
>        + I wonder if there will be variant that will allow
> 	 to pass the format in the printf() way, e.g.
> 	 int pb_printf(printbuf *buf, const char *fmt, ...);

Right now this is called pr_buf(). I suppose pr_printf()/pb_printf() makes sense
:)

> 
>        + is there any user space counter part?
> 
> 
> Now, it is clear that printfbuf API must be distinguished by another
> prefix:
> 
>        + it must be clear that it stores the output into printbuf.
> 	 It is similar to dprintf(), fprintf(), sprintf().
> 
>        + It can't be done by the suffix because it is already used
> 	 to define format of the appended string or extra operation.
> 
>        + It must be clear what is low-level API used to implement
> 	 vsprintf() and high-level API that uses vsprintf().
> 	 I mean pb_char() vs. pb_printf().

So there's more in the pr_* namespace than I realized - I guess you've convinced
me on not reusing that. Which is a shame, because it rolls off the tongue so
much easier than pb_* and I think otherwise makes more sense here - pr_foo for
"print foo".

However, I'm not going to put special operations on printbufs under the pb_
prefix: I want that naming (whether pb_* or pr_*) to _just_ be for "print foo";
this "print this" prefix should be the common prefix for _any_ pretty printer,
unless it has another subsystem prefix - that means there's going to be a lot of
functions with these prefix. So I'm going to keep "printbuf special operations"
on the printbuf_ prefix.

Also, how about prt_* instead of pb_*? I want something that sounds more like
print, and prt_ isn't taken.

^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2022-05-31 22:03 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
2022-05-19 18:21   ` Matthew Wilcox
2022-05-20  4:44     ` Kent Overstreet
2022-05-26 15:06   ` Petr Mladek
2022-05-26 15:21     ` Kent Overstreet
2022-05-26 15:36       ` Joe Perches
2022-05-27 10:29       ` Petr Mladek
2022-05-29 18:15         ` Kent Overstreet
2022-05-31 22:03         ` Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 02/28] vsprintf: Convert to printbuf Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 03/28] vsprintf: %pf(%p) Kent Overstreet
2022-05-19 18:33   ` Matthew Wilcox
2022-05-20  4:43     ` Kent Overstreet
2022-05-19 21:06   ` David Laight
2022-05-19 21:15     ` Matthew Wilcox
2022-05-20  8:03       ` David Laight
2022-05-20 13:08         ` Matthew Wilcox
2022-05-20  4:49     ` Kent Overstreet
2022-05-20 14:17       ` andriy.shevchenko
2022-05-19 21:20   ` Matthew Wilcox
2022-05-20  7:40   ` Michal Hocko
2022-05-20 15:01     ` Kent Overstreet
2022-05-20 17:56     ` Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 04/28] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 05/28] lib/printbuf: Heap allocation Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 06/28] lib/printbuf: Tabstops, indenting Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 07/28] lib/printbuf: Unit specifiers Kent Overstreet
2022-05-19 20:21   ` Andy Shevchenko
2022-05-19 20:26     ` Kent Overstreet
2022-05-19 21:11       ` Matthew Wilcox
2022-05-20  4:40         ` Kent Overstreet
2022-05-19 21:16       ` Andy Shevchenko
2022-05-19 21:22         ` Matthew Wilcox
2022-05-20  4:36         ` Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 08/28] lib/pretty-printers: pr_string_option(), pr_bitflags() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 09/28] vsprintf: Improve number() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 10/28] vsprintf: pr_u64_minwidth(), pr_u64() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 11/28] vsprintf: Lift pr_hex_bytes() out from hex_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 12/28] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 13/28] vsprintf: Start consolidating printf_spec handling Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 14/28] vsprintf: Refactor resource_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 15/28] vsprintf: Refactor fourcc_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 16/28] vsprintf: Refactor ip_addr_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 17/28] vsprintf: Refactor mac_address_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 18/28] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 19/28] vsprintf: flags_string() " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 20/28] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 21/28] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
2022-05-19 20:04   ` Andy Shevchenko
2022-05-19 20:19     ` Kent Overstreet
2022-05-19 21:09       ` Andy Shevchenko
2022-05-19 17:24 ` [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf Kent Overstreet
2022-05-20  7:59   ` Michal Hocko
2022-05-19 17:24 ` [PATCH v2 24/28] clk: tegra: bpmp: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 25/28] tools/testing/nvdimm: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 26/28] powerpc: " Kent Overstreet
2022-05-19 17:24   ` Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 27/28] x86/resctrl: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 28/28] PCI/P2PDMA: " Kent Overstreet
2022-05-26 14:44 ` [PATCH v2 00/28] Printbufs (now with more printbufs!) Petr Mladek
2022-05-26 15:11   ` Kent Overstreet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.