All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/34] Printbufs - new data structure for building strings
@ 2022-06-20  0:41 Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 01/34] lib/printbuf: New data structure for printing strings Kent Overstreet
                   ` (36 more replies)
  0 siblings, 37 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:41 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

Previous discussions:
https://lore.kernel.org/all/20220419203202.2670193-1-kent.overstreet@gmail.com/
https://lore.kernel.org/all/20220519172421.162394-1-kent.overstreet@gmail.com/
https://lore.kernel.org/all/20220604193042.1674951-1-kent.overstreet@gmail.com/

Git repo:
https://evilpiepirate.org/git/bcachefs.git/log/?h=printbuf_v4

Changes since v3:
  Bugfixes and performance improvements, the latest iteration of this patch
  series has been baking in the bcachefs tree and that shook out some bugs.

  Rasmus pointed out that -fno-strict-aliasing is going to cause gcc to generate
  nasty code, and indeed it unfortunately does but according to worst case
  scenario microbenchmarks it's not a problem for actual performance. Using
  memcpy() and memset() in the printbuf helpers _was_ a problem for performance,
  so that's been fixed.

-----------

Core idea: Wouldn't it be nice if we had a common data structure and calling
convention for outputting strings?

The core concept this patch series is aimed at cleaning up and standardizing is
that of a "pretty-printer", which is now a function like prt_foo() or
foo_to_text():

    void foo_to_text(struct printbuf *out, struct foo)

What this patch series does or enables:

 - It becomes quite a bit easier to write composable pretty printers! This is
   huge.

 - A ton of code that works in terms of raw char * pointers and lengths
   (snprintf style, and many weird variations) gets cleaned up, with error prone
   raw pointers arithmetic replaced by proper helpers

 - A ton of code that emits either directly via printk() or to other places
   (sysfs, debugfs) can now output to printbufs, and becomes more reusable and
   composable

 - Countesy of Matthew Wilcox, the new and very cool %pf() format string, which
   allows passing a pretty printer function and its arguments to sprintf() and
   family. This means we can now call type specific pretty-printers without
   adding them to lib/vsprintf.c and writing a bunch of crazy
   parsing-and-dispatch code. For example,

     printk("%pd", dentry);

   becomes

     printk("%pf(%p)", prt_dentry, dentry);

   My OOM debugging & reporting patch series that builds off of this uses this
   to solve a very real problem that Michal Hocko brought up at LSF - with this
   we write shrinkers_to_text(), slab_to_text() which can _also_ now be used for
   reporting in debugfs (which Roman has been working on), as well as in the
   show_mem() report - the "%pf()" syntax lets us print the output of those
   functions without allocating (and having to preallocate) a separate buffer.

 - Some new formatting helpers:
   
   Nicely aligned text is much easier to read, and something that we want a
   _lot, but outputting nicely aligned text with printf() is a pain in the ass.
   Printbufs add tabstops, which can be used for right or left justification -
   simple, easy. prt_tab() emits spaces up to the next tabstop, prt_tab_rjust()
   advances to the next tabstop right justifying text since the previous
   tabstop.

   Printbufs also add an indent level, obeyed by prt_newline() which can be very
   useful for multi line output.

   In the future, \n and \t in format strings may learn to obey these as well.

 - Optional heap allocation - no need to statically allocate buffers on the
   stack and guess at the output size.

 - Lots of consolidating and refactoring
   
   This series replaces seq_buf, which does basically what an earlier version of
   printbufs did.

   A good chunk of lib/string_helpers.c, as well as lib/hexdump.c are converted
   (and simplified!).

   Pretty printers in lib/vsprintf.c previously outputted to buffers on the
   stack and then copied _that_ to the actual output buffer, that's all gone
   (replaced by proper helpers for outputting chars and strings), and they also
   used printf_spec for argument passing in ad-hoc ways. This patch series does
   a lot towards converting them to more standard pretty printers that can be
   called via %pf() instead of having to live in lib/vsprintf.c. Still to do:
   format string decoding for argument passing is a mess that's scattered all
   over the place.

In the course of working on this patch series, I've spotted a _lot_ more
consolidation and refactoring that needs to be done - we've got a ton of API
fragmentation leading to lots of code duplication.

But I'm already really excited about what this patch series enables.

Cheers!

Kent Overstreet (34):
  lib/printbuf: New data structure for printing strings
  lib/string_helpers: Convert string_escape_mem() to printbuf
  vsprintf: Convert to printbuf
  lib/hexdump: 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: prt_string_option(), prt_bitflags()
  vsprintf: Improve number()
  vsprintf: prt_u64_minwidth(), prt_u64()
  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
  tracing: trace_events_synth: Convert to printbuf
  d_path: prt_path()
  ACPI/APEI: Add missing include
  tracing: Convert to printbuf
  Delete seq_buf

 Documentation/core-api/printk-formats.rst |   22 +
 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/acpi/apei/erst-dbg.c              |    1 +
 drivers/clk/tegra/clk-bpmp.c              |   21 +-
 drivers/input/joystick/analog.c           |   23 +-
 drivers/pci/p2pdma.c                      |   21 +-
 fs/d_path.c                               |   35 +
 include/linux/dcache.h                    |    1 +
 include/linux/kernel.h                    |   12 +
 include/linux/pretty-printers.h           |   10 +
 include/linux/printbuf.h                  |  253 +++
 include/linux/seq_buf.h                   |  162 --
 include/linux/string.h                    |    5 +
 include/linux/string_helpers.h            |    8 +-
 include/linux/trace_events.h              |    2 +-
 include/linux/trace_seq.h                 |   17 +-
 kernel/trace/trace.c                      |   45 +-
 kernel/trace/trace_dynevent.c             |   34 +-
 kernel/trace/trace_events_filter.c        |    2 +-
 kernel/trace/trace_events_synth.c         |   32 +-
 kernel/trace/trace_functions_graph.c      |    6 +-
 kernel/trace/trace_kprobe.c               |    2 +-
 kernel/trace/trace_seq.c                  |  111 +-
 lib/Makefile                              |    4 +-
 lib/hexdump.c                             |  246 +--
 lib/pretty-printers.c                     |   60 +
 lib/printbuf.c                            |  253 +++
 lib/seq_buf.c                             |  397 -----
 lib/string_helpers.c                      |  224 +--
 lib/test_hexdump.c                        |   30 +-
 lib/test_printf.c                         |   33 +-
 lib/vsprintf.c                            | 1723 ++++++++++-----------
 mm/memcontrol.c                           |   68 +-
 tools/testing/nvdimm/test/ndtest.c        |   22 +-
 37 files changed, 2050 insertions(+), 1976 deletions(-)
 create mode 100644 include/linux/pretty-printers.h
 create mode 100644 include/linux/printbuf.h
 delete mode 100644 include/linux/seq_buf.h
 create mode 100644 lib/pretty-printers.c
 create mode 100644 lib/printbuf.c
 delete mode 100644 lib/seq_buf.c

-- 
2.36.1


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

* [PATCH v4 01/34] lib/printbuf: New data structure for printing strings
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  4:44   ` David Laight
  2022-06-20  0:42 ` [PATCH v4 02/34] lib/string_helpers: Convert string_escape_mem() to printbuf Kent Overstreet
                   ` (35 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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>
Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 include/linux/printbuf.h | 122 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 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..8186c447ca
--- /dev/null
+++ b/include/linux/printbuf.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/* Copyright (C) 2022 Kent Overstreet */
+
+#ifndef _LINUX_PRINTBUF_H
+#define _LINUX_PRINTBUF_H
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+/*
+ * Printbufs: String buffer for outputting (printing) to, for vsnprintf
+ */
+
+struct printbuf {
+	char			*buf;
+	unsigned		size;
+	unsigned		pos;
+};
+
+/*
+ * Returns size remaining of output buffer:
+ */
+static inline unsigned printbuf_remaining_size(struct printbuf *out)
+{
+	return out->pos < out->size ? out->size - out->pos : 0;
+}
+
+/*
+ * Returns number of characters we can print to the output buffer - i.e.
+ * excluding the terminating nul:
+ */
+static inline unsigned printbuf_remaining(struct printbuf *out)
+{
+	return out->pos < out->size ? out->size - out->pos - 1 : 0;
+}
+
+static inline unsigned printbuf_written(struct printbuf *out)
+{
+	return min(out->pos, out->size);
+}
+
+/*
+ * Returns true if output was truncated:
+ */
+static inline bool printbuf_overflowed(struct printbuf *out)
+{
+	return 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 __prt_char(struct printbuf *out, char c)
+{
+	if (printbuf_remaining(out))
+		out->buf[out->pos] = c;
+	out->pos++;
+}
+
+static inline void prt_char(struct printbuf *out, char c)
+{
+	__prt_char(out, c);
+	printbuf_nul_terminate(out);
+}
+
+static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
+{
+	unsigned i, can_print = min(n, printbuf_remaining(out));
+
+	for (i = 0; i < can_print; i++)
+		out->buf[out->pos++] = c;
+	out->pos += n - can_print;
+}
+
+static inline void prt_chars(struct printbuf *out, char c, unsigned n)
+{
+	__prt_chars(out, c, n);
+	printbuf_nul_terminate(out);
+}
+
+static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
+{
+	unsigned i, can_print = min(n, printbuf_remaining(out));
+
+	for (i = 0; i < can_print; i++)
+		out->buf[out->pos++] = ((char *) b)[i];
+	out->pos += n - can_print;
+
+	printbuf_nul_terminate(out);
+}
+
+static inline void prt_str(struct printbuf *out, const char *str)
+{
+	prt_bytes(out, str, strlen(str));
+}
+
+static inline void prt_hex_byte(struct printbuf *out, u8 byte)
+{
+	__prt_char(out, hex_asc_hi(byte));
+	__prt_char(out, hex_asc_lo(byte));
+	printbuf_nul_terminate(out);
+}
+
+static inline void prt_hex_byte_upper(struct printbuf *out, u8 byte)
+{
+	__prt_char(out, hex_asc_upper_hi(byte));
+	__prt_char(out, hex_asc_upper_lo(byte));
+	printbuf_nul_terminate(out);
+}
+
+#define PRINTBUF_EXTERN(_buf, _size)			\
+((struct printbuf) {					\
+	.buf	= _buf,					\
+	.size	= _size,				\
+})
+
+#endif /* _LINUX_PRINTBUF_H */
-- 
2.36.1


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

* [PATCH v4 02/34] lib/string_helpers: Convert string_escape_mem() to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 01/34] lib/printbuf: New data structure for printing strings Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 03/34] vsprintf: Convert " Kent Overstreet
                   ` (34 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

Like the upcoming vsprintf.c conversion, this converts string_escape_mem
to prt_escaped_string(), which uses and outputs to a printbuf, and makes
string_escape_mem() a smaller wrapper to support existing users.

The new printbuf helpers greatly simplify the code.

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

diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h
index 4d72258d42..67de398944 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 */
@@ -62,6 +63,8 @@ static inline int string_unescape_any_inplace(char *buf)
 
 #define ESCAPE_ALL_MASK		GENMASK(8, 0)
 
+void prt_escaped_string(struct printbuf *out, const char *src, size_t isz,
+			unsigned int flags, const char *only);
 int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
 		unsigned int flags, const char *only);
 
@@ -71,6 +74,7 @@ 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);
 }
 
+
 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 4f877e9551..167c31f377 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>
@@ -301,19 +302,14 @@ int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
 }
 EXPORT_SYMBOL(string_unescape);
 
-static bool escape_passthrough(unsigned char c, char **dst, char *end)
+static bool escape_passthrough(struct printbuf *out, unsigned char c)
 {
-	char *out = *dst;
-
-	if (out < end)
-		*out = c;
-	*dst = out + 1;
+	prt_char(out, c);
 	return true;
 }
 
-static bool escape_space(unsigned char c, char **dst, char *end)
+static bool escape_space(struct printbuf *out, unsigned char c)
 {
-	char *out = *dst;
 	unsigned char to;
 
 	switch (c) {
@@ -336,20 +332,13 @@ static bool escape_space(unsigned char c, char **dst, char *end)
 		return false;
 	}
 
-	if (out < end)
-		*out = '\\';
-	++out;
-	if (out < end)
-		*out = to;
-	++out;
-
-	*dst = out;
+	prt_char(out, '\\');
+	prt_char(out, to);
 	return true;
 }
 
-static bool escape_special(unsigned char c, char **dst, char *end)
+static bool escape_special(struct printbuf *out, unsigned char c)
 {
-	char *out = *dst;
 	unsigned char to;
 
 	switch (c) {
@@ -369,83 +358,43 @@ static bool escape_special(unsigned char c, char **dst, char *end)
 		return false;
 	}
 
-	if (out < end)
-		*out = '\\';
-	++out;
-	if (out < end)
-		*out = to;
-	++out;
-
-	*dst = out;
+	prt_char(out, '\\');
+	prt_char(out, to);
 	return true;
 }
 
-static bool escape_null(unsigned char c, char **dst, char *end)
+static bool escape_null(struct printbuf *out, unsigned char c)
 {
-	char *out = *dst;
-
 	if (c)
 		return false;
 
-	if (out < end)
-		*out = '\\';
-	++out;
-	if (out < end)
-		*out = '0';
-	++out;
-
-	*dst = out;
+	prt_char(out, '\\');
+	prt_char(out, '0');
 	return true;
 }
 
-static bool escape_octal(unsigned char c, char **dst, char *end)
+static bool escape_octal(struct printbuf *out, unsigned char c)
 {
-	char *out = *dst;
-
-	if (out < end)
-		*out = '\\';
-	++out;
-	if (out < end)
-		*out = ((c >> 6) & 0x07) + '0';
-	++out;
-	if (out < end)
-		*out = ((c >> 3) & 0x07) + '0';
-	++out;
-	if (out < end)
-		*out = ((c >> 0) & 0x07) + '0';
-	++out;
-
-	*dst = out;
+	prt_char(out, '\\');
+	prt_char(out, ((c >> 6) & 0x07) + '0');
+	prt_char(out, ((c >> 3) & 0x07) + '0');
+	prt_char(out, ((c >> 0) & 0x07) + '0');
 	return true;
 }
 
-static bool escape_hex(unsigned char c, char **dst, char *end)
+static bool escape_hex(struct printbuf *out, unsigned char c)
 {
-	char *out = *dst;
-
-	if (out < end)
-		*out = '\\';
-	++out;
-	if (out < end)
-		*out = 'x';
-	++out;
-	if (out < end)
-		*out = hex_asc_hi(c);
-	++out;
-	if (out < end)
-		*out = hex_asc_lo(c);
-	++out;
-
-	*dst = out;
+	prt_char(out, '\\');
+	prt_char(out, 'x');
+	prt_hex_byte(out, c);
 	return true;
 }
 
 /**
- * string_escape_mem - quote characters in the given memory buffer
+ * prt_escaped_string - quote characters in the given memory buffer
+ * @out:	printbuf to output to (escaped)
  * @src:	source buffer (unescaped)
  * @isz:	source buffer size
- * @dst:	destination buffer (escaped)
- * @osz:	destination buffer size
  * @flags:	combination of the flags
  * @only:	NULL-terminated string containing characters used to limit
  *		the selected escape class. If characters are included in @only
@@ -510,18 +459,11 @@ static bool escape_hex(unsigned char c, char **dst, char *end)
  * or %ESCAPE_HEX, because they cover most of the other character classes.
  * %ESCAPE_NAP can utilize %ESCAPE_SPACE or %ESCAPE_SPECIAL in addition to
  * the above.
- *
- * Return:
- * The total size of the escaped output that would be generated for
- * the given input and flags. To check whether the output was
- * truncated, compare the return value to osz. There is room left in
- * dst for a '\0' terminator if and only if ret < osz.
  */
-int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
-		      unsigned int flags, const char *only)
+void prt_escaped_string(struct printbuf *out,
+			const char *src, size_t isz,
+			unsigned int flags, const char *only)
 {
-	char *p = dst;
-	char *end = p + osz;
 	bool is_dict = only && *only;
 	bool is_append = flags & ESCAPE_APPEND;
 
@@ -549,41 +491,126 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
 		 * %ESCAPE_NA cases.
 		 */
 		if (!(is_append || in_dict) && is_dict &&
-					  escape_passthrough(c, &p, end))
+		    escape_passthrough(out, c))
 			continue;
 
 		if (!(is_append && in_dict) && isascii(c) && isprint(c) &&
-		    flags & ESCAPE_NAP && escape_passthrough(c, &p, end))
+		    flags & ESCAPE_NAP && escape_passthrough(out, c))
 			continue;
 
 		if (!(is_append && in_dict) && isprint(c) &&
-		    flags & ESCAPE_NP && escape_passthrough(c, &p, end))
+		    flags & ESCAPE_NP && escape_passthrough(out, c))
 			continue;
 
 		if (!(is_append && in_dict) && isascii(c) &&
-		    flags & ESCAPE_NA && escape_passthrough(c, &p, end))
+		    flags & ESCAPE_NA && escape_passthrough(out, c))
 			continue;
 
-		if (flags & ESCAPE_SPACE && escape_space(c, &p, end))
+		if (flags & ESCAPE_SPACE && escape_space(out, c))
 			continue;
 
-		if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end))
+		if (flags & ESCAPE_SPECIAL && escape_special(out, c))
 			continue;
 
-		if (flags & ESCAPE_NULL && escape_null(c, &p, end))
+		if (flags & ESCAPE_NULL && escape_null(out, c))
 			continue;
 
 		/* ESCAPE_OCTAL and ESCAPE_HEX always go last */
-		if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))
+		if (flags & ESCAPE_OCTAL && escape_octal(out, c))
 			continue;
 
-		if (flags & ESCAPE_HEX && escape_hex(c, &p, end))
+		if (flags & ESCAPE_HEX && escape_hex(out, c))
 			continue;
 
-		escape_passthrough(c, &p, end);
+		escape_passthrough(out, c);
 	}
+}
+EXPORT_SYMBOL(prt_escaped_string);
+
+/**
+ * string_escape_mem - quote characters in the given memory buffer
+ * @src:	source buffer (unescaped)
+ * @isz:	source buffer size
+ * @dst:	destination buffer (escaped)
+ * @osz:	destination buffer size
+ * @flags:	combination of the flags
+ * @only:	NULL-terminated string containing characters used to limit
+ *		the selected escape class. If characters are included in @only
+ *		that would not normally be escaped by the classes selected
+ *		in @flags, they will be copied to @dst unescaped.
+ *
+ * Description:
+ * The process of escaping byte buffer includes several parts. They are applied
+ * in the following sequence.
+ *
+ *	1. The character is not matched to the one from @only string and thus
+ *	   must go as-is to the output.
+ *	2. The character is matched to the printable and ASCII classes, if asked,
+ *	   and in case of match it passes through to the output.
+ *	3. The character is matched to the printable or ASCII class, if asked,
+ *	   and in case of match it passes through to the output.
+ *	4. The character is checked if it falls into the class given by @flags.
+ *	   %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
+ *	   character. Note that they actually can't go together, otherwise
+ *	   %ESCAPE_HEX will be ignored.
+ *
+ * Caller must provide valid source and destination pointers. Be aware that
+ * destination buffer will not be NULL-terminated, thus caller have to append
+ * it if needs. The supported flags are::
+ *
+ *	%ESCAPE_SPACE: (special white space, not space itself)
+ *		'\f' - form feed
+ *		'\n' - new line
+ *		'\r' - carriage return
+ *		'\t' - horizontal tab
+ *		'\v' - vertical tab
+ *	%ESCAPE_SPECIAL:
+ *		'\"' - double quote
+ *		'\\' - backslash
+ *		'\a' - alert (BEL)
+ *		'\e' - escape
+ *	%ESCAPE_NULL:
+ *		'\0' - null
+ *	%ESCAPE_OCTAL:
+ *		'\NNN' - byte with octal value NNN (3 digits)
+ *	%ESCAPE_ANY:
+ *		all previous together
+ *	%ESCAPE_NP:
+ *		escape only non-printable characters, checked by isprint()
+ *	%ESCAPE_ANY_NP:
+ *		all previous together
+ *	%ESCAPE_HEX:
+ *		'\xHH' - byte with hexadecimal value HH (2 digits)
+ *	%ESCAPE_NA:
+ *		escape only non-ascii characters, checked by isascii()
+ *	%ESCAPE_NAP:
+ *		escape only non-printable or non-ascii characters
+ *	%ESCAPE_APPEND:
+ *		append characters from @only to be escaped by the given classes
+ *
+ * %ESCAPE_APPEND would help to pass additional characters to the escaped, when
+ * one of %ESCAPE_NP, %ESCAPE_NA, or %ESCAPE_NAP is provided.
+ *
+ * One notable caveat, the %ESCAPE_NAP, %ESCAPE_NP and %ESCAPE_NA have the
+ * higher priority than the rest of the flags (%ESCAPE_NAP is the highest).
+ * It doesn't make much sense to use either of them without %ESCAPE_OCTAL
+ * or %ESCAPE_HEX, because they cover most of the other character classes.
+ * %ESCAPE_NAP can utilize %ESCAPE_SPACE or %ESCAPE_SPECIAL in addition to
+ * the above.
+ *
+ * Return:
+ * The total size of the escaped output that would be generated for
+ * the given input and flags. To check whether the output was
+ * truncated, compare the return value to osz. There is room left in
+ * dst for a '\0' terminator if and only if ret < osz.
+ */
+int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
+		      unsigned int flags, const char *only)
+{
+	struct printbuf out = PRINTBUF_EXTERN(dst, osz);
 
-	return p - dst;
+	prt_escaped_string(&out, src, isz, flags, only);
+	return out.pos;
 }
 EXPORT_SYMBOL(string_escape_mem);
 
-- 
2.36.1


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

* [PATCH v4 03/34] vsprintf: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 01/34] lib/printbuf: New data structure for printing strings Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 02/34] lib/string_helpers: Convert string_escape_mem() to printbuf Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 04/34] lib/hexdump: " Kent Overstreet
                   ` (33 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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:
 - prt_printf(), which is like snprintf but outputs to a printbuf
 - prt_vprintf, like vsnprintf

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

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/kernel.h |    4 +
 include/linux/string.h |    5 +
 lib/vsprintf.c         | 1367 +++++++++++++++++++---------------------
 3 files changed, 643 insertions(+), 733 deletions(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index fe6efb24d1..5c4f4b6d36 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -207,6 +207,10 @@ extern int num_to_str(char *buf, int size,
 
 /* lib/printf utilities */
 
+struct printbuf;
+extern __printf(2, 3) void prt_printf(struct printbuf *out, const char *fmt, ...);
+extern __printf(2, 0) void prt_vprintf(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.h b/include/linux/string.h
index b6572aeca2..0a737d5b92 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -195,7 +195,12 @@ int __sysfs_match_string(const char * const *array, size_t n, const char *s);
  */
 #define sysfs_match_string(_a, _s) __sysfs_match_string(_a, ARRAY_SIZE(_a), _s)
 
+struct printbuf;
+
 #ifdef CONFIG_BINARY_PRINTF
+void prt_vbinprintf(struct printbuf *out, const char *fmt, va_list args);
+void prt_bstrprintf(struct printbuf *out, const char *fmt, const u32 *bin_buf);
+void prt_bprintf(struct printbuf *out, const char *fmt, ...) __printf(2, 3);
 int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args);
 int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf);
 int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 40d26a07a1..7b24714674 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 */
 
@@ -451,8 +452,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);
@@ -512,67 +513,43 @@ char *number(char *buf, char *end, unsigned long long num,
 	if (i > precision)
 		precision = i;
 	/* leading space padding */
-	field_width -= precision;
-	if (!(spec.flags & (ZEROPAD | LEFT))) {
-		while (--field_width >= 0) {
-			if (buf < end)
-				*buf = ' ';
-			++buf;
-		}
+	field_width = max(0, field_width - precision);
+	if (!(spec.flags & (ZEROPAD | LEFT)) && field_width) {
+		__prt_chars(out, ' ', field_width);
+		field_width = 0;
 	}
 	/* sign */
-	if (sign) {
-		if (buf < end)
-			*buf = sign;
-		++buf;
-	}
+	if (sign)
+		__prt_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)
+			__prt_char(out, '0');
+		if (spec.base == 16)
+			__prt_char(out, 'X' | locase);
 	}
 	/* zero or space padding */
-	if (!(spec.flags & LEFT)) {
+	if (!(spec.flags & LEFT) && field_width) {
 		char c = ' ' + (spec.flags & ZEROPAD);
 
-		while (--field_width >= 0) {
-			if (buf < end)
-				*buf = c;
-			++buf;
-		}
+		__prt_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)
+		__prt_chars(out, '0', precision - i);
 	/* actual digits of result */
-	while (--i >= 0) {
-		if (buf < end)
-			*buf = tmp[i];
-		++buf;
-	}
+	while (--i >= 0)
+		__prt_char(out, tmp[i]);
 	/* trailing space padding */
-	while (--field_width >= 0) {
-		if (buf < end)
-			*buf = ' ';
-		++buf;
-	}
+	if (field_width)
+		__prt_chars(out, ' ', field_width);
 
-	return buf;
+	printbuf_nul_terminate(out);
 }
 
 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;
 
@@ -582,25 +559,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;
 }
 
 /*
@@ -612,67 +592,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
+		prt_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 = strnlen(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);
+	prt_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
@@ -682,7 +650,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);
 }
 
 /*
@@ -701,14 +669,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;
 	}
 
@@ -716,18 +685,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;
@@ -736,7 +706,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. */
@@ -825,8 +795,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;
@@ -837,47 +808,49 @@ 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);
 }
 
-static char *default_pointer(char *buf, char *end, const void *ptr,
-			     struct printf_spec spec)
+static void default_pointer(struct printbuf *out,
+			    const void *ptr,
+			    struct printf_spec spec)
 {
 	/*
 	 * default is to _not_ leak addresses, so hash before printing,
 	 * 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);
 
-	return ptr_to_id(buf, end, ptr, spec);
+	return ptr_to_id(out, ptr, 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 default_pointer(buf, end, ptr, spec);
+		return default_pointer(out, ptr, spec);
 	case 1: {
 		const struct cred *cred;
 
@@ -888,7 +861,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);
 		}
 
 		/*
@@ -914,12 +887,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;
@@ -936,9 +910,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);
@@ -951,7 +925,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)
@@ -959,49 +933,47 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
 			c = '/';
 			s = array[--i];
 		}
-		if (buf < end)
-			*buf = c;
+		prt_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]))
+			prt_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
@@ -1024,9 +996,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
 }
 
@@ -1061,8 +1033,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
@@ -1101,69 +1073,67 @@ 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++ = '[';
+	prt_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);
+			prt_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';
+	prt_char(&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 */
@@ -1171,10 +1141,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':
@@ -1195,34 +1165,27 @@ 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;
-		}
+		__prt_char(out, hex_asc_hi(addr[i]));
+		__prt_char(out, hex_asc_lo(addr[i]));
+
+		if (separator && i != len - 1)
+			__prt_char(out, separator);
 	}
 
-	return buf;
+	printbuf_nul_terminate(out);
 }
 
 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 };
@@ -1241,54 +1204,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)
+			prt_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)
+			prt_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);
+		prt_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;
@@ -1296,8 +1250,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':
@@ -1324,11 +1278,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');
@@ -1361,24 +1316,21 @@ 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';
+				prt_char(out, '0');
 			if (digits < 2)
-				*p++ = '0';
+				prt_char(out, '0');
 		}
 		/* reverse the digits in the quad */
 		while (digits--)
-			*p++ = temp[digits];
+			prt_char(out, temp[digits]);
 		if (i < 3)
-			*p++ = '.';
+			prt_char(out, '.');
 		index += step;
 	}
-	*p = '\0';
-
-	return p;
 }
 
 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];
@@ -1422,14 +1374,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++ = ':';
+				__prt_char(out, ':');
+			__prt_char(out, ':');
 			needcolon = false;
 			i += longest - 1;
 			continue;
 		}
 		if (needcolon) {
-			*p++ = ':';
+			__prt_char(out, ':');
 			needcolon = false;
 		}
 		/* hex u16 without leading 0s */
@@ -1438,81 +1390,79 @@ char *ip6_compressed_string(char *p, const char *addr)
 		lo = word & 0xff;
 		if (hi) {
 			if (hi > 0x0f)
-				p = hex_byte_pack(p, hi);
+				prt_hex_byte(out, hi);
 			else
-				*p++ = hex_asc_lo(hi);
-			p = hex_byte_pack(p, lo);
+				__prt_char(out, hex_asc_lo(hi));
+			prt_hex_byte(out, lo);
 		}
 		else if (lo > 0x0f)
-			p = hex_byte_pack(p, lo);
+			prt_hex_byte(out, lo);
 		else
-			*p++ = hex_asc_lo(lo);
+			__prt_char(out, hex_asc_lo(lo));
 		needcolon = true;
 	}
 
 	if (useIPv4) {
 		if (needcolon)
-			*p++ = ':';
-		p = ip4_string(p, &in6.s6_addr[12], "I4");
+			__prt_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++);
+		prt_hex_byte(out, *addr++);
+		prt_hex_byte(out, *addr++);
 		if (fmt[0] == 'I' && i != 7)
-			*p++ = ':';
+			prt_char(out, ':');
 	}
-	*p = '\0';
-
-	return p;
 }
 
 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)) {
@@ -1532,44 +1482,42 @@ 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)
+		prt_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++ = ']';
+		prt_char(&ip6_addr, ']');
 
 	if (have_p) {
-		*p++ = ':';
-		p = number(p, pend, ntohs(sa->sin6_port), spec);
+		prt_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);
+		prt_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);
+		prt_char(&ip6_addr, '%');
+		number(&ip6_addr, sa->sin6_scope_id, spec);
 	}
-	*p = '\0';
 
-	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 };
 
@@ -1588,30 +1536,29 @@ 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);
+		prt_char(&ip4_addr, ':');
+		number(&ip4_addr, ntohs(sa->sin_port), spec);
 	}
-	*p = '\0';
 
-	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;
@@ -1621,21 +1568,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;
@@ -1643,10 +1590,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++]) {
@@ -1681,44 +1628,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;
+	prt_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);
+	prt_vprintf(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':
@@ -1734,60 +1672,58 @@ 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]]);
+			prt_hex_byte_upper(&uuid, addr[index[i]]);
 		else
-			p = hex_byte_pack(p, addr[index[i]]);
+			prt_hex_byte(&uuid, addr[index[i]]);
 		switch (i) {
 		case 3:
 		case 5:
 		case 7:
 		case 9:
-			*p++ = '-';
+			prt_char(&uuid, '-');
 			break;
 		}
 	}
 
-	*p = 0;
-
-	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 orig, 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;
 
 	orig = get_unaligned(fourcc);
 	val = orig & ~BIT(31);
@@ -1796,31 +1732,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 : '.';
+		prt_char(&output, isascii(c) && isprint(c) ? c : '.');
 	}
 
-	*p++ = ' ';
-	strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian");
-	p += strlen(p);
+	prt_char(&output, ' ');
+	prt_str(&output, orig & BIT(31) ? "big-endian" : "little-endian");
 
-	*p++ = ' ';
-	*p++ = '(';
-	p = special_hex_number(p, output + sizeof(output) - 2, orig, sizeof(u32));
-	*p++ = ')';
-	*p = '\0';
+	prt_char(&output, ' ');
+	prt_char(&output, '(');
+	special_hex_number(&output, orig, sizeof(u32));
+	prt_char(&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':
@@ -1834,55 +1768,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);
+	prt_char(out, '-');
+	number(out, mon, default_dec02_spec);
+	prt_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);
+	prt_char(out, ':');
+	number(out, tm->tm_min, default_dec02_spec);
+	prt_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':
@@ -1910,21 +1833,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)
+		prt_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;
@@ -1942,47 +1860,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;
 
@@ -1991,20 +1910,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)
+			prt_char(out, '|');
 	}
 
 	if (flags)
-		buf = number(buf, end, flags, default_flag_spec);
-
-	return buf;
+		number(out, flags, default_flag_spec);
 }
 
 struct page_flags_fields {
@@ -2029,20 +1943,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);
+	prt_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;
 	}
 
@@ -2053,41 +1965,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)
+			prt_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);
+		prt_char(out, '=');
+		number(out, (flags >> pff[i].shift) & pff[i].mask, *pff[i].spec);
 
 		append = true;
 	}
-	if (buf < end)
-		*buf = ')';
-	buf++;
-
-	return buf;
+	prt_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;
@@ -2097,15 +1999,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;
 
@@ -2114,25 +2016,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;
 
@@ -2140,13 +2040,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++;
@@ -2155,32 +2055,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)
+			prt_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' : '-';
@@ -2188,21 +2084,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;
 			}
@@ -2212,37 +2108,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);
 }
 
 int __init no_hash_pointers_enable(char *str)
@@ -2398,8 +2295,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':
@@ -2407,24 +2304,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
@@ -2434,57 +2331,57 @@ 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))
-			return default_pointer(buf, end, ptr, spec);
-		return err_ptr(buf, end, ptr, spec);
+			return default_pointer(out, 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);
 		}
 	default:
-		return default_pointer(buf, end, ptr, spec);
+		return default_pointer(out, ptr, spec);
 	}
 }
 
@@ -2706,52 +2603,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
+ * prt_vprintf - 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.
- *
- * **Please update the documentation in both places when making changes**
+ * prt_vprintf works much like the traditional vsnprintf(), but outputs to a
+ * printbuf instead of raw pointer/size.
  *
- * 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 prt_printf().
  *
- * 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 prt_vprintf(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;
@@ -2760,16 +2632,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:
+			prt_bytes(out, old_fmt, read);
 			break;
-		}
 
 		case FORMAT_TYPE_WIDTH:
 			set_field_width(&spec, va_arg(args, int));
@@ -2779,44 +2644,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))
+				prt_chars(out, spec.field_width, ' ');
 
-			if (!(spec.flags & LEFT)) {
-				while (--spec.field_width > 0) {
-					if (str < end)
-						*str = ' ';
-					++str;
+			__prt_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))
+				prt_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;
+			__prt_char(out, '%');
 			break;
 
 		case FORMAT_TYPE_INVALID:
@@ -2869,21 +2719,70 @@ 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(prt_vprintf);
 
-	/* the trailing null byte doesn't count towards the total */
-	return str-buf;
+/**
+ * prt_printf - 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
+ *
+ *
+ * prt_printf 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 prt_printf(struct printbuf *out, const char *fmt, ...)
+{
+	va_list args;
 
+	va_start(args, fmt);
+	prt_vprintf(out, fmt, args);
+	va_end(args);
+}
+EXPORT_SYMBOL(prt_printf);
+
+/**
+ * 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);
+
+	prt_vprintf(&out, fmt, args);
+	return out.pos;
 }
 EXPORT_SYMBOL(vsnprintf);
 
@@ -3021,53 +2920,46 @@ 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
- * @size: The size of the buffer(by words(32bits), not characters)
+ * prt_vbinprintf - Parse a format string and place args' binary value in a buffer
+ * @out: The buffer to place args' binary value
  * @fmt: The format string to use
  * @args: Arguments for the format string
  *
  * The format follows C99 vsnprintf, except %n is ignored, and its argument
  * is skipped.
  *
- * The return value is the number of words(32bits) which would be generated for
- * the given input.
- *
  * NOTE:
  * If the return value is greater than @size, the resulting bin_buf is NOT
  * valid for bstr_printf().
  */
-int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
+void prt_vbinprintf(struct printbuf *out, const char *fmt, va_list args)
 {
 	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));			\
+		prt_bytes(out, (u32 *) &val8, 4);			\
+		prt_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));			\
+		prt_bytes(out, &val4, sizeof(type));			\
 		value = (unsigned long long)val4;			\
 	}								\
-	str += sizeof(type);						\
 	value;								\
 })
 
@@ -3098,16 +2990,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;
+			prt_str(out, save_str);
 			break;
 		}
 
@@ -3127,12 +3015,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))
@@ -3170,15 +3053,15 @@ 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);
 #undef save_arg
 }
-EXPORT_SYMBOL_GPL(vbin_printf);
+EXPORT_SYMBOL_GPL(prt_vbinprintf);
 
 /**
- * bstr_printf - Format a string from binary arguments and place it in a buffer
+ * prt_bstrprintf - Format a string from binary arguments 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
  * @bin_buf: Binary arguments for the format string
  *
@@ -3188,26 +3071,14 @@ EXPORT_SYMBOL_GPL(vbin_printf);
  *
  * The format follows C99 vsnprintf, but has some extensions:
  *  see vsnprintf comment for details.
- *
- * 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.
  */
-int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
+void prt_bstrprintf(struct printbuf *out, const char *fmt, const u32 *bin_buf)
 {
 	struct printf_spec spec = {0};
-	char *str, *end;
 	const char *args = (const char *)bin_buf;
 
-	if (WARN_ON_ONCE(size > INT_MAX))
-		return 0;
-
-	str = buf;
-	end = buf + size;
+	if (WARN_ON_ONCE(out->size > INT_MAX))
+		return;
 
 #define get_arg(type)							\
 ({									\
@@ -3224,12 +3095,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);
@@ -3237,16 +3102,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:
+			prt_bytes(out, old_fmt, read);
 			break;
-		}
 
 		case FORMAT_TYPE_WIDTH:
 			set_field_width(&spec, get_arg(int));
@@ -3256,38 +3114,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))
+				prt_chars(out, spec.field_width, ' ');
+			__prt_char(out, (unsigned char) get_arg(char));
+			if ((spec.flags & LEFT))
+				prt_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':
@@ -3303,17 +3147,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);
+				prt_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++;
@@ -3321,9 +3160,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;
+			__prt_char(out, '%');
 			break;
 
 		case FORMAT_TYPE_INVALID:
@@ -3366,23 +3203,87 @@ 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
+	printbuf_nul_terminate(out);
+}
+EXPORT_SYMBOL_GPL(prt_bstrprintf);
+
+/**
+ * prt_bprintf - Parse a format string and place args' binary value in a buffer
+ * @out: The buffer to place args' binary value
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ */
+void prt_bprintf(struct printbuf *out, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	prt_vbinprintf(out, fmt, args);
+	va_end(args);
+}
+EXPORT_SYMBOL_GPL(prt_bprintf);
+
+/**
+ * vbin_printf - Parse a format string and place args' binary value in a buffer
+ * @bin_buf: The buffer to place args' binary value
+ * @size: The size of the buffer(by words(32bits), not characters)
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The format follows C99 vsnprintf, except %n is ignored, and its argument
+ * is skipped.
+ *
+ * The return value is the number of words(32bits) which would be generated for
+ * the given input.
+ *
+ * NOTE:
+ * If the return value is greater than @size, the resulting bin_buf is NOT
+ * valid for bstr_printf().
+ */
+int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
+{
+	struct printbuf out = PRINTBUF_EXTERN((char *) bin_buf, size);
+
+	prt_vbinprintf(&out, fmt, args);
+	return out.pos;
+}
+EXPORT_SYMBOL_GPL(vbin_printf);
+
+/**
+ * bstr_printf - Format a string from binary arguments 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
+ * @bin_buf: Binary arguments for the format string
+ *
+ * This function like C99 vsnprintf, but the difference is that vsnprintf gets
+ * arguments from stack, and bstr_printf gets arguments from @bin_buf which is
+ * a binary buffer that generated by vbin_printf.
+ *
+ * The format follows C99 vsnprintf, but has some extensions:
+ *  see vsnprintf comment for details.
+ *
+ * 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.
+ */
+int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
+{
+	struct printbuf out = PRINTBUF_EXTERN(buf, size);
 
-	/* the trailing null byte doesn't count towards the total */
-	return str - buf;
+	prt_bstrprintf(&out, fmt, bin_buf);
+	return out.pos;
 }
 EXPORT_SYMBOL_GPL(bstr_printf);
 
-- 
2.36.1


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

* [PATCH v4 04/34] lib/hexdump: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (2 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 03/34] vsprintf: Convert " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 05/34] vsprintf: %pf(%p) Kent Overstreet
                   ` (32 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

This converts most of the hexdump code to printbufs, along with some
significant cleanups and a bit of reorganization. The old non-printbuf
functions are mostly left as wrappers around the new printbuf versions.

Big note: byte swabbing behaviour

Previously, hex_dump_to_buffer() would byteswab the groups of bytes
being printed on little endian machines. This behaviour is... not
standard or typical for a hex dumper, and this behaviour was silently
added/changed without documentation (in 2007).

Given that the hex dumpers are just used for debugging output, nothing
is likely to break, and hopefully by reverting to more standard
behaviour the end result will be _less_ confusion, modulo a few kernel
developers who will certainly be annoyed by their tools changing.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/kernel.h |   6 +
 lib/hexdump.c          | 246 ++++++++++++++++++++++++-----------------
 lib/test_hexdump.c     |  30 +----
 3 files changed, 159 insertions(+), 123 deletions(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 5c4f4b6d36..1906861ece 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -293,6 +293,12 @@ extern int hex_to_bin(unsigned char ch);
 extern int __must_check hex2bin(u8 *dst, const char *src, size_t count);
 extern char *bin2hex(char *dst, const void *src, size_t count);
 
+struct printbuf;
+void prt_hex_bytes(struct printbuf *, const void *, unsigned, unsigned, unsigned);
+void prt_hex_line(struct printbuf *, const void *, size_t, int, int, bool);
+void prt_hex_dump(struct printbuf *, const void *, size_t,
+		  const char *, int, unsigned, unsigned, bool);
+
 bool mac_pton(const char *s, u8 *mac);
 
 /*
diff --git a/lib/hexdump.c b/lib/hexdump.c
index 06833d4043..9556f15ad2 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/minmax.h>
 #include <linux/export.h>
+#include <linux/printbuf.h>
 #include <asm/unaligned.h>
 
 const char hex_asc[] = "0123456789abcdef";
@@ -79,32 +80,40 @@ int hex2bin(u8 *dst, const char *src, size_t count)
 EXPORT_SYMBOL(hex2bin);
 
 /**
- * bin2hex - convert binary data to an ascii hexadecimal string
- * @dst: ascii hexadecimal result
- * @src: binary data
- * @count: binary data length
+ * prt_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
  */
-char *bin2hex(char *dst, const void *src, size_t count)
+void prt_hex_bytes(struct printbuf *out, const void *buf, unsigned len,
+		   unsigned groupsize, unsigned separator)
 {
-	const unsigned char *_src = src;
+	const u8 *ptr = buf;
+	unsigned i;
 
-	while (count--)
-		dst = hex_byte_pack(dst, *_src++);
-	return dst;
+	if (!groupsize)
+		groupsize = 1;
+
+	for (i = 0; i < len ; ++i) {
+		if (i && separator && !(i % groupsize))
+			__prt_char(out, separator);
+		prt_hex_byte(out, ptr[i]);
+	}
 }
-EXPORT_SYMBOL(bin2hex);
+EXPORT_SYMBOL(prt_hex_bytes);
 
 /**
- * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * prt_hex_line - convert a blob of data to "hex ASCII" in memory
+ * @out: printbuf to output to
  * @buf: data blob to dump
  * @len: number of bytes in the @buf
  * @rowsize: number of bytes to print per line; must be 16 or 32
  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
- * @linebuf: where to put the converted data
- * @linebuflen: total size of @linebuf, including space for terminating NUL
  * @ascii: include ASCII after the hex output
  *
- * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * prt_hex_line() works on one "line" of output at a time, i.e.,
  * 16 or 32 bytes of input data converted to hex + ASCII output.
  *
  * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
@@ -117,22 +126,13 @@ EXPORT_SYMBOL(bin2hex);
  *
  * example output buffer:
  * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
- *
- * Return:
- * The amount of bytes placed in the buffer without terminating NUL. If the
- * output was truncated, then the return value is the number of bytes
- * (excluding the terminating NUL) which would have been written to the final
- * string if enough space had been available.
  */
-int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
-		       char *linebuf, size_t linebuflen, bool ascii)
+void prt_hex_line(struct printbuf *out, const void *buf, size_t len,
+		  int rowsize, int groupsize, bool ascii)
 {
+	unsigned saved_pos = out->pos;
 	const u8 *ptr = buf;
-	int ngroups;
-	u8 ch;
-	int j, lx = 0;
-	int ascii_column;
-	int ret;
+	int i, ngroups;
 
 	if (rowsize != 16 && rowsize != 32)
 		rowsize = 16;
@@ -145,84 +145,127 @@ int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
 		groupsize = 1;
 
 	ngroups = len / groupsize;
-	ascii_column = rowsize * 2 + rowsize / groupsize + 1;
-
-	if (!linebuflen)
-		goto overflow1;
 
 	if (!len)
-		goto nil;
-
-	if (groupsize == 8) {
-		const u64 *ptr8 = buf;
-
-		for (j = 0; j < ngroups; j++) {
-			ret = snprintf(linebuf + lx, linebuflen - lx,
-				       "%s%16.16llx", j ? " " : "",
-				       get_unaligned(ptr8 + j));
-			if (ret >= linebuflen - lx)
-				goto overflow1;
-			lx += ret;
-		}
-	} else if (groupsize == 4) {
-		const u32 *ptr4 = buf;
-
-		for (j = 0; j < ngroups; j++) {
-			ret = snprintf(linebuf + lx, linebuflen - lx,
-				       "%s%8.8x", j ? " " : "",
-				       get_unaligned(ptr4 + j));
-			if (ret >= linebuflen - lx)
-				goto overflow1;
-			lx += ret;
-		}
-	} else if (groupsize == 2) {
-		const u16 *ptr2 = buf;
-
-		for (j = 0; j < ngroups; j++) {
-			ret = snprintf(linebuf + lx, linebuflen - lx,
-				       "%s%4.4x", j ? " " : "",
-				       get_unaligned(ptr2 + j));
-			if (ret >= linebuflen - lx)
-				goto overflow1;
-			lx += ret;
-		}
-	} else {
-		for (j = 0; j < len; j++) {
-			if (linebuflen < lx + 2)
-				goto overflow2;
-			ch = ptr[j];
-			linebuf[lx++] = hex_asc_hi(ch);
-			if (linebuflen < lx + 2)
-				goto overflow2;
-			linebuf[lx++] = hex_asc_lo(ch);
-			if (linebuflen < lx + 2)
-				goto overflow2;
-			linebuf[lx++] = ' ';
+		return;
+
+	prt_hex_bytes(out, ptr, len, groupsize, ' ');
+
+	if (ascii) {
+		unsigned ascii_column = rowsize * 2 + rowsize / groupsize + 1;
+
+		prt_chars(out, ' ', max_t(int, 0, ascii_column - (out->pos - saved_pos)));
+
+		for (i = 0; i < len; i++) {
+			u8 ch = ptr[i];
+			prt_char(out, isascii(ch) && isprint(ch) ? ch : '.');
 		}
-		if (j)
-			lx--;
 	}
-	if (!ascii)
-		goto nil;
+}
+EXPORT_SYMBOL(prt_hex_line);
 
-	while (lx < ascii_column) {
-		if (linebuflen < lx + 2)
-			goto overflow2;
-		linebuf[lx++] = ' ';
-	}
-	for (j = 0; j < len; j++) {
-		if (linebuflen < lx + 2)
-			goto overflow2;
-		ch = ptr[j];
-		linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
+/**
+ * prt_hex_dump - print multiline formatted hex dump
+ * @out: printbuf to output to
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @prefix_str: string to prefix each line with;
+ *  caller supplies trailing spaces for alignment if desired
+ * @prefix_type: controls whether prefix of an offset, address, or none
+ *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @ascii: include ASCII after the hex output
+ *
+ * Function is an analogue of print_hex_dump() and thus has similar interface.
+ *
+ * linebuf size is maximal length for one line.
+ * 32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
+ *	separating space
+ * 2 - spaces separating hex dump and ascii representation
+ * 32 - ascii representation
+ * 1 - terminating '\0'
+ */
+void prt_hex_dump(struct printbuf *out, const void *buf, size_t len,
+		  const char *prefix_str, int prefix_type,
+		  unsigned rowsize, unsigned groupsize, bool ascii)
+{
+	const u8 *ptr = buf;
+	size_t i;
+
+	if (rowsize != 16 && rowsize != 32)
+		rowsize = 16;
+
+	for (i = 0; i < len; i += rowsize) {
+		prt_str(out, prefix_str);
+
+		switch (prefix_type) {
+		case DUMP_PREFIX_ADDRESS:
+			prt_printf(out, "%p: ", ptr + i);
+			break;
+		case DUMP_PREFIX_OFFSET:
+			prt_printf(out, "%.8zx: ", i);
+			break;
+		}
+
+		prt_hex_line(out, ptr + i, min_t(size_t, len - i, rowsize),
+			     rowsize, groupsize, ascii);
+		prt_char(out, '\n');
 	}
-nil:
-	linebuf[lx] = '\0';
-	return lx;
-overflow2:
-	linebuf[lx++] = '\0';
-overflow1:
-	return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
+}
+
+/**
+ * bin2hex - convert binary data to an ascii hexadecimal string
+ * @dst: ascii hexadecimal result
+ * @src: binary data
+ * @count: binary data length
+ */
+char *bin2hex(char *dst, const void *src, size_t count)
+{
+	struct printbuf out = PRINTBUF_EXTERN(dst, count * 4);
+
+	prt_hex_bytes(&out, src, count, 0, 0);
+	return dst + out.pos;
+}
+EXPORT_SYMBOL(bin2hex);
+
+/**
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ * @ascii: include ASCII after the hex output
+ *
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ *
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
+ * to a hex + ASCII dump at the supplied memory location.
+ * The converted output is always NUL-terminated.
+ *
+ * E.g.:
+ *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
+ *			linebuf, sizeof(linebuf), true);
+ *
+ * example output buffer:
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
+ *
+ * Return:
+ * The amount of bytes placed in the buffer without terminating NUL. If the
+ * output was truncated, then the return value is the number of bytes
+ * (excluding the terminating NUL) which would have been written to the final
+ * string if enough space had been available.
+ */
+int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
+		       char *linebuf, size_t linebuflen, bool ascii)
+{
+	struct printbuf out = PRINTBUF_EXTERN(linebuf, linebuflen);
+
+	prt_hex_line(&out, buf, len, rowsize, groupsize, ascii);
+	return out.pos;
 }
 EXPORT_SYMBOL(hex_dump_to_buffer);
 
@@ -262,6 +305,11 @@ void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
 		    int rowsize, int groupsize,
 		    const void *buf, size_t len, bool ascii)
 {
+	/*
+	 * XXX: this code does the exact same thing as prt_hex_dump(): we should
+	 * be able to call that and printk() the result, except printk is
+	 * restricted to 1024 bytes of output per call
+	 */
 	const u8 *ptr = buf;
 	int i, linelen, remaining = len;
 	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
index 5144899d3c..f9e97879dc 100644
--- a/lib/test_hexdump.c
+++ b/lib/test_hexdump.c
@@ -25,36 +25,19 @@ static const char * const test_data_1[] __initconst = {
 	"4c", "d1", "19", "99", "43", "b1", "af", "0c",
 };
 
-static const char * const test_data_2_le[] __initconst = {
-	"32be", "7bdb", "180a", "b293",
-	"ba70", "24c4", "837d", "9b34",
-	"9ca6", "ad31", "0f9c", "e9ac",
-	"d14c", "9919", "b143", "0caf",
-};
-
-static const char * const test_data_2_be[] __initconst = {
+static const char * const test_data_2[] __initconst = {
 	"be32", "db7b", "0a18", "93b2",
 	"70ba", "c424", "7d83", "349b",
 	"a69c", "31ad", "9c0f", "ace9",
 	"4cd1", "1999", "43b1", "af0c",
 };
 
-static const char * const test_data_4_le[] __initconst = {
-	"7bdb32be", "b293180a", "24c4ba70", "9b34837d",
-	"ad319ca6", "e9ac0f9c", "9919d14c", "0cafb143",
-};
-
-static const char * const test_data_4_be[] __initconst = {
+static const char * const test_data_4[] __initconst = {
 	"be32db7b", "0a1893b2", "70bac424", "7d83349b",
 	"a69c31ad", "9c0face9", "4cd11999", "43b1af0c",
 };
 
-static const char * const test_data_8_le[] __initconst = {
-	"b293180a7bdb32be", "9b34837d24c4ba70",
-	"e9ac0f9cad319ca6", "0cafb1439919d14c",
-};
-
-static const char * const test_data_8_be[] __initconst = {
+static const char * const test_data_8[] __initconst = {
 	"be32db7b0a1893b2", "70bac4247d83349b",
 	"a69c31ad9c0face9", "4cd1199943b1af0c",
 };
@@ -73,7 +56,6 @@ static void __init test_hexdump_prepare_test(size_t len, int rowsize,
 	size_t l = len;
 	int gs = groupsize, rs = rowsize;
 	unsigned int i;
-	const bool is_be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN);
 
 	if (rs != 16 && rs != 32)
 		rs = 16;
@@ -85,11 +67,11 @@ static void __init test_hexdump_prepare_test(size_t len, int rowsize,
 		gs = 1;
 
 	if (gs == 8)
-		result = is_be ? test_data_8_be : test_data_8_le;
+		result = test_data_8;
 	else if (gs == 4)
-		result = is_be ? test_data_4_be : test_data_4_le;
+		result = test_data_4;
 	else if (gs == 2)
-		result = is_be ? test_data_2_be : test_data_2_le;
+		result = test_data_2;
 	else
 		result = test_data_1;
 
-- 
2.36.1


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

* [PATCH v4 05/34] vsprintf: %pf(%p)
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (3 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 04/34] lib/hexdump: " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-21  7:04   ` Rasmus Villemoes
  2022-06-20  0:42 ` [PATCH v4 06/34] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
                   ` (31 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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:

 %pf(%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>
Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 Documentation/core-api/printk-formats.rst | 22 ++++++
 lib/test_printf.c                         | 27 ++++++++
 lib/vsprintf.c                            | 83 ++++++++++++++++++++++-
 3 files changed, 130 insertions(+), 2 deletions(-)

diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index 5e89497ba3..8fc0b62af1 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -625,6 +625,28 @@ Examples::
 	%p4cc	Y10  little-endian (0x20303159)
 	%p4cc	NV12 big-endian (0xb231564e)
 
+Calling a pretty printer function
+---------------------------------
+
+::
+
+        %pf(%p)     pretty printer function taking one argument
+        %pf(%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("%pf(%p)", foo_to_text, foo);
+
+Note that a pretty-printer may not sleep, if called from printk(). If called
+from pr_buf() or sprintf() there are no such restrictions.
+
 Thanks
 ======
 
diff --git a/lib/test_printf.c b/lib/test_printf.c
index 07309c45f3..e3de52da91 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/printk.h>
+#include <linux/printbuf.h>
 #include <linux/random.h>
 #include <linux/rtc.h>
 #include <linux/slab.h>
@@ -783,6 +784,31 @@ test_pointer(void)
 	fourcc_pointer();
 }
 
+static void printf_test_fn_0(struct printbuf *out)
+{
+	prt_str(out, "0");
+}
+
+static void printf_test_fn_1(struct printbuf *out, void *p)
+{
+	int *i = p;
+
+	prt_printf(out, "%i", *i);
+}
+
+static void __init
+test_fn(void)
+{
+	int i = 1;
+
+	test("0", "%pf()",   printf_test_fn_0);
+	test("1", "%pf(%p)", printf_test_fn_1, &i);
+	/*
+	 * Not tested, so we don't fail the build with -Werror:
+	 */
+	//test("1", "%(%p)", printf_test_fn, &i);
+}
+
 static void __init selftest(void)
 {
 	alloced_buffer = kmalloc(BUF_SIZE + 2*PAD_SIZE, GFP_KERNEL);
@@ -794,6 +820,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 7b24714674..5afa74dda5 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -436,7 +436,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 {
@@ -2520,7 +2521,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 '%':
@@ -2602,6 +2612,49 @@ set_precision(struct printf_spec *spec, int prec)
 	}
 }
 
+static void call_prt_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;
+	}
+}
+
 /**
  * prt_vprintf - Format a string, outputting to a printbuf
  * @out: The printbuf to output to
@@ -2665,6 +2718,32 @@ void prt_vprintf(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 (*fmt != ')') {
+				if (nr_args) {
+					if (fmt[0] != ',')
+						goto out;
+					fmt++;
+				}
+
+				if (fmt[0] != '%' || fmt[1] != 'p')
+					goto out;
+				fmt += 2;
+
+				if (WARN_ON_ONCE(nr_args == ARRAY_SIZE(fn_args)))
+					goto out;
+				fn_args[nr_args++] = va_arg(args, void *);
+			}
+
+			call_prt_fn(out, fn, fn_args, nr_args);
+			fmt++; /* past trailing ) */
+			break;
+		}
+
 		case FORMAT_TYPE_PERCENT_CHAR:
 			__prt_char(out, '%');
 			break;
-- 
2.36.1


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

* [PATCH v4 06/34] lib/string_helpers: string_get_size() now returns characters wrote
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (4 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 05/34] vsprintf: %pf(%p) Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 07/34] lib/printbuf: Heap allocation Kent Overstreet
                   ` (30 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 67de398944..52e0f1d283 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 167c31f377..c1c8d4dfc9 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.1


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

* [PATCH v4 07/34] lib/printbuf: Heap allocation
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (5 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 06/34] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-21  7:58   ` Rasmus Villemoes
  2022-06-20  0:42 ` [PATCH v4 08/34] lib/printbuf: Tabstops, indenting Kent Overstreet
                   ` (29 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 120 +++++++++++++++++++++++++++++++++------
 lib/Makefile             |   2 +-
 lib/printbuf.c           |  71 +++++++++++++++++++++++
 3 files changed, 175 insertions(+), 18 deletions(-)
 create mode 100644 lib/printbuf.c

diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
index 8186c447ca..382863afa7 100644
--- a/include/linux/printbuf.h
+++ b/include/linux/printbuf.h
@@ -4,19 +4,69 @@
 #ifndef _LINUX_PRINTBUF_H
 #define _LINUX_PRINTBUF_H
 
-#include <linux/kernel.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;
+ *
+ *   prt_printf(&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/kernel.h>
+#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,				\
+})
+
 /*
  * Returns size remaining of output buffer:
  */
@@ -49,26 +99,36 @@ static inline bool printbuf_overflowed(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 __prt_char(struct printbuf *out, char c)
+/* Doesn't call printbuf_make_room(), doesn't nul terminate: */
+static inline void __prt_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 __prt_char(struct printbuf *out, char c)
+{
+	printbuf_make_room(out, 1);
+	__prt_char_reserved(out, c);
+}
+
 static inline void prt_char(struct printbuf *out, char c)
 {
 	__prt_char(out, c);
 	printbuf_nul_terminate(out);
 }
 
-static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
+static inline void __prt_chars_reserved(struct printbuf *out, char c, unsigned n)
 {
 	unsigned i, can_print = min(n, printbuf_remaining(out));
 
@@ -79,13 +139,18 @@ static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
 
 static inline void prt_chars(struct printbuf *out, char c, unsigned n)
 {
-	__prt_chars(out, c, n);
+	printbuf_make_room(out, n);
+	__prt_chars_reserved(out, c, n);
 	printbuf_nul_terminate(out);
 }
 
 static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
 {
-	unsigned i, can_print = min(n, printbuf_remaining(out));
+	unsigned i, can_print;
+
+	printbuf_make_room(out, n);
+
+	can_print = min(n, printbuf_remaining(out));
 
 	for (i = 0; i < can_print; i++)
 		out->buf[out->pos++] = ((char *) b)[i];
@@ -101,22 +166,43 @@ static inline void prt_str(struct printbuf *out, const char *str)
 
 static inline void prt_hex_byte(struct printbuf *out, u8 byte)
 {
-	__prt_char(out, hex_asc_hi(byte));
-	__prt_char(out, hex_asc_lo(byte));
+	printbuf_make_room(out, 2);
+	__prt_char_reserved(out, hex_asc_hi(byte));
+	__prt_char_reserved(out, hex_asc_lo(byte));
 	printbuf_nul_terminate(out);
 }
 
 static inline void prt_hex_byte_upper(struct printbuf *out, u8 byte)
 {
-	__prt_char(out, hex_asc_upper_hi(byte));
-	__prt_char(out, hex_asc_upper_lo(byte));
+	printbuf_make_room(out, 2);
+	__prt_char_reserved(out, hex_asc_upper_hi(byte));
+	__prt_char_reserved(out, hex_asc_upper_lo(byte));
 	printbuf_nul_terminate(out);
 }
 
-#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 6b9ffc1bd1..b4609a4258 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..8c70128e31
--- /dev/null
+++ b/lib/printbuf.c
@@ -0,0 +1,71 @@
+// 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/err.h>
+#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.1


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

* [PATCH v4 08/34] lib/printbuf: Tabstops, indenting
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (6 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 07/34] lib/printbuf: Heap allocation Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-21  8:14   ` Rasmus Villemoes
  2022-06-20  0:42 ` [PATCH v4 09/34] lib/printbuf: Unit specifiers Kent Overstreet
                   ` (28 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 |  30 ++++++++++
 lib/printbuf.c           | 125 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 155 insertions(+)

diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
index 382863afa7..1e43c47891 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.
+ *
+ * printbuf_indent_push() and printbuf_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, prt_tab() will output spaces up to the next tabstop.
+ * prt_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 prt_newline() instead of \n in the format string for indent
+ * level and tabstops to work corretly.
  */
 
 #include <linux/kernel.h>
@@ -45,18 +62,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 prt_newline(struct printbuf *);
+void printbuf_indent_add(struct printbuf *, unsigned);
+void printbuf_indent_sub(struct printbuf *, unsigned);
+void prt_tab(struct printbuf *);
+void prt_tab_rjust(struct printbuf *);
+
 /* Initializer for a heap allocated printbuf: */
 #define PRINTBUF ((struct printbuf) { .heap_allocated = true })
 
@@ -187,6 +215,8 @@ static inline void printbuf_reset(struct printbuf *buf)
 {
 	buf->pos		= 0;
 	buf->allocation_failure	= 0;
+	buf->indent		= 0;
+	buf->tabstop		= 0;
 }
 
 /**
diff --git a/lib/printbuf.c b/lib/printbuf.c
index 8c70128e31..a7f80f63ca 100644
--- a/lib/printbuf.c
+++ b/lib/printbuf.c
@@ -12,6 +12,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;
@@ -69,3 +74,123 @@ void printbuf_exit(struct printbuf *buf)
 	}
 }
 EXPORT_SYMBOL(printbuf_exit);
+
+void prt_newline(struct printbuf *buf)
+{
+	unsigned i;
+
+	printbuf_make_room(buf, 1 + buf->indent);
+
+	__prt_char(buf, '\n');
+
+	buf->last_newline	= buf->pos;
+
+	for (i = 0; i < buf->indent; i++)
+		__prt_char(buf, ' ');
+
+	printbuf_nul_terminate(buf);
+
+	buf->last_field		= buf->pos;
+	buf->tabstop = 0;
+}
+EXPORT_SYMBOL(prt_newline);
+
+/**
+ * printbuf_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 printbuf_indent_add(struct printbuf *buf, unsigned spaces)
+{
+	if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
+		spaces = 0;
+
+	buf->indent += spaces;
+	while (spaces--)
+		prt_char(buf, ' ');
+}
+EXPORT_SYMBOL(printbuf_indent_add);
+
+/**
+ * printbuf_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 printbuf_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(printbuf_indent_sub);
+
+/**
+ * prt_tab - Advance printbuf to the next tabstop
+ *
+ * @buf: printbuf to control
+ *
+ * Advance output to the next tabstop by printing spaces.
+ */
+void prt_tab(struct printbuf *out)
+{
+	int spaces = max_t(int, 0, out->tabstops[out->tabstop] - printbuf_linelen(out));
+
+	BUG_ON(out->tabstop > ARRAY_SIZE(out->tabstops));
+
+	prt_chars(out, ' ', spaces);
+
+	out->last_field = out->pos;
+	out->tabstop++;
+}
+EXPORT_SYMBOL(prt_tab);
+
+/**
+ * prt_tab_rjust - 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 prt_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(prt_tab_rjust);
-- 
2.36.1


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

* [PATCH v4 09/34] lib/printbuf: Unit specifiers
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (7 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 08/34] lib/printbuf: Tabstops, indenting Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 10/34] lib/pretty-printers: prt_string_option(), prt_bitflags() Kent Overstreet
                   ` (27 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 1e43c47891..5100287eed 100644
--- a/include/linux/printbuf.h
+++ b/include/linux/printbuf.h
@@ -53,11 +53,20 @@
  *
  * Make sure you use prt_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. prt_units() obeys it.
  */
 
 #include <linux/kernel.h>
 #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;
@@ -71,6 +80,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];
 };
@@ -84,6 +95,10 @@ void printbuf_indent_add(struct printbuf *, unsigned);
 void printbuf_indent_sub(struct printbuf *, unsigned);
 void prt_tab(struct printbuf *);
 void prt_tab_rjust(struct printbuf *);
+void prt_human_readable_u64(struct printbuf *, u64);
+void prt_human_readable_s64(struct printbuf *, s64);
+void prt_units_u64(struct printbuf *, u64);
+void prt_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 a7f80f63ca..553f89ebc1 100644
--- a/lib/printbuf.c
+++ b/lib/printbuf.c
@@ -10,6 +10,7 @@
 
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/string_helpers.h>
 #include <linux/printbuf.h>
 
 static inline size_t printbuf_linelen(struct printbuf *buf)
@@ -194,3 +195,59 @@ void prt_tab_rjust(struct printbuf *buf)
 	buf->tabstop++;
 }
 EXPORT_SYMBOL(prt_tab_rjust);
+
+/**
+ * prt_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 prt_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_size(buf));
+}
+EXPORT_SYMBOL(prt_human_readable_u64);
+
+/**
+ * prt_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 prt_human_readable_s64(struct printbuf *buf, s64 v)
+{
+	if (v < 0)
+		prt_char(buf, '-');
+	prt_human_readable_u64(buf, abs(v));
+}
+EXPORT_SYMBOL(prt_human_readable_s64);
+
+/**
+ * prt_units_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 prt_units_u64(struct printbuf *out, u64 v)
+{
+	if (out->human_readable_units)
+		prt_human_readable_u64(out, v);
+	else
+		prt_printf(out, "%llu", v);
+}
+EXPORT_SYMBOL(prt_units_u64);
+
+/**
+ * prt_units_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 prt_units_s64(struct printbuf *out, s64 v)
+{
+	if (v < 0)
+		prt_char(out, '-');
+	prt_units_u64(out, abs(v));
+}
+EXPORT_SYMBOL(prt_units_s64);
-- 
2.36.1


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

* [PATCH v4 10/34] lib/pretty-printers: prt_string_option(), prt_bitflags()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (8 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 09/34] lib/printbuf: Unit specifiers Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 11/34] vsprintf: Improve number() Kent Overstreet
                   ` (26 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/pretty-printers.h | 10 ++++++
 lib/Makefile                    |  2 +-
 lib/pretty-printers.c           | 60 +++++++++++++++++++++++++++++++++
 3 files changed, 71 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..f39d8edfba
--- /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 prt_string_option(struct printbuf *, const char * const[], size_t);
+void prt_bitflags(struct printbuf *, const char * const[], u64);
+
+#endif /* _LINUX_PRETTY_PRINTERS_H */
diff --git a/lib/Makefile b/lib/Makefile
index b4609a4258..b520024852 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..addbac95e0
--- /dev/null
+++ b/lib/pretty-printers.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/* Copyright (C) 2022 Kent Overstreet */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/printbuf.h>
+#include <linux/pretty-printers.h>
+
+/**
+ * prt_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 prt_string_option(struct printbuf *out,
+		       const char * const list[],
+		       size_t selected)
+{
+	size_t i;
+
+	for (i = 0; list[i]; i++) {
+		if (i)
+			prt_char(out, ' ');
+		if (i == selected)
+			prt_char(out, '[');
+		prt_str(out, list[i]);
+		if (i == selected)
+			prt_char(out, ']');
+	}
+}
+EXPORT_SYMBOL(prt_string_option);
+
+/**
+ * prt_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 prt_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)
+			prt_char(out, ',');
+		first = false;
+		prt_str(out, list[bit]);
+		flags ^= 1 << bit;
+	}
+}
+EXPORT_SYMBOL(prt_bitflags);
-- 
2.36.1


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

* [PATCH v4 11/34] vsprintf: Improve number()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (9 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 10/34] lib/pretty-printers: prt_string_option(), prt_bitflags() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-21  8:33   ` Rasmus Villemoes
  2022-06-20  0:42 ` [PATCH v4 12/34] vsprintf: prt_u64_minwidth(), prt_u64() Kent Overstreet
                   ` (25 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 83 +++++++++++++++++++++++++-------------------------
 1 file changed, 41 insertions(+), 42 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 5afa74dda5..7d20406deb 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -458,93 +458,92 @@ 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)) && field_width) {
-		__prt_chars(out, ' ', field_width);
+		__prt_chars_reserved(out, ' ', field_width);
 		field_width = 0;
 	}
+
 	/* sign */
 	if (sign)
-		__prt_char(out, sign);
+		__prt_char_reserved(out, sign);
+
 	/* "0x" / "0" prefix */
 	if (need_pfx) {
 		if (spec.base == 16 || !is_zero)
-			__prt_char(out, '0');
+			__prt_char_reserved(out, '0');
 		if (spec.base == 16)
-			__prt_char(out, 'X' | locase);
+			__prt_char_reserved(out, 'X' | locase);
 	}
-	/* zero or space padding */
-	if (!(spec.flags & LEFT) && field_width) {
-		char c = ' ' + (spec.flags & ZEROPAD);
 
-		__prt_chars(out, c, field_width);
-		field_width = 0;
-	}
-	/* hmm even more zero padding? */
-	if (precision > i)
-		__prt_chars(out, '0', precision - i);
+	/* zero padding */
+	if (!(spec.flags & LEFT) && field_width)
+		__prt_chars_reserved(out, '0', field_width);
+
+	/* zero padding from precision */
+	if (precision > nr_digits)
+		__prt_chars_reserved(out, '0', precision - nr_digits);
+
 	/* actual digits of result */
-	while (--i >= 0)
-		__prt_char(out, tmp[i]);
+	while (--nr_digits >= 0)
+		__prt_char_reserved(out, tmp[nr_digits]);
+
 	/* trailing space padding */
-	if (field_width)
-		__prt_chars(out, ' ', field_width);
+	if ((spec.flags & LEFT) && field_width)
+		__prt_chars_reserved(out, ' ', field_width);
 
 	printbuf_nul_terminate(out);
 }
-- 
2.36.1


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

* [PATCH v4 12/34] vsprintf: prt_u64_minwidth(), prt_u64()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (10 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 11/34] vsprintf: Improve number() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
                   ` (24 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

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

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/kernel.h |  4 +-
 lib/vsprintf.c         | 94 ++++++++++++++++++++----------------------
 2 files changed, 48 insertions(+), 50 deletions(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 1906861ece..9ba5a53c6a 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -202,12 +202,14 @@ static inline void might_fault(void) { }
 
 void do_exit(long error_code) __noreturn;
 
+struct printbuf;
+extern void prt_u64_minwidth(struct printbuf *out, u64 num, unsigned width);
+extern void prt_u64(struct printbuf *out, u64 num);
 extern int num_to_str(char *buf, int size,
 		      unsigned long long num, unsigned int width);
 
 /* lib/printf utilities */
 
-struct printbuf;
 extern __printf(2, 3) void prt_printf(struct printbuf *out, const char *fmt, ...);
 extern __printf(2, 0) void prt_vprintf(struct printbuf *out, const char *fmt, va_list);
 
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 7d20406deb..e65115f90f 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -368,41 +368,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.
+/**
+ * prt_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 prt_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)
+		__prt_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)
+		__prt_char_reserved(out, tmp[--len]);
+	printbuf_nul_terminate(out);
+}
 
-	for (idx = 0; idx < len; ++idx)
-		buf[idx + width] = tmp[len - idx - 1];
+/**
+ * prt_u64 - print a simple u64, in decimal
+ * @out: printbuf to output to
+ * @num: u64 to print
+ */
+void prt_u64(struct printbuf *out, u64 num)
+{
+	prt_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 prt_u64() or prt_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);
+
+	prt_u64_minwidth(&out, num, width);
+	return out.pos;
 }
 
 #define SIGN	1		/* unsigned/signed, must be 1 */
@@ -1018,20 +1028,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)
@@ -1231,12 +1227,12 @@ void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
 			prt_char(out, ',');
 		first = false;
 
-		number(out, rbot, default_dec_spec);
+		prt_u64(out, rbot);
 		if (rtop == rbot + 1)
 			continue;
 
 		prt_char(out, '-');
-		number(out, rtop - 1, default_dec_spec);
+		prt_u64(out, rtop - 1);
 	}
 }
 
@@ -1778,21 +1774,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);
+	prt_u64_minwidth(out, year, 4);
 	prt_char(out, '-');
-	number(out, mon, default_dec02_spec);
+	prt_u64_minwidth(out, mon, 2);
 	prt_char(out, '-');
-	number(out, tm->tm_mday, default_dec02_spec);
+	prt_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);
+	prt_u64_minwidth(out, tm->tm_hour, 2);
 	prt_char(out, ':');
-	number(out, tm->tm_min, default_dec02_spec);
+	prt_u64_minwidth(out, tm->tm_min, 2);
 	prt_char(out, ':');
-	number(out, tm->tm_sec, default_dec02_spec);
+	prt_u64_minwidth(out, tm->tm_sec, 2);
 }
 
 static noinline_for_stack
@@ -2070,7 +2066,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);
+			prt_u64(out, (unsigned int)dn->phandle);
 			break;
 		case 'P':	/* path-spec */
 			p = fwnode_get_name(of_fwnode_handle(dn));
-- 
2.36.1


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

* [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (11 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 12/34] vsprintf: prt_u64_minwidth(), prt_u64() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-21  7:19   ` Rasmus Villemoes
  2022-06-20  0:42 ` [PATCH v4 14/34] vsprintf: Start consolidating printf_spec handling Kent Overstreet
                   ` (23 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 e3de52da91..853e89e2f8 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -79,12 +79,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.1


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

* [PATCH v4 14/34] vsprintf: Start consolidating printf_spec handling
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (12 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 15/34] vsprintf: Refactor resource_string() Kent Overstreet
                   ` (22 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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, 141 insertions(+), 107 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index e65115f90f..feaca085cd 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -617,6 +617,19 @@ void widen_string(struct printbuf *out, int n,
 		prt_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,
@@ -649,7 +662,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)
 {
 	/*
@@ -679,7 +692,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)
 {
@@ -687,7 +700,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;
 	}
 
@@ -695,16 +708,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.
+	 */
+	prt_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;
+
+	prt_str(out, s);
+}
+
 static void pointer_string(struct printbuf *out,
 			   const void *ptr,
 			   struct printf_spec spec)
@@ -830,7 +874,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);
@@ -871,7 +915,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);
 		}
 
 		/*
@@ -901,14 +945,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':
@@ -920,7 +962,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;
 		}
@@ -934,56 +976,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];
-		}
-		prt_char(out, c);
+	while (1) {
+		prt_str(out, array[--i]);
+		if (!i)
+			break;
+		prt_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]))
 			prt_char(out, 'p');
-		number(out, bdev->bd_partno, spec);
+		prt_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
@@ -1006,17 +1038,12 @@ void symbol_string(struct printbuf *out, void *ptr,
 	else
 		sprint_symbol_no_offset(sym, value);
 
-	string_nocheck(out, sym, spec);
+	prt_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,
@@ -1075,7 +1102,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;
 
 	prt_char(&sym, '[');
@@ -1139,7 +1166,7 @@ void hex_string(struct printbuf *out, u8 *addr,
 		/* nothing to print */
 		return;
 
-	if (check_pointer(out, addr, spec))
+	if (check_pointer_spec(out, addr, spec))
 		return;
 
 	switch (fmt[1]) {
@@ -1180,7 +1207,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 */
@@ -1219,7 +1246,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) {
@@ -1246,7 +1273,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]) {
@@ -1547,7 +1574,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]) {
@@ -1568,12 +1595,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
@@ -1588,7 +1615,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 {
@@ -1633,7 +1660,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);
@@ -1642,16 +1669,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)) {
@@ -1668,30 +1692,28 @@ void uuid_string(struct printbuf *out, const u8 *addr,
 
 	for (i = 0; i < 16; i++) {
 		if (uc)
-			prt_hex_byte_upper(&uuid, addr[index[i]]);
+			prt_hex_byte_upper(out, addr[index[i]]);
 		else
-			prt_hex_byte(&uuid, addr[index[i]]);
+			prt_hex_byte(out, addr[index[i]]);
 		switch (i) {
 		case 3:
 		case 5:
 		case 7:
 		case 9:
-			prt_char(&uuid, '-');
+			prt_char(out, '-');
 			break;
 		}
 	}
-
-	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]) {
@@ -1701,7 +1723,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;
 	}
 }
@@ -1716,9 +1738,9 @@ void fourcc_string(struct printbuf *out, const u32 *fourcc,
 	u32 orig, 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;
 
 	orig = get_unaligned(fourcc);
@@ -1739,17 +1761,17 @@ void fourcc_string(struct printbuf *out, const u32 *fourcc,
 	special_hex_number(&output, orig, sizeof(u32));
 	prt_char(&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]) {
@@ -1800,7 +1822,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]) {
@@ -1870,7 +1892,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);
 	}
 }
 
@@ -1879,16 +1901,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
@@ -1906,7 +1928,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)
@@ -1964,7 +1986,7 @@ void format_page_flags(struct printbuf *out, unsigned long flags)
 		if (append)
 			prt_char(out, '|');
 
-		string(out, pff[i].name, default_str_spec);
+		string(out, pff[i].name);
 		prt_char(out, '=');
 		number(out, (flags >> pff[i].shift) & pff[i].mask, *pff[i].spec);
 
@@ -1980,7 +2002,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]) {
@@ -1995,7 +2017,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);
@@ -2012,10 +2034,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);
 	}
@@ -2036,12 +2056,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 */
@@ -2062,7 +2082,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 */
@@ -2072,7 +2092,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' : '-';
@@ -2082,18 +2102,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;
@@ -2118,16 +2138,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:
@@ -2294,13 +2314,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);
@@ -2331,28 +2354,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':
@@ -2372,9 +2401,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);
 		}
 	default:
 		return default_pointer(out, ptr, spec);
@@ -2704,7 +2733,12 @@ void prt_vprintf(struct printbuf *out, const char *fmt, va_list args)
 			break;
 
 		case FORMAT_TYPE_STR:
-			string(out, va_arg(args, char *), spec);
+			/*
+			 * we can't use string() then do_width_precision
+			 * afterwards: people use the field width for passing
+			 * non nul terminated strings
+			 */
+			string_spec(out, va_arg(args, char *), spec);
 			break;
 
 		case FORMAT_TYPE_PTR:
@@ -3199,7 +3233,7 @@ void prt_bstrprintf(struct printbuf *out, 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.1


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

* [PATCH v4 15/34] vsprintf: Refactor resource_string()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (13 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 14/34] vsprintf: Start consolidating printf_spec handling Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 16/34] vsprintf: Refactor fourcc_string() Kent Overstreet
                   ` (21 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 51 ++++++++++++++++++++++++--------------------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index feaca085cd..5e96ab24f5 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1057,7 +1057,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
@@ -1096,62 +1096,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;
 
-	prt_char(&sym, '[');
+	prt_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) {
-			prt_char(&sym, '-');
-			number(&sym, res->end, *specp);
+			prt_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);
 	}
-	prt_char(&sym, ']');
+	prt_char(out, ']');
 
-	string_nocheck(out, sym_buf, spec);
+	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
@@ -2326,7 +2322,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.1


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

* [PATCH v4 16/34] vsprintf: Refactor fourcc_string()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (14 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 15/34] vsprintf: Refactor resource_string() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 17/34] vsprintf: Refactor ip_addr_string() Kent Overstreet
                   ` (20 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 5e96ab24f5..55a42ae055 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1726,17 +1726,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 orig, 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;
 
 	orig = get_unaligned(fourcc);
@@ -1746,18 +1744,16 @@ void fourcc_string(struct printbuf *out, const u32 *fourcc,
 		unsigned char c = val >> (i * 8);
 
 		/* Print non-control ASCII characters as-is, dot otherwise */
-		prt_char(&output, isascii(c) && isprint(c) ? c : '.');
+		prt_char(out, isascii(c) && isprint(c) ? c : '.');
 	}
 
-	prt_char(&output, ' ');
-	prt_str(&output, orig & BIT(31) ? "big-endian" : "little-endian");
-
-	prt_char(&output, ' ');
-	prt_char(&output, '(');
-	special_hex_number(&output, orig, sizeof(u32));
-	prt_char(&output, ')');
+	prt_char(out, ' ');
+	prt_str(out, orig & BIT(31) ? "big-endian" : "little-endian");
 
-	string_spec(out, output_buf, spec);
+	prt_char(out, ' ');
+	prt_char(out, '(');
+	special_hex_number(out, orig, sizeof(u32));
+	prt_char(out, ')');
 }
 
 static noinline_for_stack
@@ -2361,7 +2357,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.1


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

* [PATCH v4 17/34] vsprintf: Refactor ip_addr_string()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (15 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 16/34] vsprintf: Refactor fourcc_string() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 18/34] vsprintf: Refactor mac_address_string() Kent Overstreet
                   ` (19 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 114 ++++++++++++++++---------------------------------
 1 file changed, 37 insertions(+), 77 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 55a42ae055..5c9a529415 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1301,13 +1301,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':
@@ -1331,19 +1331,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)
-				prt_char(out, '0');
-			if (digits < 2)
-				prt_char(out, '0');
-		}
-		/* reverse the digits in the quad */
-		while (digits--)
-			prt_char(out, temp[digits]);
-		if (i < 3)
+		if (i)
 			prt_char(out, '.');
+		number(out, addr[index], spec);
 		index += step;
 	}
 }
@@ -1426,8 +1416,6 @@ void ip6_compressed_string(struct printbuf *out, const char *addr)
 			__prt_char(out, ':');
 		ip4_string(out, &in6.s6_addr[12], "I4");
 	}
-
-	printbuf_nul_terminate(out);
 }
 
 static noinline_for_stack
@@ -1445,41 +1433,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' };
 
@@ -1502,41 +1469,35 @@ void ip6_addr_string_sa(struct printbuf *out,
 	}
 
 	if (have_p || have_s || have_f)
-		prt_char(&ip6_addr, '[');
+		prt_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)
-		prt_char(&ip6_addr, ']');
+		prt_char(out, ']');
 
 	if (have_p) {
-		prt_char(&ip6_addr, ':');
-		number(&ip6_addr, ntohs(sa->sin6_port), spec);
+		prt_char(out, ':');
+		prt_u64(out, ntohs(sa->sin6_port));
 	}
 	if (have_f) {
-		prt_char(&ip6_addr, '/');
-		number(&ip6_addr, ntohl(sa->sin6_flowinfo &
-					IPV6_FLOWINFO_MASK), spec);
+		prt_char(out, '/');
+		prt_u64(out, ntohl(sa->sin6_flowinfo & IPV6_FLOWINFO_MASK));
 	}
 	if (have_s) {
-		prt_char(&ip6_addr, '%');
-		number(&ip6_addr, sa->sin6_scope_id, spec);
+		prt_char(out, '%');
+		prt_u64(out, sa->sin6_scope_id);
 	}
-
-	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 };
 
@@ -1555,29 +1516,27 @@ void ip4_addr_string_sa(struct printbuf *out,
 		}
 	}
 
-	ip4_string(&ip4_addr, addr, fmt4);
+	ip4_string(out, addr, fmt4);
 	if (have_p) {
-		prt_char(&ip4_addr, ':');
-		number(&ip4_addr, ntohs(sa->sin_port), spec);
+		prt_char(out, ':');
+		prt_u64(out, ntohs(sa->sin_port));
 	}
-
-	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;
@@ -1587,16 +1546,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
@@ -2343,7 +2302,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.1


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

* [PATCH v4 18/34] vsprintf: Refactor mac_address_string()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (16 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 17/34] vsprintf: Refactor ip_addr_string() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 19/34] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
                   ` (18 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

 - We're attempting to consolidate printf_spec and format string
   handling in the top level ptr_vprintf(), 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 5c9a529415..486c09b4ad 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1261,15 +1261,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]) {
@@ -1288,16 +1286,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]);
+			prt_hex_byte(out, addr[5 - i]);
 		else
-			p = hex_byte_pack(p, addr[i]);
+			prt_hex_byte(out, addr[i]);
 
 		if (fmt[0] == 'M' && i != 5)
-			*p++ = separator;
+			prt_char(out, separator);
 	}
-	*p = '\0';
-
-	string_nocheck(out, mac_addr, spec);
 }
 
 static noinline_for_stack
@@ -2292,7 +2287,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.1


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

* [PATCH v4 19/34] vsprintf: time_and_date() no longer takes printf_spec
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (17 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 18/34] vsprintf: Refactor mac_address_string() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 20/34] vsprintf: flags_string() " Kent Overstreet
                   ` (17 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 486c09b4ad..affe2b0bcc 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1761,14 +1761,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]) {
@@ -1806,7 +1806,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;
@@ -1824,21 +1824,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?)");
 	}
 }
 
@@ -2322,7 +2321,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.1


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

* [PATCH v4 20/34] vsprintf: flags_string() no longer takes printf_spec
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (18 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 19/34] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 21/34] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
                   ` (16 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 affe2b0bcc..3900f7a1a5 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1942,12 +1942,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]) {
@@ -1962,7 +1962,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);
@@ -2335,7 +2335,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.1


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

* [PATCH v4 21/34] vsprintf: Refactor device_node_string, fwnode_string
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (19 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 20/34] vsprintf: flags_string() " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 22/34] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
                   ` (15 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 73 ++++++++++++++++++++------------------------------
 1 file changed, 29 insertions(+), 44 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3900f7a1a5..7f47533ed8 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1988,25 +1988,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 */
@@ -2015,7 +2010,6 @@ 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)
 			prt_char(out, ':');
 
@@ -2023,43 +2017,41 @@ void device_node_string(struct printbuf *out, struct device_node *dn,
 		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;
+
+			prt_bytes(out, name, len);
 			break;
+		}
 		case 'p':	/* phandle */
-			prt_u64(out, (unsigned int)dn->phandle);
+			prt_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);
+			prt_char(out, of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-');
+			prt_char(out, of_node_check_flag(dn, OF_DETACHED) ? 'd' : '-');
+			prt_char(out, of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-');
+			prt_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);
+					prt_char(out, ',');
+				prt_char(out, '\"');
+				string(out, p);
+				prt_char(out, '\"');
 
 				has_mult = true;
 			}
@@ -2068,39 +2060,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);
 }
 
 int __init no_hash_pointers_enable(char *str)
@@ -2338,9 +2321,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.1


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

* [PATCH v4 22/34] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (20 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 21/34] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 23/34] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
                   ` (14 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 60 +++++++++++++++++++++++---------------------------
 1 file changed, 28 insertions(+), 32 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 7f47533ed8..fcdf187b21 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -53,6 +53,7 @@
 #include <asm/unaligned.h>
 
 #include <linux/string_helpers.h>
+#include <linux/pretty-printers.h>
 #include "kstrtox.h"
 
 /* Disable pointer hashing if requested */
@@ -1151,18 +1152,23 @@ void resource_string(struct printbuf *out, struct resource *res,
 }
 
 static noinline_for_stack
-void hex_string(struct printbuf *out, u8 *addr,
-		struct printf_spec spec, const char *fmt)
+void hex_string(struct printbuf *out, const u8 *addr,
+		int len, const char *fmt)
 {
-	int i, 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]) {
@@ -1180,34 +1186,21 @@ void hex_string(struct printbuf *out, u8 *addr,
 		break;
 	}
 
-	if (spec.field_width > 0)
-		len = min_t(int, spec.field_width, 64);
-
-	for (i = 0; i < len; ++i) {
-		__prt_char(out, hex_asc_hi(addr[i]));
-		__prt_char(out, hex_asc_lo(addr[i]));
-
-		if (separator && i != len - 1)
-			__prt_char(out, separator);
-	}
-
-	printbuf_nul_terminate(out);
+	prt_hex_bytes(out, addr, len, 1, 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)
@@ -1236,13 +1229,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) {
@@ -2257,13 +2251,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.1


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

* [PATCH v4 23/34] Input/joystick/analog: Convert from seq_buf -> printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (21 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 22/34] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf Kent Overstreet
                   ` (13 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

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..a8c5f90e82 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);
+	prt_printf(&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));
-
+		prt_printf(&buf, " %d-hat", hweight16(analog->mask & ANALOG_HATS_ALL));
 	if (analog->mask & ANALOG_HAT_FCS)
-		seq_buf_printf(&s, " FCS");
+		prt_printf(&buf, " FCS");
 	if (analog->mask & ANALOG_ANY_CHF)
-		seq_buf_printf(&s, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
+		prt_printf(&buf, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
 
-	seq_buf_printf(&s, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
+	prt_printf(&buf, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
 }
 
 /*
-- 
2.36.1


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

* [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (22 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 23/34] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20 11:37   ` Michal Hocko
  2022-06-20  0:42 ` [PATCH v4 25/34] clk: tegra: bpmp: " Kent Overstreet
                   ` (12 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 598fece89e..57861dc9fe 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -62,7 +62,7 @@
 #include <linux/file.h>
 #include <linux/resume_user_mode.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>
@@ -1461,13 +1461,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.
@@ -1484,49 +1480,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);
+		prt_printf(&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);
+			prt_printf(&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));
+	prt_printf(&buf, "%s %lu\n", vm_event_name(PGFAULT),
+	       memcg_events(memcg, PGFAULT));
+	prt_printf(&buf, "%s %lu\n", vm_event_name(PGMAJFAULT),
+	       memcg_events(memcg, PGMAJFAULT));
+	prt_printf(&buf, "%s %lu\n",  vm_event_name(PGREFILL),
+	       memcg_events(memcg, PGREFILL));
+	prt_printf(&buf, "pgscan %lu\n",
+	       memcg_events(memcg, PGSCAN_KSWAPD) +
+	       memcg_events(memcg, PGSCAN_DIRECT));
+	prt_printf(&buf, "pgsteal %lu\n",
+	       memcg_events(memcg, PGSTEAL_KSWAPD) +
+	       memcg_events(memcg, PGSTEAL_DIRECT));
+	prt_printf(&buf, "%s %lu\n", vm_event_name(PGACTIVATE),
+	       memcg_events(memcg, PGACTIVATE));
+	prt_printf(&buf, "%s %lu\n", vm_event_name(PGDEACTIVATE),
+	       memcg_events(memcg, PGDEACTIVATE));
+	prt_printf(&buf, "%s %lu\n", vm_event_name(PGLAZYFREE),
+	       memcg_events(memcg, PGLAZYFREE));
+	prt_printf(&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));
+	prt_printf(&buf, "%s %lu\n", vm_event_name(THP_FAULT_ALLOC),
+	       memcg_events(memcg, THP_FAULT_ALLOC));
+	prt_printf(&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.1


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

* [PATCH v4 25/34] clk: tegra: bpmp: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (23 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 26/34] tools/testing/nvdimm: " Kent Overstreet
                   ` (11 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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..301551174c 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, "(");
+		prt_printf(&buf, "(");
 
 	if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
-		seq_buf_printf(&buf, "%smux", prefix);
+		prt_printf(&buf, "%smux", prefix);
 		prefix = ", ";
 	}
 
 	if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
-		seq_buf_printf(&buf, "%sfixed", prefix);
+		prt_printf(&buf, "%sfixed", prefix);
 		prefix = ", ";
 	}
 
 	if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
-		seq_buf_printf(&buf, "%sroot", prefix);
+		prt_printf(&buf, "%sroot", prefix);
 		prefix = ", ";
 	}
 
 	if (info->flags)
-		seq_buf_printf(&buf, ")");
+		prt_printf(&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.1


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

* [PATCH v4 26/34] tools/testing/nvdimm: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (24 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 25/34] clk: tegra: bpmp: " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-24 19:32   ` Dan Williams
  2022-06-20  0:42   ` Kent Overstreet
                   ` (10 subsequent siblings)
  36 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 4d1a947367..a2097955da 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"
@@ -740,32 +740,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 ");
+		prt_printf(&s, "not_armed ");
 
 	if (flags & PAPR_PMEM_BAD_SHUTDOWN_MASK)
-		seq_buf_printf(&s, "flush_fail ");
+		prt_printf(&s, "flush_fail ");
 
 	if (flags & PAPR_PMEM_BAD_RESTORE_MASK)
-		seq_buf_printf(&s, "restore_fail ");
+		prt_printf(&s, "restore_fail ");
 
 	if (flags & PAPR_PMEM_SAVE_MASK)
-		seq_buf_printf(&s, "save_fail ");
+		prt_printf(&s, "save_fail ");
 
 	if (flags & PAPR_PMEM_SMART_EVENT_MASK)
-		seq_buf_printf(&s, "smart_notify ");
+		prt_printf(&s, "smart_notify ");
 
+	if (printbuf_written(&s))
+		prt_printf(&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.1


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

* [PATCH v4 27/34] powerpc: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
@ 2022-06-20  0:42   ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 02/34] lib/string_helpers: Convert string_escape_mem() to printbuf Kent Overstreet
                     ` (35 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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..fb8ba50223 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 ");
+				prt_printf(&s, "XXXXXXXX ");
 				continue;
 			}
-			seq_buf_printf(&s, regs->nip == pc ? "<%08x> " : "%08x ", instr);
+			prt_printf(&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 d96fd14bd7..b34de62e65 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");
+		prt_printf(&s, "Mitigation: RFI Flush");
 		if (thread_priv)
-			seq_buf_printf(&s, ", L1D private per thread");
-
-		seq_buf_printf(&s, "\n");
-
-		return s.len;
+			prt_printf(&s, ", L1D private per thread");
+
+		prt_printf(&s, "\n");
+	} else if (thread_priv) {
+		prt_printf(&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)) {
+		prt_printf(&s, "Not affected\n");
+	} else {
+		prt_printf(&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");
+			prt_printf(&s, "Mitigation: __user pointer sanitization");
 		else
-			seq_buf_printf(&s, "Vulnerable");
+			prt_printf(&s, "Vulnerable");
 
 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
-			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
+			prt_printf(&s, ", ori31 speculation barrier enabled");
 
-		seq_buf_printf(&s, "\n");
+		prt_printf(&s, "\n");
 	} else
-		seq_buf_printf(&s, "Not affected\n");
+		prt_printf(&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: ");
+		prt_printf(&s, "Mitigation: ");
 
 		if (bcs)
-			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
+			prt_printf(&s, "Indirect branch serialisation (kernel only)");
 
 		if (bcs && ccd)
-			seq_buf_printf(&s, ", ");
+			prt_printf(&s, ", ");
 
 		if (ccd)
-			seq_buf_printf(&s, "Indirect branch cache disabled");
+			prt_printf(&s, "Indirect branch cache disabled");
 
 	} else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
-		seq_buf_printf(&s, "Mitigation: Software count cache flush");
+		prt_printf(&s, "Mitigation: Software count cache flush");
 
 		if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			prt_printf(&s, " (hardware accelerated)");
 
 	} else if (btb_flush_enabled) {
-		seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
+		prt_printf(&s, "Mitigation: Branch predictor state flush");
 	} else {
-		seq_buf_printf(&s, "Vulnerable");
+		prt_printf(&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");
+			prt_printf(&s, ", Software link stack flush");
 		if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			prt_printf(&s, " (hardware accelerated)");
 	}
 
-	seq_buf_printf(&s, "\n");
+	prt_printf(&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 39962c9055..317d4513db 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>
@@ -1145,7 +1145,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);
@@ -1168,18 +1168,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));
+		prt_printf(&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);
 
@@ -1188,7 +1187,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;
 
@@ -1199,29 +1198,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 ");
+		prt_printf(&s, "not_armed ");
 
 	if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK)
-		seq_buf_printf(&s, "flush_fail ");
+		prt_printf(&s, "flush_fail ");
 
 	if (health & PAPR_PMEM_BAD_RESTORE_MASK)
-		seq_buf_printf(&s, "restore_fail ");
+		prt_printf(&s, "restore_fail ");
 
 	if (health & PAPR_PMEM_ENCRYPTED)
-		seq_buf_printf(&s, "encrypted ");
+		prt_printf(&s, "encrypted ");
 
 	if (health & PAPR_PMEM_SMART_EVENT_MASK)
-		seq_buf_printf(&s, "smart_notify ");
+		prt_printf(&s, "smart_notify ");
 
 	if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED)
-		seq_buf_printf(&s, "scrubbed locked ");
+		prt_printf(&s, "scrubbed locked ");
 
-	if (seq_buf_used(&s))
-		seq_buf_printf(&s, "\n");
+	if (printbuf_written(&s))
+		prt_printf(&s, "\n");
 
-	return seq_buf_used(&s);
+	return printbuf_written(&s);
 }
 DEVICE_ATTR_RO(flags);
 
-- 
2.36.1


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

* [PATCH v4 27/34] powerpc: Convert to printbuf
@ 2022-06-20  0:42   ` Kent Overstreet
  0 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: linux, rostedt, enozhatsky, willy, linuxppc-dev, Kent Overstreet

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..fb8ba50223 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 ");
+				prt_printf(&s, "XXXXXXXX ");
 				continue;
 			}
-			seq_buf_printf(&s, regs->nip == pc ? "<%08x> " : "%08x ", instr);
+			prt_printf(&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 d96fd14bd7..b34de62e65 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");
+		prt_printf(&s, "Mitigation: RFI Flush");
 		if (thread_priv)
-			seq_buf_printf(&s, ", L1D private per thread");
-
-		seq_buf_printf(&s, "\n");
-
-		return s.len;
+			prt_printf(&s, ", L1D private per thread");
+
+		prt_printf(&s, "\n");
+	} else if (thread_priv) {
+		prt_printf(&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)) {
+		prt_printf(&s, "Not affected\n");
+	} else {
+		prt_printf(&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");
+			prt_printf(&s, "Mitigation: __user pointer sanitization");
 		else
-			seq_buf_printf(&s, "Vulnerable");
+			prt_printf(&s, "Vulnerable");
 
 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
-			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
+			prt_printf(&s, ", ori31 speculation barrier enabled");
 
-		seq_buf_printf(&s, "\n");
+		prt_printf(&s, "\n");
 	} else
-		seq_buf_printf(&s, "Not affected\n");
+		prt_printf(&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: ");
+		prt_printf(&s, "Mitigation: ");
 
 		if (bcs)
-			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
+			prt_printf(&s, "Indirect branch serialisation (kernel only)");
 
 		if (bcs && ccd)
-			seq_buf_printf(&s, ", ");
+			prt_printf(&s, ", ");
 
 		if (ccd)
-			seq_buf_printf(&s, "Indirect branch cache disabled");
+			prt_printf(&s, "Indirect branch cache disabled");
 
 	} else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
-		seq_buf_printf(&s, "Mitigation: Software count cache flush");
+		prt_printf(&s, "Mitigation: Software count cache flush");
 
 		if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			prt_printf(&s, " (hardware accelerated)");
 
 	} else if (btb_flush_enabled) {
-		seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
+		prt_printf(&s, "Mitigation: Branch predictor state flush");
 	} else {
-		seq_buf_printf(&s, "Vulnerable");
+		prt_printf(&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");
+			prt_printf(&s, ", Software link stack flush");
 		if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW)
-			seq_buf_printf(&s, " (hardware accelerated)");
+			prt_printf(&s, " (hardware accelerated)");
 	}
 
-	seq_buf_printf(&s, "\n");
+	prt_printf(&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 39962c9055..317d4513db 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>
@@ -1145,7 +1145,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);
@@ -1168,18 +1168,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));
+		prt_printf(&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);
 
@@ -1188,7 +1187,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;
 
@@ -1199,29 +1198,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 ");
+		prt_printf(&s, "not_armed ");
 
 	if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK)
-		seq_buf_printf(&s, "flush_fail ");
+		prt_printf(&s, "flush_fail ");
 
 	if (health & PAPR_PMEM_BAD_RESTORE_MASK)
-		seq_buf_printf(&s, "restore_fail ");
+		prt_printf(&s, "restore_fail ");
 
 	if (health & PAPR_PMEM_ENCRYPTED)
-		seq_buf_printf(&s, "encrypted ");
+		prt_printf(&s, "encrypted ");
 
 	if (health & PAPR_PMEM_SMART_EVENT_MASK)
-		seq_buf_printf(&s, "smart_notify ");
+		prt_printf(&s, "smart_notify ");
 
 	if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED)
-		seq_buf_printf(&s, "scrubbed locked ");
+		prt_printf(&s, "scrubbed locked ");
 
-	if (seq_buf_used(&s))
-		seq_buf_printf(&s, "\n");
+	if (printbuf_written(&s))
+		prt_printf(&s, "\n");
 
-	return seq_buf_used(&s);
+	return printbuf_written(&s);
 }
 DEVICE_ATTR_RO(flags);
 
-- 
2.36.1


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

* [PATCH v4 28/34] x86/resctrl: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (26 preceding siblings ...)
  2022-06-20  0:42   ` Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 29/34] PCI/P2PDMA: " Kent Overstreet
                   ` (8 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 83f901e2c2..5b6720b6a4 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);
+	prt_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);
+	prt_vprintf(&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.1


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

* [PATCH v4 29/34] PCI/P2PDMA: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (27 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 28/34] x86/resctrl: " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 30/34] tracing: trace_events_synth: " Kent Overstreet
                   ` (7 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, 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 | 21 ++++++++-------------
 1 file changed, 8 insertions(+), 13 deletions(-)

diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 30b1df3c9d..3b7a6ca446 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 prt_bus_devfn(struct printbuf *buf, struct pci_dev *pdev)
 {
-	if (!buf)
-		return;
-
-	seq_buf_printf(buf, "%s;", pci_name(pdev));
+	prt_printf(buf, "%s;", pci_name(pdev));
 }
 
 static bool cpu_supports_p2pdma(void)
@@ -455,13 +452,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
@@ -472,7 +467,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);
+			prt_bus_devfn(&acs_list, a);
 			acs_cnt++;
 		}
 
@@ -501,7 +496,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);
+			prt_bus_devfn(&acs_list, bb);
 			acs_cnt++;
 		}
 
@@ -516,11 +511,11 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
 	}
 
 	if (verbose) {
-		acs_list.buffer[acs_list.len-1] = 0; /* drop final semicolon */
+		acs_list.buf[acs_list.pos-1] = 0; /* drop final semicolon */
 		pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
 			 pci_name(provider));
 		pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
-			 acs_list.buffer);
+			 acs_list.buf);
 	}
 	acs_redirects = true;
 
-- 
2.36.1


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

* [PATCH v4 30/34] tracing: trace_events_synth: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (28 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 29/34] PCI/P2PDMA: " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 31/34] d_path: prt_path() Kent Overstreet
                   ` (6 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy, Ingo Molnar

This converts from seq_buf to printbuf

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@redhat.com>
---
 kernel/trace/trace_events_synth.c | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c
index 5e8c07aef0..627e0e45f0 100644
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -5,13 +5,14 @@
  * Copyright (C) 2015, 2020 Tom Zanussi <tom.zanussi@linux.intel.com>
  */
 
-#include <linux/module.h>
 #include <linux/kallsyms.h>
-#include <linux/security.h>
+#include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/printbuf.h>
+#include <linux/rculist.h>
+#include <linux/security.h>
 #include <linux/slab.h>
 #include <linux/stacktrace.h>
-#include <linux/rculist.h>
 #include <linux/tracefs.h>
 
 /* for gfp flag names */
@@ -611,7 +612,7 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
 	const char *prefix = NULL, *field_type = argv[0], *field_name, *array;
 	struct synth_field *field;
 	int len, ret = -ENOMEM;
-	struct seq_buf s;
+	struct printbuf s;
 	ssize_t size;
 
 	if (!strcmp(field_type, "unsigned")) {
@@ -666,17 +667,15 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
 	if (!field->type)
 		goto free;
 
-	seq_buf_init(&s, field->type, len);
+	s = PRINTBUF_EXTERN(field->type, len);
 	if (prefix)
-		seq_buf_puts(&s, prefix);
-	seq_buf_puts(&s, field_type);
+		prt_str(&s, prefix);
+	prt_str(&s, field_type);
 	if (array)
-		seq_buf_puts(&s, array);
-	if (WARN_ON_ONCE(!seq_buf_buffer_left(&s)))
+		prt_str(&s, array);
+	if (WARN_ON_ONCE(!printbuf_remaining(&s)))
 		goto free;
 
-	s.buffer[s.len] = '\0';
-
 	size = synth_field_size(field->type);
 	if (size < 0) {
 		if (array)
@@ -694,13 +693,12 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
 			if (!type)
 				goto free;
 
-			seq_buf_init(&s, type, len);
-			seq_buf_puts(&s, "__data_loc ");
-			seq_buf_puts(&s, field->type);
+			s = PRINTBUF_EXTERN(type, len);
+			prt_str(&s, "__data_loc ");
+			prt_str(&s, field->type);
 
-			if (WARN_ON_ONCE(!seq_buf_buffer_left(&s)))
+			if (WARN_ON_ONCE(!printbuf_remaining(&s)))
 				goto free;
-			s.buffer[s.len] = '\0';
 
 			kfree(field->type);
 			field->type = type;
-- 
2.36.1


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

* [PATCH v4 31/34] d_path: prt_path()
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (29 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 30/34] tracing: trace_events_synth: " Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 32/34] ACPI/APEI: Add missing include Kent Overstreet
                   ` (5 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

This implements a new printbuf version of d_path()/mangle_path(), which
will replace the seq_buf version.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 fs/d_path.c            | 35 +++++++++++++++++++++++++++++++++++
 include/linux/dcache.h |  1 +
 2 files changed, 36 insertions(+)

diff --git a/fs/d_path.c b/fs/d_path.c
index e4e0ebad1f..1bd9e85f2f 100644
--- a/fs/d_path.c
+++ b/fs/d_path.c
@@ -5,6 +5,7 @@
 #include <linux/fs_struct.h>
 #include <linux/fs.h>
 #include <linux/slab.h>
+#include <linux/printbuf.h>
 #include <linux/prefetch.h>
 #include "mount.h"
 
@@ -294,6 +295,40 @@ char *d_path(const struct path *path, char *buf, int buflen)
 }
 EXPORT_SYMBOL(d_path);
 
+/**
+ * prt_path - format a path for output
+ * @out: printbuf to output to
+ * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
+ *
+ * Write a path name into the sequence buffer.
+ *
+ * Returns 0 on success, or error code from d_path
+ */
+int prt_path(struct printbuf *out, const struct path *path, const char *esc)
+{
+	char *p, *buf;
+	size_t size;
+again:
+	buf = out->buf + out->pos;
+	size = printbuf_remaining_size(out);
+
+	p = d_path(path, buf, size);
+	if (IS_ERR(p)) {
+		printbuf_make_room(out, max_t(size_t, 64, size * 2));
+		if (printbuf_remaining_size(out) > size)
+			goto again;
+
+		return PTR_ERR(p);
+	}
+
+	p = mangle_path(buf, p, esc);
+	if (p)
+		out->pos += p - buf;
+	return 0;
+}
+EXPORT_SYMBOL(prt_path);
+
 /*
  * Helper function for dentry_operations.d_dname() members
  */
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index f5bba51480..2181144f9f 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -293,6 +293,7 @@ extern char *d_absolute_path(const struct path *, char *, int);
 extern char *d_path(const struct path *, char *, int);
 extern char *dentry_path_raw(const struct dentry *, char *, int);
 extern char *dentry_path(const struct dentry *, char *, int);
+extern int prt_path(struct printbuf *, const struct path *, const char *);
 
 /* Allocation counts.. */
 
-- 
2.36.1


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

* [PATCH v4 32/34] ACPI/APEI: Add missing include
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (30 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 31/34] d_path: prt_path() Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 33/34] tracing: Convert to printbuf Kent Overstreet
                   ` (4 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

The upcoming patch to switch tracing from seq_buf to printbuf means
we're no longer pulling in headers that we used to; this adds a missing
include so things don't break.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 drivers/acpi/apei/erst-dbg.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/acpi/apei/erst-dbg.c b/drivers/acpi/apei/erst-dbg.c
index c740f0faad..90aa034dce 100644
--- a/drivers/acpi/apei/erst-dbg.c
+++ b/drivers/acpi/apei/erst-dbg.c
@@ -11,6 +11,7 @@
  *   Author: Huang Ying <ying.huang@intel.com>
  */
 
+#include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
-- 
2.36.1


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

* [PATCH v4 33/34] tracing: Convert to printbuf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (31 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 32/34] ACPI/APEI: Add missing include Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  0:42 ` [PATCH v4 34/34] Delete seq_buf Kent Overstreet
                   ` (3 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

This converts the seq_bufs in dynevent_cmd and trace_seq to printbufs.

 - read_pos in seq_buf doesn't exist in printbuf, so is added to
   trace_seq

 - seq_buf_to_user doesn't have a printbuf equivalent, so is inlined
   into trace_seq_to_user

 - seq_buf_putmem_hex currently swabs bytes on little endian, hardcoded
   to 8 byte units. This patch switches it to prt_hex_bytes(), which
   does _not_ swab.

Otherwise this is largely a direct conversion, with a few slight
refactorings and cleanups.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/trace_events.h         |   2 +-
 include/linux/trace_seq.h            |  17 ++--
 kernel/trace/trace.c                 |  45 ++++-------
 kernel/trace/trace_dynevent.c        |  34 ++++----
 kernel/trace/trace_events_filter.c   |   2 +-
 kernel/trace/trace_events_synth.c    |   2 +-
 kernel/trace/trace_functions_graph.c |   6 +-
 kernel/trace/trace_kprobe.c          |   2 +-
 kernel/trace/trace_seq.c             | 111 ++++++++++++++-------------
 9 files changed, 103 insertions(+), 118 deletions(-)

diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index e6e95a9f07..48471e32f8 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -496,7 +496,7 @@ struct dynevent_cmd;
 typedef int (*dynevent_create_fn_t)(struct dynevent_cmd *cmd);
 
 struct dynevent_cmd {
-	struct seq_buf		seq;
+	struct printbuf		seq;
 	const char		*event_name;
 	unsigned int		n_fields;
 	enum dynevent_type	type;
diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
index 5a2c650d9e..d2b51007b3 100644
--- a/include/linux/trace_seq.h
+++ b/include/linux/trace_seq.h
@@ -2,10 +2,12 @@
 #ifndef _LINUX_TRACE_SEQ_H
 #define _LINUX_TRACE_SEQ_H
 
-#include <linux/seq_buf.h>
+#include <linux/printbuf.h>
 
 #include <asm/page.h>
 
+struct seq_file;
+
 /*
  * Trace sequences are used to allow a function to call several other functions
  * to create a string of data to use (up to a max of PAGE_SIZE).
@@ -13,14 +15,16 @@
 
 struct trace_seq {
 	char			buffer[PAGE_SIZE];
-	struct seq_buf		seq;
+	struct printbuf		seq;
+	unsigned		readpos;
 	int			full;
 };
 
 static inline void
 trace_seq_init(struct trace_seq *s)
 {
-	seq_buf_init(&s->seq, s->buffer, PAGE_SIZE);
+	s->seq = PRINTBUF_EXTERN(s->buffer, PAGE_SIZE);
+	s->readpos = 0;
 	s->full = 0;
 }
 
@@ -39,7 +43,7 @@ trace_seq_init(struct trace_seq *s)
  */
 static inline int trace_seq_used(struct trace_seq *s)
 {
-	return seq_buf_used(&s->seq);
+	return printbuf_written(&s->seq);
 }
 
 /**
@@ -54,7 +58,7 @@ static inline int trace_seq_used(struct trace_seq *s)
 static inline char *
 trace_seq_buffer_ptr(struct trace_seq *s)
 {
-	return s->buffer + seq_buf_used(&s->seq);
+	return s->buffer + printbuf_written(&s->seq);
 }
 
 /**
@@ -66,7 +70,7 @@ trace_seq_buffer_ptr(struct trace_seq *s)
  */
 static inline bool trace_seq_has_overflowed(struct trace_seq *s)
 {
-	return s->full || seq_buf_has_overflowed(&s->seq);
+	return s->full || printbuf_overflowed(&s->seq);
 }
 
 /*
@@ -87,6 +91,7 @@ extern void trace_seq_putc(struct trace_seq *s, unsigned char c);
 extern void trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len);
 extern void trace_seq_putmem_hex(struct trace_seq *s, const void *mem,
 				unsigned int len);
+struct path;
 extern int trace_seq_path(struct trace_seq *s, const struct path *path);
 
 extern void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp,
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index f4de111fa1..b815a914b5 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1670,15 +1670,15 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt)
 {
 	int len;
 
-	if (trace_seq_used(s) <= s->seq.readpos)
+	if (trace_seq_used(s) <= s->readpos)
 		return -EBUSY;
 
-	len = trace_seq_used(s) - s->seq.readpos;
+	len = trace_seq_used(s) - s->readpos;
 	if (cnt > len)
 		cnt = len;
-	memcpy(buf, s->buffer + s->seq.readpos, cnt);
+	memcpy(buf, s->buffer + s->readpos, cnt);
 
-	s->seq.readpos += cnt;
+	s->readpos += cnt;
 	return cnt;
 }
 
@@ -3725,11 +3725,7 @@ static bool trace_safe_str(struct trace_iterator *iter, const char *str,
 
 static const char *show_buffer(struct trace_seq *s)
 {
-	struct seq_buf *seq = &s->seq;
-
-	seq_buf_terminate(seq);
-
-	return seq->buffer;
+	return printbuf_str(&s->seq);
 }
 
 static DEFINE_STATIC_KEY_FALSE(trace_no_verify);
@@ -6762,12 +6758,12 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
 	trace_access_lock(iter->cpu_file);
 	while (trace_find_next_entry_inc(iter) != NULL) {
 		enum print_line_t ret;
-		int save_len = iter->seq.seq.len;
+		int save_pos = iter->seq.seq.pos;
 
 		ret = print_trace_line(iter);
 		if (ret == TRACE_TYPE_PARTIAL_LINE) {
 			/* don't print partial lines */
-			iter->seq.seq.len = save_len;
+			iter->seq.seq.pos = save_pos;
 			break;
 		}
 		if (ret != TRACE_TYPE_NO_CONSUME)
@@ -6789,7 +6785,7 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
 
 	/* Now copy what we have to the user */
 	sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
-	if (iter->seq.seq.readpos >= trace_seq_used(&iter->seq))
+	if (iter->seq.readpos >= trace_seq_used(&iter->seq))
 		trace_seq_init(&iter->seq);
 
 	/*
@@ -6815,16 +6811,15 @@ static size_t
 tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
 {
 	size_t count;
-	int save_len;
 	int ret;
 
 	/* Seq buffer is page-sized, exactly what we need. */
 	for (;;) {
-		save_len = iter->seq.seq.len;
+		unsigned save_pos = iter->seq.seq.pos;
 		ret = print_trace_line(iter);
 
 		if (trace_seq_has_overflowed(&iter->seq)) {
-			iter->seq.seq.len = save_len;
+			iter->seq.seq.pos = save_pos;
 			break;
 		}
 
@@ -6834,14 +6829,14 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
 		 * anyway to be safe.
 		 */
 		if (ret == TRACE_TYPE_PARTIAL_LINE) {
-			iter->seq.seq.len = save_len;
+			iter->seq.seq.pos = save_pos;
 			break;
 		}
 
-		count = trace_seq_used(&iter->seq) - save_len;
+		count = trace_seq_used(&iter->seq) - save_pos;
 		if (rem < count) {
 			rem = 0;
-			iter->seq.seq.len = save_len;
+			iter->seq.seq.pos = save_pos;
 			break;
 		}
 
@@ -9817,20 +9812,8 @@ static struct notifier_block trace_die_notifier = {
 void
 trace_printk_seq(struct trace_seq *s)
 {
-	/* Probably should print a warning here. */
-	if (s->seq.len >= TRACE_MAX_PRINT)
-		s->seq.len = TRACE_MAX_PRINT;
-
-	/*
-	 * More paranoid code. Although the buffer size is set to
-	 * PAGE_SIZE, and TRACE_MAX_PRINT is 1000, this is just
-	 * an extra layer of protection.
-	 */
-	if (WARN_ON_ONCE(s->seq.len >= s->seq.size))
-		s->seq.len = s->seq.size - 1;
-
 	/* should be zero ended, but we are paranoid. */
-	s->buffer[s->seq.len] = 0;
+	printbuf_nul_terminate(&s->seq);
 
 	printk(KERN_TRACE "%s", s->buffer);
 
diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c
index e34e8182ee..eabeeb97b5 100644
--- a/kernel/trace/trace_dynevent.c
+++ b/kernel/trace/trace_dynevent.c
@@ -295,21 +295,19 @@ int dynevent_arg_add(struct dynevent_cmd *cmd,
 		     struct dynevent_arg *arg,
 		     dynevent_check_arg_fn_t check_arg)
 {
-	int ret = 0;
-
 	if (check_arg) {
-		ret = check_arg(arg);
+		int ret = check_arg(arg);
 		if (ret)
 			return ret;
 	}
 
-	ret = seq_buf_printf(&cmd->seq, " %s%c", arg->str, arg->separator);
-	if (ret) {
+	prt_printf(&cmd->seq, " %s%c", arg->str, arg->separator);
+	if (printbuf_overflowed(&cmd->seq)) {
 		pr_err("String is too long: %s%c\n", arg->str, arg->separator);
 		return -E2BIG;
 	}
 
-	return ret;
+	return 0;
 }
 
 /**
@@ -340,25 +338,23 @@ int dynevent_arg_pair_add(struct dynevent_cmd *cmd,
 			  struct dynevent_arg_pair *arg_pair,
 			  dynevent_check_arg_fn_t check_arg)
 {
-	int ret = 0;
-
 	if (check_arg) {
-		ret = check_arg(arg_pair);
+		int ret = check_arg(arg_pair);
 		if (ret)
 			return ret;
 	}
 
-	ret = seq_buf_printf(&cmd->seq, " %s%c%s%c", arg_pair->lhs,
-			     arg_pair->operator, arg_pair->rhs,
-			     arg_pair->separator);
-	if (ret) {
+	prt_printf(&cmd->seq, " %s%c%s%c", arg_pair->lhs,
+		   arg_pair->operator, arg_pair->rhs,
+		   arg_pair->separator);
+	if (printbuf_overflowed(&cmd->seq)) {
 		pr_err("field string is too long: %s%c%s%c\n", arg_pair->lhs,
 		       arg_pair->operator, arg_pair->rhs,
 		       arg_pair->separator);
 		return -E2BIG;
 	}
 
-	return ret;
+	return 0;
 }
 
 /**
@@ -373,15 +369,13 @@ int dynevent_arg_pair_add(struct dynevent_cmd *cmd,
  */
 int dynevent_str_add(struct dynevent_cmd *cmd, const char *str)
 {
-	int ret = 0;
-
-	ret = seq_buf_puts(&cmd->seq, str);
-	if (ret) {
+	prt_str(&cmd->seq, str);
+	if (printbuf_overflowed(&cmd->seq)) {
 		pr_err("String is too long: %s\n", str);
 		return -E2BIG;
 	}
 
-	return ret;
+	return 0;
 }
 
 /**
@@ -410,7 +404,7 @@ void dynevent_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen,
 {
 	memset(cmd, '\0', sizeof(*cmd));
 
-	seq_buf_init(&cmd->seq, buf, maxlen);
+	cmd->seq = PRINTBUF_EXTERN(buf, maxlen);
 	cmd->type = type;
 	cmd->run_command = run_command;
 }
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index b458a9afa2..70cfd12410 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1059,7 +1059,7 @@ static void append_filter_err(struct trace_array *tr,
 				FILT_ERR_ERRNO, 0);
 	}
 	trace_seq_putc(s, 0);
-	buf = kmemdup_nul(s->buffer, s->seq.len, GFP_KERNEL);
+	buf = kstrdup(printbuf_str(&s->seq), GFP_KERNEL);
 	if (buf) {
 		kfree(filter->filter_string);
 		filter->filter_string = buf;
diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c
index 627e0e45f0..ddb2a2737b 100644
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -1512,7 +1512,7 @@ static int synth_event_run_command(struct dynevent_cmd *cmd)
 	struct synth_event *se;
 	int ret;
 
-	ret = create_or_delete_synth_event(cmd->seq.buffer);
+	ret = create_or_delete_synth_event(cmd->seq.buf);
 	if (ret)
 		return ret;
 
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 203204cadf..9f270fdde9 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -1022,9 +1022,9 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
 		goto out;
 
 	/* Strip ending newline */
-	if (s->buffer[s->seq.len - 1] == '\n') {
-		s->buffer[s->seq.len - 1] = '\0';
-		s->seq.len--;
+	if (s->buffer[s->seq.pos - 1] == '\n') {
+		s->buffer[s->seq.pos - 1] = '\0';
+		s->seq.pos--;
 	}
 
 	trace_seq_puts(s, " */\n");
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 47cebef785..b97a912eed 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -915,7 +915,7 @@ static int create_or_delete_trace_kprobe(const char *raw_command)
 
 static int trace_kprobe_run_command(struct dynevent_cmd *cmd)
 {
-	return create_or_delete_trace_kprobe(cmd->seq.buffer);
+	return create_or_delete_trace_kprobe(printbuf_str(&cmd->seq));
 }
 
 /**
diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
index 9c90b3a7dc..48c08f29c3 100644
--- a/kernel/trace/trace_seq.c
+++ b/kernel/trace/trace_seq.c
@@ -25,11 +25,9 @@
  */
 #include <linux/uaccess.h>
 #include <linux/seq_file.h>
+#include <linux/string.h>
 #include <linux/trace_seq.h>
 
-/* How much buffer is left on the trace_seq? */
-#define TRACE_SEQ_BUF_LEFT(s) seq_buf_buffer_left(&(s)->seq)
-
 /*
  * trace_seq should work with being initialized with 0s.
  */
@@ -54,7 +52,7 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
 
 	__trace_seq_init(s);
 
-	ret = seq_buf_print_seq(m, &s->seq);
+	ret = seq_write(m, s->seq.buf, printbuf_written(&s->seq));
 
 	/*
 	 * Only reset this buffer if we successfully wrote to the
@@ -80,7 +78,7 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
  */
 void trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 	va_list ap;
 
 	if (s->full)
@@ -89,12 +87,12 @@ void trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
 	__trace_seq_init(s);
 
 	va_start(ap, fmt);
-	seq_buf_vprintf(&s->seq, fmt, ap);
+	prt_vprintf(&s->seq, fmt, ap);
 	va_end(ap);
 
 	/* If we can't write it all, don't bother writing anything */
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 	}
 }
@@ -111,17 +109,17 @@ EXPORT_SYMBOL_GPL(trace_seq_printf);
 void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp,
 		      int nmaskbits)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 
 	if (s->full)
 		return;
 
 	__trace_seq_init(s);
 
-	seq_buf_printf(&s->seq, "%*pb", nmaskbits, maskp);
+	prt_printf(&s->seq, "%*pb", nmaskbits, maskp);
 
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 	}
 }
@@ -140,18 +138,18 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
  */
 void trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 
 	if (s->full)
 		return;
 
 	__trace_seq_init(s);
 
-	seq_buf_vprintf(&s->seq, fmt, args);
+	prt_vprintf(&s->seq, fmt, args);
 
 	/* If we can't write it all, don't bother writing anything */
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 	}
 }
@@ -174,18 +172,18 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf);
  */
 void trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 
 	if (s->full)
 		return;
 
 	__trace_seq_init(s);
 
-	seq_buf_bprintf(&s->seq, fmt, binary);
+	prt_bstrprintf(&s->seq, fmt, binary);
 
 	/* If we can't write it all, don't bother writing anything */
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(!printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 		return;
 	}
@@ -211,12 +209,12 @@ void trace_seq_puts(struct trace_seq *s, const char *str)
 
 	__trace_seq_init(s);
 
-	if (len > TRACE_SEQ_BUF_LEFT(s)) {
+	if (len > printbuf_remaining(&s->seq)) {
 		s->full = 1;
 		return;
 	}
 
-	seq_buf_putmem(&s->seq, str, len);
+	prt_bytes(&s->seq, str, len);
 }
 EXPORT_SYMBOL_GPL(trace_seq_puts);
 
@@ -237,12 +235,12 @@ void trace_seq_putc(struct trace_seq *s, unsigned char c)
 
 	__trace_seq_init(s);
 
-	if (TRACE_SEQ_BUF_LEFT(s) < 1) {
+	if (!printbuf_remaining(&s->seq)) {
 		s->full = 1;
 		return;
 	}
 
-	seq_buf_putc(&s->seq, c);
+	prt_char(&s->seq, c);
 }
 EXPORT_SYMBOL_GPL(trace_seq_putc);
 
@@ -263,12 +261,12 @@ void trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len)
 
 	__trace_seq_init(s);
 
-	if (len > TRACE_SEQ_BUF_LEFT(s)) {
+	if (len > printbuf_remaining(&s->seq)) {
 		s->full = 1;
 		return;
 	}
 
-	seq_buf_putmem(&s->seq, mem, len);
+	prt_bytes(&s->seq, mem, len);
 }
 EXPORT_SYMBOL_GPL(trace_seq_putmem);
 
@@ -285,24 +283,17 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem);
 void trace_seq_putmem_hex(struct trace_seq *s, const void *mem,
 			 unsigned int len)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 
 	if (s->full)
 		return;
 
 	__trace_seq_init(s);
 
-	/* Each byte is represented by two chars */
-	if (len * 2 > TRACE_SEQ_BUF_LEFT(s)) {
-		s->full = 1;
-		return;
-	}
+	prt_hex_bytes(&s->seq, mem, len, 8, ' ');
 
-	/* The added spaces can still cause an overflow */
-	seq_buf_putmem_hex(&s->seq, mem, len);
-
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 		return;
 	}
@@ -323,22 +314,22 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
  */
 int trace_seq_path(struct trace_seq *s, const struct path *path)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 
 	if (s->full)
 		return 0;
 
 	__trace_seq_init(s);
 
-	if (TRACE_SEQ_BUF_LEFT(s) < 1) {
+	if (printbuf_remaining(&s->seq) < 1) {
 		s->full = 1;
 		return 0;
 	}
 
-	seq_buf_path(&s->seq, path, "\n");
+	prt_path(&s->seq, path, "\n");
 
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 		return 0;
 	}
@@ -369,8 +360,25 @@ EXPORT_SYMBOL_GPL(trace_seq_path);
  */
 int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, int cnt)
 {
+	int ret, len;
+
 	__trace_seq_init(s);
-	return seq_buf_to_user(&s->seq, ubuf, cnt);
+
+	len = printbuf_written(&s->seq);
+	if (len <= s->readpos)
+		return -EBUSY;
+
+	len -= s->readpos;
+	if (cnt > len)
+		cnt = len;
+	ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
+	if (ret == cnt)
+		return -EFAULT;
+
+	cnt -= ret;
+
+	s->readpos += cnt;
+	return cnt;
 }
 EXPORT_SYMBOL_GPL(trace_seq_to_user);
 
@@ -378,24 +386,19 @@ int trace_seq_hex_dump(struct trace_seq *s, const char *prefix_str,
 		       int prefix_type, int rowsize, int groupsize,
 		       const void *buf, size_t len, bool ascii)
 {
-	unsigned int save_len = s->seq.len;
+	unsigned int save_pos = s->seq.pos;
 
 	if (s->full)
 		return 0;
 
 	__trace_seq_init(s);
 
-	if (TRACE_SEQ_BUF_LEFT(s) < 1) {
-		s->full = 1;
-		return 0;
-	}
-
-	seq_buf_hex_dump(&(s->seq), prefix_str,
-		   prefix_type, rowsize, groupsize,
-		   buf, len, ascii);
+	prt_hex_dump(&s->seq, buf, len,
+		     prefix_str, prefix_type,
+		     rowsize, groupsize, ascii);
 
-	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
-		s->seq.len = save_len;
+	if (unlikely(printbuf_overflowed(&s->seq))) {
+		s->seq.pos = save_pos;
 		s->full = 1;
 		return 0;
 	}
-- 
2.36.1


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

* [PATCH v4 34/34] Delete seq_buf
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (32 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 33/34] tracing: Convert to printbuf Kent Overstreet
@ 2022-06-20  0:42 ` Kent Overstreet
  2022-06-20  4:19 ` [PATCH v4 00/34] Printbufs - new data structure for building strings David Laight
                   ` (2 subsequent siblings)
  36 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20  0:42 UTC (permalink / raw)
  To: linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy

No longer has any users, so delete it.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 include/linux/seq_buf.h | 162 ----------------
 lib/Makefile            |   2 +-
 lib/seq_buf.c           | 397 ----------------------------------------
 3 files changed, 1 insertion(+), 560 deletions(-)
 delete mode 100644 include/linux/seq_buf.h
 delete mode 100644 lib/seq_buf.c

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
deleted file mode 100644
index 5b31c51479..0000000000
--- a/include/linux/seq_buf.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _LINUX_SEQ_BUF_H
-#define _LINUX_SEQ_BUF_H
-
-#include <linux/fs.h>
-
-/*
- * Trace sequences are used to allow a function to call several other functions
- * to create a string of data to use.
- */
-
-/**
- * seq_buf - seq buffer structure
- * @buffer:	pointer to the buffer
- * @size:	size of the buffer
- * @len:	the amount of data inside the buffer
- * @readpos:	The next position to read in the buffer.
- */
-struct seq_buf {
-	char			*buffer;
-	size_t			size;
-	size_t			len;
-	loff_t			readpos;
-};
-
-static inline void seq_buf_clear(struct seq_buf *s)
-{
-	s->len = 0;
-	s->readpos = 0;
-}
-
-static inline void
-seq_buf_init(struct seq_buf *s, char *buf, unsigned int size)
-{
-	s->buffer = buf;
-	s->size = size;
-	seq_buf_clear(s);
-}
-
-/*
- * seq_buf have a buffer that might overflow. When this happens
- * the len and size are set to be equal.
- */
-static inline bool
-seq_buf_has_overflowed(struct seq_buf *s)
-{
-	return s->len > s->size;
-}
-
-static inline void
-seq_buf_set_overflow(struct seq_buf *s)
-{
-	s->len = s->size + 1;
-}
-
-/*
- * How much buffer is left on the seq_buf?
- */
-static inline unsigned int
-seq_buf_buffer_left(struct seq_buf *s)
-{
-	if (seq_buf_has_overflowed(s))
-		return 0;
-
-	return s->size - s->len;
-}
-
-/* How much buffer was written? */
-static inline unsigned int seq_buf_used(struct seq_buf *s)
-{
-	return min(s->len, s->size);
-}
-
-/**
- * seq_buf_terminate - Make sure buffer is nul terminated
- * @s: the seq_buf descriptor to terminate.
- *
- * This makes sure that the buffer in @s is nul terminated and
- * safe to read as a string.
- *
- * Note, if this is called when the buffer has overflowed, then
- * the last byte of the buffer is zeroed, and the len will still
- * point passed it.
- *
- * After this function is called, s->buffer is safe to use
- * in string operations.
- */
-static inline void seq_buf_terminate(struct seq_buf *s)
-{
-	if (WARN_ON(s->size == 0))
-		return;
-
-	if (seq_buf_buffer_left(s))
-		s->buffer[s->len] = 0;
-	else
-		s->buffer[s->size - 1] = 0;
-}
-
-/**
- * seq_buf_get_buf - get buffer to write arbitrary data to
- * @s: the seq_buf handle
- * @bufp: the beginning of the buffer is stored here
- *
- * Return the number of bytes available in the buffer, or zero if
- * there's no space.
- */
-static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp)
-{
-	WARN_ON(s->len > s->size + 1);
-
-	if (s->len < s->size) {
-		*bufp = s->buffer + s->len;
-		return s->size - s->len;
-	}
-
-	*bufp = NULL;
-	return 0;
-}
-
-/**
- * seq_buf_commit - commit data to the buffer
- * @s: the seq_buf handle
- * @num: the number of bytes to commit
- *
- * Commit @num bytes of data written to a buffer previously acquired
- * by seq_buf_get.  To signal an error condition, or that the data
- * didn't fit in the available space, pass a negative @num value.
- */
-static inline void seq_buf_commit(struct seq_buf *s, int num)
-{
-	if (num < 0) {
-		seq_buf_set_overflow(s);
-	} else {
-		/* num must be negative on overflow */
-		BUG_ON(s->len + num > s->size);
-		s->len += num;
-	}
-}
-
-extern __printf(2, 3)
-int seq_buf_printf(struct seq_buf *s, const char *fmt, ...);
-extern __printf(2, 0)
-int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args);
-extern int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s);
-extern int seq_buf_to_user(struct seq_buf *s, char __user *ubuf,
-			   int cnt);
-extern int seq_buf_puts(struct seq_buf *s, const char *str);
-extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
-extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
-extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
-			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
-extern int seq_buf_hex_dump(struct seq_buf *s, const char *prefix_str,
-			    int prefix_type, int rowsize, int groupsize,
-			    const void *buf, size_t len, bool ascii);
-
-#ifdef CONFIG_BINARY_PRINTF
-extern int
-seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary);
-#endif
-
-#endif /* _LINUX_SEQ_BUF_H */
diff --git a/lib/Makefile b/lib/Makefile
index b520024852..f8ad89dc9a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,7 +32,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 idr.o extable.o sha1.o irq_regs.o argv_split.o \
 	 flex_proportions.o ratelimit.o show_mem.o \
 	 is_single_threaded.o plist.o decompress.o kobject_uevent.o \
-	 earlycpio.o seq_buf.o siphash.o dec_and_lock.o \
+	 earlycpio.o siphash.o dec_and_lock.o \
 	 nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o \
 	 buildid.o printbuf.o pretty-printers.o
 
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
deleted file mode 100644
index 0a68f7aa85..0000000000
--- a/lib/seq_buf.c
+++ /dev/null
@@ -1,397 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * seq_buf.c
- *
- * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
- *
- * The seq_buf is a handy tool that allows you to pass a descriptor around
- * to a buffer that other functions can write to. It is similar to the
- * seq_file functionality but has some differences.
- *
- * To use it, the seq_buf must be initialized with seq_buf_init().
- * This will set up the counters within the descriptor. You can call
- * seq_buf_init() more than once to reset the seq_buf to start
- * from scratch.
- */
-#include <linux/uaccess.h>
-#include <linux/seq_file.h>
-#include <linux/seq_buf.h>
-
-/**
- * seq_buf_can_fit - can the new data fit in the current buffer?
- * @s: the seq_buf descriptor
- * @len: The length to see if it can fit in the current buffer
- *
- * Returns true if there's enough unused space in the seq_buf buffer
- * to fit the amount of new data according to @len.
- */
-static bool seq_buf_can_fit(struct seq_buf *s, size_t len)
-{
-	return s->len + len <= s->size;
-}
-
-/**
- * seq_buf_print_seq - move the contents of seq_buf into a seq_file
- * @m: the seq_file descriptor that is the destination
- * @s: the seq_buf descriptor that is the source.
- *
- * Returns zero on success, non zero otherwise
- */
-int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s)
-{
-	unsigned int len = seq_buf_used(s);
-
-	return seq_write(m, s->buffer, len);
-}
-
-/**
- * seq_buf_vprintf - sequence printing of information.
- * @s: seq_buf descriptor
- * @fmt: printf format string
- * @args: va_list of arguments from a printf() type function
- *
- * Writes a vnprintf() format into the sequencce buffer.
- *
- * Returns zero on success, -1 on overflow.
- */
-int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
-{
-	int len;
-
-	WARN_ON(s->size == 0);
-
-	if (s->len < s->size) {
-		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
-		if (s->len + len < s->size) {
-			s->len += len;
-			return 0;
-		}
-	}
-	seq_buf_set_overflow(s);
-	return -1;
-}
-
-/**
- * seq_buf_printf - sequence printing of information
- * @s: seq_buf descriptor
- * @fmt: printf format string
- *
- * Writes a printf() format into the sequence buffer.
- *
- * Returns zero on success, -1 on overflow.
- */
-int seq_buf_printf(struct seq_buf *s, const char *fmt, ...)
-{
-	va_list ap;
-	int ret;
-
-	va_start(ap, fmt);
-	ret = seq_buf_vprintf(s, fmt, ap);
-	va_end(ap);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(seq_buf_printf);
-
-#ifdef CONFIG_BINARY_PRINTF
-/**
- * seq_buf_bprintf - Write the printf string from binary arguments
- * @s: seq_buf descriptor
- * @fmt: The format string for the @binary arguments
- * @binary: The binary arguments for @fmt.
- *
- * When recording in a fast path, a printf may be recorded with just
- * saving the format and the arguments as they were passed to the
- * function, instead of wasting cycles converting the arguments into
- * ASCII characters. Instead, the arguments are saved in a 32 bit
- * word array that is defined by the format string constraints.
- *
- * This function will take the format and the binary array and finish
- * the conversion into the ASCII string within the buffer.
- *
- * Returns zero on success, -1 on overflow.
- */
-int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
-{
-	unsigned int len = seq_buf_buffer_left(s);
-	int ret;
-
-	WARN_ON(s->size == 0);
-
-	if (s->len < s->size) {
-		ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
-		if (s->len + ret < s->size) {
-			s->len += ret;
-			return 0;
-		}
-	}
-	seq_buf_set_overflow(s);
-	return -1;
-}
-#endif /* CONFIG_BINARY_PRINTF */
-
-/**
- * seq_buf_puts - sequence printing of simple string
- * @s: seq_buf descriptor
- * @str: simple string to record
- *
- * Copy a simple string into the sequence buffer.
- *
- * Returns zero on success, -1 on overflow
- */
-int seq_buf_puts(struct seq_buf *s, const char *str)
-{
-	size_t len = strlen(str);
-
-	WARN_ON(s->size == 0);
-
-	/* Add 1 to len for the trailing null byte which must be there */
-	len += 1;
-
-	if (seq_buf_can_fit(s, len)) {
-		memcpy(s->buffer + s->len, str, len);
-		/* Don't count the trailing null byte against the capacity */
-		s->len += len - 1;
-		return 0;
-	}
-	seq_buf_set_overflow(s);
-	return -1;
-}
-
-/**
- * seq_buf_putc - sequence printing of simple character
- * @s: seq_buf descriptor
- * @c: simple character to record
- *
- * Copy a single character into the sequence buffer.
- *
- * Returns zero on success, -1 on overflow
- */
-int seq_buf_putc(struct seq_buf *s, unsigned char c)
-{
-	WARN_ON(s->size == 0);
-
-	if (seq_buf_can_fit(s, 1)) {
-		s->buffer[s->len++] = c;
-		return 0;
-	}
-	seq_buf_set_overflow(s);
-	return -1;
-}
-
-/**
- * seq_buf_putmem - write raw data into the sequenc buffer
- * @s: seq_buf descriptor
- * @mem: The raw memory to copy into the buffer
- * @len: The length of the raw memory to copy (in bytes)
- *
- * There may be cases where raw memory needs to be written into the
- * buffer and a strcpy() would not work. Using this function allows
- * for such cases.
- *
- * Returns zero on success, -1 on overflow
- */
-int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
-{
-	WARN_ON(s->size == 0);
-
-	if (seq_buf_can_fit(s, len)) {
-		memcpy(s->buffer + s->len, mem, len);
-		s->len += len;
-		return 0;
-	}
-	seq_buf_set_overflow(s);
-	return -1;
-}
-
-#define MAX_MEMHEX_BYTES	8U
-#define HEX_CHARS		(MAX_MEMHEX_BYTES*2 + 1)
-
-/**
- * seq_buf_putmem_hex - write raw memory into the buffer in ASCII hex
- * @s: seq_buf descriptor
- * @mem: The raw memory to write its hex ASCII representation of
- * @len: The length of the raw memory to copy (in bytes)
- *
- * This is similar to seq_buf_putmem() except instead of just copying the
- * raw memory into the buffer it writes its ASCII representation of it
- * in hex characters.
- *
- * Returns zero on success, -1 on overflow
- */
-int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
-		       unsigned int len)
-{
-	unsigned char hex[HEX_CHARS];
-	const unsigned char *data = mem;
-	unsigned int start_len;
-	int i, j;
-
-	WARN_ON(s->size == 0);
-
-	BUILD_BUG_ON(MAX_MEMHEX_BYTES * 2 >= HEX_CHARS);
-
-	while (len) {
-		start_len = min(len, MAX_MEMHEX_BYTES);
-#ifdef __BIG_ENDIAN
-		for (i = 0, j = 0; i < start_len; i++) {
-#else
-		for (i = start_len-1, j = 0; i >= 0; i--) {
-#endif
-			hex[j++] = hex_asc_hi(data[i]);
-			hex[j++] = hex_asc_lo(data[i]);
-		}
-		if (WARN_ON_ONCE(j == 0 || j/2 > len))
-			break;
-
-		/* j increments twice per loop */
-		hex[j++] = ' ';
-
-		seq_buf_putmem(s, hex, j);
-		if (seq_buf_has_overflowed(s))
-			return -1;
-
-		len -= start_len;
-		data += start_len;
-	}
-	return 0;
-}
-
-/**
- * seq_buf_path - copy a path into the sequence buffer
- * @s: seq_buf descriptor
- * @path: path to write into the sequence buffer.
- * @esc: set of characters to escape in the output
- *
- * Write a path name into the sequence buffer.
- *
- * Returns the number of written bytes on success, -1 on overflow
- */
-int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
-{
-	char *buf;
-	size_t size = seq_buf_get_buf(s, &buf);
-	int res = -1;
-
-	WARN_ON(s->size == 0);
-
-	if (size) {
-		char *p = d_path(path, buf, size);
-		if (!IS_ERR(p)) {
-			char *end = mangle_path(buf, p, esc);
-			if (end)
-				res = end - buf;
-		}
-	}
-	seq_buf_commit(s, res);
-
-	return res;
-}
-
-/**
- * seq_buf_to_user - copy the sequence buffer to user space
- * @s: seq_buf descriptor
- * @ubuf: The userspace memory location to copy to
- * @cnt: The amount to copy
- *
- * Copies the sequence buffer into the userspace memory pointed to
- * by @ubuf. It starts from the last read position (@s->readpos)
- * and writes up to @cnt characters or till it reaches the end of
- * the content in the buffer (@s->len), which ever comes first.
- *
- * On success, it returns a positive number of the number of bytes
- * it copied.
- *
- * On failure it returns -EBUSY if all of the content in the
- * sequence has been already read, which includes nothing in the
- * sequence (@s->len == @s->readpos).
- *
- * Returns -EFAULT if the copy to userspace fails.
- */
-int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt)
-{
-	int len;
-	int ret;
-
-	if (!cnt)
-		return 0;
-
-	len = seq_buf_used(s);
-
-	if (len <= s->readpos)
-		return -EBUSY;
-
-	len -= s->readpos;
-	if (cnt > len)
-		cnt = len;
-	ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
-	if (ret == cnt)
-		return -EFAULT;
-
-	cnt -= ret;
-
-	s->readpos += cnt;
-	return cnt;
-}
-
-/**
- * seq_buf_hex_dump - print formatted hex dump into the sequence buffer
- * @s: seq_buf descriptor
- * @prefix_str: string to prefix each line with;
- *  caller supplies trailing spaces for alignment if desired
- * @prefix_type: controls whether prefix of an offset, address, or none
- *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
- * @rowsize: number of bytes to print per line; must be 16 or 32
- * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
- * @buf: data blob to dump
- * @len: number of bytes in the @buf
- * @ascii: include ASCII after the hex output
- *
- * Function is an analogue of print_hex_dump() and thus has similar interface.
- *
- * linebuf size is maximal length for one line.
- * 32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
- *	separating space
- * 2 - spaces separating hex dump and ascii representation
- * 32 - ascii representation
- * 1 - terminating '\0'
- *
- * Returns zero on success, -1 on overflow
- */
-int seq_buf_hex_dump(struct seq_buf *s, const char *prefix_str, int prefix_type,
-		     int rowsize, int groupsize,
-		     const void *buf, size_t len, bool ascii)
-{
-	const u8 *ptr = buf;
-	int i, linelen, remaining = len;
-	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
-	int ret;
-
-	if (rowsize != 16 && rowsize != 32)
-		rowsize = 16;
-
-	for (i = 0; i < len; i += rowsize) {
-		linelen = min(remaining, rowsize);
-		remaining -= rowsize;
-
-		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
-				   linebuf, sizeof(linebuf), ascii);
-
-		switch (prefix_type) {
-		case DUMP_PREFIX_ADDRESS:
-			ret = seq_buf_printf(s, "%s%p: %s\n",
-			       prefix_str, ptr + i, linebuf);
-			break;
-		case DUMP_PREFIX_OFFSET:
-			ret = seq_buf_printf(s, "%s%.8x: %s\n",
-					     prefix_str, i, linebuf);
-			break;
-		default:
-			ret = seq_buf_printf(s, "%s%s\n", prefix_str, linebuf);
-			break;
-		}
-		if (ret)
-			return ret;
-	}
-	return 0;
-}
-- 
2.36.1


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

* RE: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (33 preceding siblings ...)
  2022-06-20  0:42 ` [PATCH v4 34/34] Delete seq_buf Kent Overstreet
@ 2022-06-20  4:19 ` David Laight
  2022-06-20  4:54   ` Matthew Wilcox
                     ` (2 more replies)
  2022-06-21  6:11 ` Rasmus Villemoes
  2022-07-19 23:15 ` Steven Rostedt
  36 siblings, 3 replies; 86+ messages in thread
From: David Laight @ 2022-06-20  4:19 UTC (permalink / raw)
  To: 'Kent Overstreet', linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, linux, willy

From: Kent Overstreet
> Sent: 20 June 2022 01:42
> 
> Previous discussions:
> https://lore.kernel.org/all/20220419203202.2670193-1-kent.overstreet@gmail.com/
> https://lore.kernel.org/all/20220519172421.162394-1-kent.overstreet@gmail.com/
> https://lore.kernel.org/all/20220604193042.1674951-1-kent.overstreet@gmail.com/
> 
> Git repo:
> https://evilpiepirate.org/git/bcachefs.git/log/?h=printbuf_v4
> 
> Changes since v3:
>   Bugfixes and performance improvements, the latest iteration of this patch
>   series has been baking in the bcachefs tree and that shook out some bugs.
> 
>   Rasmus pointed out that -fno-strict-aliasing is going to cause gcc to generate
>   nasty code, and indeed it unfortunately does but according to worst case
>   scenario microbenchmarks it's not a problem for actual performance.

Just copy some of the structure members to local variables
and, if necessary, write them back at the end.

> Using
>   memcpy() and memset() in the printbuf helpers _was_ a problem for performance,
>   so that's been fixed.
> 
> -----------
> 
> Core idea: Wouldn't it be nice if we had a common data structure and calling
> convention for outputting strings?
> 
> The core concept this patch series is aimed at cleaning up and standardizing is
> that of a "pretty-printer", which is now a function like prt_foo() or
> foo_to_text():
> 
>     void foo_to_text(struct printbuf *out, struct foo)
> 
> What this patch series does or enables:
> 
>  - It becomes quite a bit easier to write composable pretty printers! This is
>    huge.
> 
>  - A ton of code that works in terms of raw char * pointers and lengths
>    (snprintf style, and many weird variations) gets cleaned up, with error prone
>    raw pointers arithmetic replaced by proper helpers
> 
>  - A ton of code that emits either directly via printk() or to other places
>    (sysfs, debugfs) can now output to printbufs, and becomes more reusable and
>    composable
> 
>  - Countesy of Matthew Wilcox, the new and very cool %pf() format string, which
>    allows passing a pretty printer function and its arguments to sprintf() and
>    family. This means we can now call type specific pretty-printers without
>    adding them to lib/vsprintf.c and writing a bunch of crazy
>    parsing-and-dispatch code. For example,
> 
>      printk("%pd", dentry);
> 
>    becomes
> 
>      printk("%pf(%p)", prt_dentry, dentry);
> 
>    My OOM debugging & reporting patch series that builds off of this uses this
>    to solve a very real problem that Michal Hocko brought up at LSF - with this
>    we write shrinkers_to_text(), slab_to_text() which can _also_ now be used for
>    reporting in debugfs (which Roman has been working on), as well as in the
>    show_mem() report - the "%pf()" syntax lets us print the output of those
>    functions without allocating (and having to preallocate) a separate buffer.

I really think that is a bad idea.
printk() already uses a lot of stack, anything doing a recursive
call is just making that worse.
Especially since these calls can often be in error paths
which are not often tested and can already be on deep stacks.

	David

> 
>  - Some new formatting helpers:
> 
>    Nicely aligned text is much easier to read, and something that we want a
>    _lot, but outputting nicely aligned text with printf() is a pain in the ass.
>    Printbufs add tabstops, which can be used for right or left justification -
>    simple, easy. prt_tab() emits spaces up to the next tabstop, prt_tab_rjust()
>    advances to the next tabstop right justifying text since the previous
>    tabstop.
> 
>    Printbufs also add an indent level, obeyed by prt_newline() which can be very
>    useful for multi line output.
> 
>    In the future, \n and \t in format strings may learn to obey these as well.
> 
>  - Optional heap allocation - no need to statically allocate buffers on the
>    stack and guess at the output size.
> 
>  - Lots of consolidating and refactoring
> 
>    This series replaces seq_buf, which does basically what an earlier version of
>    printbufs did.
> 
>    A good chunk of lib/string_helpers.c, as well as lib/hexdump.c are converted
>    (and simplified!).
> 
>    Pretty printers in lib/vsprintf.c previously outputted to buffers on the
>    stack and then copied _that_ to the actual output buffer, that's all gone
>    (replaced by proper helpers for outputting chars and strings), and they also
>    used printf_spec for argument passing in ad-hoc ways. This patch series does
>    a lot towards converting them to more standard pretty printers that can be
>    called via %pf() instead of having to live in lib/vsprintf.c. Still to do:
>    format string decoding for argument passing is a mess that's scattered all
>    over the place.
> 
> In the course of working on this patch series, I've spotted a _lot_ more
> consolidation and refactoring that needs to be done - we've got a ton of API
> fragmentation leading to lots of code duplication.
> 
> But I'm already really excited about what this patch series enables.
> 
> Cheers!
> 
> Kent Overstreet (34):
>   lib/printbuf: New data structure for printing strings
>   lib/string_helpers: Convert string_escape_mem() to printbuf
>   vsprintf: Convert to printbuf
>   lib/hexdump: 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: prt_string_option(), prt_bitflags()
>   vsprintf: Improve number()
>   vsprintf: prt_u64_minwidth(), prt_u64()
>   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
>   tracing: trace_events_synth: Convert to printbuf
>   d_path: prt_path()
>   ACPI/APEI: Add missing include
>   tracing: Convert to printbuf
>   Delete seq_buf
> 
>  Documentation/core-api/printk-formats.rst |   22 +
>  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/acpi/apei/erst-dbg.c              |    1 +
>  drivers/clk/tegra/clk-bpmp.c              |   21 +-
>  drivers/input/joystick/analog.c           |   23 +-
>  drivers/pci/p2pdma.c                      |   21 +-
>  fs/d_path.c                               |   35 +
>  include/linux/dcache.h                    |    1 +
>  include/linux/kernel.h                    |   12 +
>  include/linux/pretty-printers.h           |   10 +
>  include/linux/printbuf.h                  |  253 +++
>  include/linux/seq_buf.h                   |  162 --
>  include/linux/string.h                    |    5 +
>  include/linux/string_helpers.h            |    8 +-
>  include/linux/trace_events.h              |    2 +-
>  include/linux/trace_seq.h                 |   17 +-
>  kernel/trace/trace.c                      |   45 +-
>  kernel/trace/trace_dynevent.c             |   34 +-
>  kernel/trace/trace_events_filter.c        |    2 +-
>  kernel/trace/trace_events_synth.c         |   32 +-
>  kernel/trace/trace_functions_graph.c      |    6 +-
>  kernel/trace/trace_kprobe.c               |    2 +-
>  kernel/trace/trace_seq.c                  |  111 +-
>  lib/Makefile                              |    4 +-
>  lib/hexdump.c                             |  246 +--
>  lib/pretty-printers.c                     |   60 +
>  lib/printbuf.c                            |  253 +++
>  lib/seq_buf.c                             |  397 -----
>  lib/string_helpers.c                      |  224 +--
>  lib/test_hexdump.c                        |   30 +-
>  lib/test_printf.c                         |   33 +-
>  lib/vsprintf.c                            | 1723 ++++++++++-----------
>  mm/memcontrol.c                           |   68 +-
>  tools/testing/nvdimm/test/ndtest.c        |   22 +-
>  37 files changed, 2050 insertions(+), 1976 deletions(-)
>  create mode 100644 include/linux/pretty-printers.h
>  create mode 100644 include/linux/printbuf.h
>  delete mode 100644 include/linux/seq_buf.h
>  create mode 100644 lib/pretty-printers.c
>  create mode 100644 lib/printbuf.c
>  delete mode 100644 lib/seq_buf.c
> 
> --
> 2.36.1

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


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

* RE: [PATCH v4 01/34] lib/printbuf: New data structure for printing strings
  2022-06-20  0:42 ` [PATCH v4 01/34] lib/printbuf: New data structure for printing strings Kent Overstreet
@ 2022-06-20  4:44   ` David Laight
  2022-06-20 15:30     ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: David Laight @ 2022-06-20  4:44 UTC (permalink / raw)
  To: 'Kent Overstreet', linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, linux, willy

From: Kent Overstreet
> Sent: 20 June 2022 01:42
> 
> 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>
> ---
>  include/linux/printbuf.h | 122 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 122 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..8186c447ca
> --- /dev/null
> +++ b/include/linux/printbuf.h
> @@ -0,0 +1,122 @@
> +/* SPDX-License-Identifier: LGPL-2.1+ */
> +/* Copyright (C) 2022 Kent Overstreet */
> +
> +#ifndef _LINUX_PRINTBUF_H
> +#define _LINUX_PRINTBUF_H
> +
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +
> +/*
> + * Printbufs: String buffer for outputting (printing) to, for vsnprintf
> + */
> +
> +struct printbuf {
> +	char			*buf;
> +	unsigned		size;
> +	unsigned		pos;

No naked unsigneds.

> +};
> +
> +/*
> + * Returns size remaining of output buffer:
> + */
> +static inline unsigned printbuf_remaining_size(struct printbuf *out)
> +{
> +	return out->pos < out->size ? out->size - out->pos : 0;
> +}
> +
> +/*
> + * Returns number of characters we can print to the output buffer - i.e.
> + * excluding the terminating nul:
> + */
> +static inline unsigned printbuf_remaining(struct printbuf *out)
> +{
> +	return out->pos < out->size ? out->size - out->pos - 1 : 0;
> +}

Those two are so similar mistakes will be make.
You can also just return negatives when the buffer has overlowed
and get the callers to test < or <= as required.

I also wonder it is necessary to count the total length
when the buffer isn't long enough?
Unless there is a real pressing need for it I'd not bother.
Setting pos == size (after writing the '\0') allows
overflow be detected without most of the dangers.

> +
> +static inline unsigned printbuf_written(struct printbuf *out)
> +{
> +	return min(out->pos, out->size);

That excludes the '\0' for short buffers but includes
it for overlong ones.

> +}
> +
> +/*
> + * Returns true if output was truncated:
> + */
> +static inline bool printbuf_overflowed(struct printbuf *out)
> +{
> +	return 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 __prt_char(struct printbuf *out, char c)
> +{
> +	if (printbuf_remaining(out))
> +		out->buf[out->pos] = c;

At this point it is (should be) always safe to add the '\0'.
Doing so would save the extra conditionals later on.

> +	out->pos++;
> +}
> +
> +static inline void prt_char(struct printbuf *out, char c)
> +{
> +	__prt_char(out, c);
> +	printbuf_nul_terminate(out);
> +}
> +
> +static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
> +{
> +	unsigned i, can_print = min(n, printbuf_remaining(out));
> +
> +	for (i = 0; i < can_print; i++)
> +		out->buf[out->pos++] = c;
> +	out->pos += n - can_print;
> +}
> +
> +static inline void prt_chars(struct printbuf *out, char c, unsigned n)
> +{
> +	__prt_chars(out, c, n);
> +	printbuf_nul_terminate(out);
> +}
> +
> +static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
> +{
> +	unsigned i, can_print = min(n, printbuf_remaining(out));
> +
> +	for (i = 0; i < can_print; i++)
> +		out->buf[out->pos++] = ((char *) b)[i];
> +	out->pos += n - can_print;
> +
> +	printbuf_nul_terminate(out);

jeepers - that can be written so much better.
Something like:
	unsigned int i, pos = out->pos;
	int space = pos - out->size - 1;
	char *tgt = out->buf + pos;
	const char *src = b;
	out->pos = pos + n;

	if (space <= 0)
		return;
	if (n > space)
		n = space;

	for (i = 0; i < n; i++)
		tgt[i] = src[i];
	tgt[1] = 0;

> +}
> +
> +static inline void prt_str(struct printbuf *out, const char *str)
> +{
> +	prt_bytes(out, str, strlen(str));

Do you really need to call strlen() and then process
the buffer byte by byte?

	David

> +}
> +
> +static inline void prt_hex_byte(struct printbuf *out, u8 byte)
> +{
> +	__prt_char(out, hex_asc_hi(byte));
> +	__prt_char(out, hex_asc_lo(byte));
> +	printbuf_nul_terminate(out);
> +}
> +
> +static inline void prt_hex_byte_upper(struct printbuf *out, u8 byte)
> +{
> +	__prt_char(out, hex_asc_upper_hi(byte));
> +	__prt_char(out, hex_asc_upper_lo(byte));
> +	printbuf_nul_terminate(out);
> +}
> +
> +#define PRINTBUF_EXTERN(_buf, _size)			\
> +((struct printbuf) {					\
> +	.buf	= _buf,					\
> +	.size	= _size,				\
> +})
> +
> +#endif /* _LINUX_PRINTBUF_H */
> --
> 2.36.1

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


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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  4:19 ` [PATCH v4 00/34] Printbufs - new data structure for building strings David Laight
@ 2022-06-20  4:54   ` Matthew Wilcox
  2022-06-20  8:00     ` David Laight
  2022-06-20 15:07   ` Kent Overstreet
  2022-06-21  3:11   ` Kent Overstreet
  2 siblings, 1 reply; 86+ messages in thread
From: Matthew Wilcox @ 2022-06-20  4:54 UTC (permalink / raw)
  To: David Laight
  Cc: 'Kent Overstreet',
	linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux

On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> I really think that is a bad idea.
> printk() already uses a lot of stack, anything doing a recursive
> call is just making that worse.
> Especially since these calls can often be in error paths
> which are not often tested and can already be on deep stacks.

You made this complaint last time and I challenged you to provide data.
You have not, as yet, provided data.

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

* RE: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  4:54   ` Matthew Wilcox
@ 2022-06-20  8:00     ` David Laight
  0 siblings, 0 replies; 86+ messages in thread
From: David Laight @ 2022-06-20  8:00 UTC (permalink / raw)
  To: 'Matthew Wilcox'
  Cc: 'Kent Overstreet',
	linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux

From: Matthew Wilcox
> Sent: 20 June 2022 05:55
> 
> On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > I really think that is a bad idea.
> > printk() already uses a lot of stack, anything doing a recursive
> > call is just making that worse.
> > Especially since these calls can often be in error paths
> > which are not often tested and can already be on deep stacks.
> 
> You made this complaint last time and I challenged you to provide data.
> You have not, as yet, provided data.

There is already an issue with printk() needing 2k of stack and
'blowing' the stack in the stack overflow check.
This is with KASAN, but that that probably doesn't make
a massive difference - especially since it has more stack
to play with.

	David

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


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

* Re: [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf
  2022-06-20  0:42 ` [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf Kent Overstreet
@ 2022-06-20 11:37   ` Michal Hocko
  2022-06-20 15:13     ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: Michal Hocko @ 2022-06-20 11:37 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Sun 19-06-22 20:42:23, 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.
> 
> Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>

I have asked for a justification two times already not hearing anything.
Please drop this patch. I do not see any actual advantage of the
conversion. The primary downside of the existing code is that an
internal buffer is exposed to the caller which is error prone and ugly.
The conversion doesn't really address that part.

Moreover there is an inconsistency between failrure case where the
printbuf is destroyed by a docummented way (printbuf_exit) and when the
caller frees the buffer directly. If printbuf_exit evers need to do more
than just kfree (e.g. kvfree) then this is a subtle bug hard to find.

> ---
>  mm/memcontrol.c | 68 ++++++++++++++++++++++++-------------------------
>  1 file changed, 33 insertions(+), 35 deletions(-)
> 
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 598fece89e..57861dc9fe 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -62,7 +62,7 @@
>  #include <linux/file.h>
>  #include <linux/resume_user_mode.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>
> @@ -1461,13 +1461,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.
> @@ -1484,49 +1480,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);
> +		prt_printf(&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);
> +			prt_printf(&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));
> +	prt_printf(&buf, "%s %lu\n", vm_event_name(PGFAULT),
> +	       memcg_events(memcg, PGFAULT));
> +	prt_printf(&buf, "%s %lu\n", vm_event_name(PGMAJFAULT),
> +	       memcg_events(memcg, PGMAJFAULT));
> +	prt_printf(&buf, "%s %lu\n",  vm_event_name(PGREFILL),
> +	       memcg_events(memcg, PGREFILL));
> +	prt_printf(&buf, "pgscan %lu\n",
> +	       memcg_events(memcg, PGSCAN_KSWAPD) +
> +	       memcg_events(memcg, PGSCAN_DIRECT));
> +	prt_printf(&buf, "pgsteal %lu\n",
> +	       memcg_events(memcg, PGSTEAL_KSWAPD) +
> +	       memcg_events(memcg, PGSTEAL_DIRECT));
> +	prt_printf(&buf, "%s %lu\n", vm_event_name(PGACTIVATE),
> +	       memcg_events(memcg, PGACTIVATE));
> +	prt_printf(&buf, "%s %lu\n", vm_event_name(PGDEACTIVATE),
> +	       memcg_events(memcg, PGDEACTIVATE));
> +	prt_printf(&buf, "%s %lu\n", vm_event_name(PGLAZYFREE),
> +	       memcg_events(memcg, PGLAZYFREE));
> +	prt_printf(&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));
> +	prt_printf(&buf, "%s %lu\n", vm_event_name(THP_FAULT_ALLOC),
> +	       memcg_events(memcg, THP_FAULT_ALLOC));
> +	prt_printf(&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.1

-- 
Michal Hocko
SUSE Labs

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  4:19 ` [PATCH v4 00/34] Printbufs - new data structure for building strings David Laight
  2022-06-20  4:54   ` Matthew Wilcox
@ 2022-06-20 15:07   ` Kent Overstreet
  2022-06-20 15:21     ` David Laight
  2022-06-21  0:38     ` Joe Perches
  2022-06-21  3:11   ` Kent Overstreet
  2 siblings, 2 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20 15:07 UTC (permalink / raw)
  To: David Laight
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> From: Kent Overstreet
> >   Rasmus pointed out that -fno-strict-aliasing is going to cause gcc to generate
> >   nasty code, and indeed it unfortunately does but according to worst case
> >   scenario microbenchmarks it's not a problem for actual performance.
> 
> Just copy some of the structure members to local variables
> and, if necessary, write them back at the end.

You must not have read any of the code - half the point of this patch series is
implementing proper helpers for printing chars, strings of bytes, etc. and that
doesn't work if we're not using actual types.

> >      printk("%pd", dentry);
> > 
> >    becomes
> > 
> >      printk("%pf(%p)", prt_dentry, dentry);
> > 
> >    My OOM debugging & reporting patch series that builds off of this uses this
> >    to solve a very real problem that Michal Hocko brought up at LSF - with this
> >    we write shrinkers_to_text(), slab_to_text() which can _also_ now be used for
> >    reporting in debugfs (which Roman has been working on), as well as in the
> >    show_mem() report - the "%pf()" syntax lets us print the output of those
> >    functions without allocating (and having to preallocate) a separate buffer.
> 
> I really think that is a bad idea.
> printk() already uses a lot of stack, anything doing a recursive
> call is just making that worse.
> Especially since these calls can often be in error paths
> which are not often tested and can already be on deep stacks.

We went over this before - this patch series drastically reduces stack usage of
sprintf by eliminating a bunch of stack allocated buffers. Do try to keep up...

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

* Re: [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf
  2022-06-20 11:37   ` Michal Hocko
@ 2022-06-20 15:13     ` Kent Overstreet
  2022-06-20 15:52       ` Michal Hocko
  0 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20 15:13 UTC (permalink / raw)
  To: Michal Hocko
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 01:37:56PM +0200, Michal Hocko wrote:
> On Sun 19-06-22 20:42:23, 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.
> > 
> > Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
> 
> I have asked for a justification two times already not hearing anything.
> Please drop this patch. I do not see any actual advantage of the
> conversion. The primary downside of the existing code is that an
> internal buffer is exposed to the caller which is error prone and ugly.
> The conversion doesn't really address that part.

Do you want to tone down the hostility? Yeesh.

This patch is part of a wider series that deletes seq_buf, if you missed it here
you go: https://lore.kernel.org/all/20220620004233.3805-1-kent.overstreet@gmail.com/

> Moreover there is an inconsistency between failrure case where the
> printbuf is destroyed by a docummented way (printbuf_exit) and when the
> caller frees the buffer directly. If printbuf_exit evers need to do more
> than just kfree (e.g. kvfree) then this is a subtle bug hard to find.

Ok, _that's_ a technical point we can talk about and address. I'll add
documentation to the printbuf code that the buffer must be freeable with
kfree().

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

* RE: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20 15:07   ` Kent Overstreet
@ 2022-06-20 15:21     ` David Laight
  2022-06-21  0:38     ` Joe Perches
  1 sibling, 0 replies; 86+ messages in thread
From: David Laight @ 2022-06-20 15:21 UTC (permalink / raw)
  To: 'Kent Overstreet'
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

From: Kent Overstreet
> Sent: 20 June 2022 16:07

> On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > From: Kent Overstreet
> > >   Rasmus pointed out that -fno-strict-aliasing is going to cause gcc to generate
> > >   nasty code, and indeed it unfortunately does but according to worst case
> > >   scenario microbenchmarks it's not a problem for actual performance.
> >
> > Just copy some of the structure members to local variables
> > and, if necessary, write them back at the end.
> 
> You must not have read any of the code - half the point of this patch series is
> implementing proper helpers for printing chars, strings of bytes, etc. and that
> doesn't work if we're not using actual types.

I'm talking about things like:
	out->buf[out->pos] = c;
(or whatever the field names are.)

Even without strict aliasing the compiler has to reread
'buf' and 'pos' every iteration.

Of course, you might find the compiler decides to 'optimise'
the loop to memcpy() or memset().

	David

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


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

* Re: [PATCH v4 01/34] lib/printbuf: New data structure for printing strings
  2022-06-20  4:44   ` David Laight
@ 2022-06-20 15:30     ` Kent Overstreet
  2022-06-20 15:53       ` David Laight
  0 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20 15:30 UTC (permalink / raw)
  To: David Laight
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 04:44:10AM +0000, David Laight wrote:
> From: Kent Overstreet
> > Sent: 20 June 2022 01:42
> > 
> > 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>
> > ---
> >  include/linux/printbuf.h | 122 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 122 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..8186c447ca
> > --- /dev/null
> > +++ b/include/linux/printbuf.h
> > @@ -0,0 +1,122 @@
> > +/* SPDX-License-Identifier: LGPL-2.1+ */
> > +/* Copyright (C) 2022 Kent Overstreet */
> > +
> > +#ifndef _LINUX_PRINTBUF_H
> > +#define _LINUX_PRINTBUF_H
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/string.h>
> > +
> > +/*
> > + * Printbufs: String buffer for outputting (printing) to, for vsnprintf
> > + */
> > +
> > +struct printbuf {
> > +	char			*buf;
> > +	unsigned		size;
> > +	unsigned		pos;
> 
> No naked unsigneds.

This is the way I've _always_ written kernel code - single word type names.

> 
> > +};
> > +
> > +/*
> > + * Returns size remaining of output buffer:
> > + */
> > +static inline unsigned printbuf_remaining_size(struct printbuf *out)
> > +{
> > +	return out->pos < out->size ? out->size - out->pos : 0;
> > +}
> > +
> > +/*
> > + * Returns number of characters we can print to the output buffer - i.e.
> > + * excluding the terminating nul:
> > + */
> > +static inline unsigned printbuf_remaining(struct printbuf *out)
> > +{
> > +	return out->pos < out->size ? out->size - out->pos - 1 : 0;
> > +}
> 
> Those two are so similar mistakes will be make.

If you've got ideas for better names I'd be happy to hear them - we discussed
this and this was what we came up with.

> You can also just return negatives when the buffer has overlowed
> and get the callers to test < or <= as required.

Yeesh, no.

> I also wonder it is necessary to count the total length
> when the buffer isn't long enough?
> Unless there is a real pressing need for it I'd not bother.
> Setting pos == size (after writing the '\0') allows
> overflow be detected without most of the dangers.

Because that's what snprintf() needs.

> > +
> > +static inline unsigned printbuf_written(struct printbuf *out)
> > +{
> > +	return min(out->pos, out->size);
> 
> That excludes the '\0' for short buffers but includes
> it for overlong ones.

It actually doesn't.

> > +}
> > +
> > +/*
> > + * Returns true if output was truncated:
> > + */
> > +static inline bool printbuf_overflowed(struct printbuf *out)
> > +{
> > +	return 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 __prt_char(struct printbuf *out, char c)
> > +{
> > +	if (printbuf_remaining(out))
> > +		out->buf[out->pos] = c;
> 
> At this point it is (should be) always safe to add the '\0'.
> Doing so would save the extra conditionals later on.

True, but at the cost of making the code less straightforward. I may have a look
at it.

> 
> > +	out->pos++;
> > +}
> > +
> > +static inline void prt_char(struct printbuf *out, char c)
> > +{
> > +	__prt_char(out, c);
> > +	printbuf_nul_terminate(out);
> > +}
> > +
> > +static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
> > +{
> > +	unsigned i, can_print = min(n, printbuf_remaining(out));
> > +
> > +	for (i = 0; i < can_print; i++)
> > +		out->buf[out->pos++] = c;
> > +	out->pos += n - can_print;
> > +}
> > +
> > +static inline void prt_chars(struct printbuf *out, char c, unsigned n)
> > +{
> > +	__prt_chars(out, c, n);
> > +	printbuf_nul_terminate(out);
> > +}
> > +
> > +static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
> > +{
> > +	unsigned i, can_print = min(n, printbuf_remaining(out));
> > +
> > +	for (i = 0; i < can_print; i++)
> > +		out->buf[out->pos++] = ((char *) b)[i];
> > +	out->pos += n - can_print;
> > +
> > +	printbuf_nul_terminate(out);
> 
> jeepers - that can be written so much better.
> Something like:
> 	unsigned int i, pos = out->pos;
> 	int space = pos - out->size - 1;
> 	char *tgt = out->buf + pos;
> 	const char *src = b;
> 	out->pos = pos + n;
> 
> 	if (space <= 0)
> 		return;
> 	if (n > space)
> 		n = space;
> 
> 	for (i = 0; i < n; i++)
> 		tgt[i] = src[i];
> 	tgt[1] = 0;
> 

I find your version considerably harder to read, and I've stared at enough
assembly that I trust the compiler to generate pretty equivalent code.

> > +}
> > +
> > +static inline void prt_str(struct printbuf *out, const char *str)
> > +{
> > +	prt_bytes(out, str, strlen(str));
> 
> Do you really need to call strlen() and then process
> the buffer byte by byte?

Versus introducing a branch to check for nul into the inner loop of prt_bytes()?
You're not serious, are you?

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

* Re: [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf
  2022-06-20 15:13     ` Kent Overstreet
@ 2022-06-20 15:52       ` Michal Hocko
  0 siblings, 0 replies; 86+ messages in thread
From: Michal Hocko @ 2022-06-20 15:52 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon 20-06-22 11:13:56, Kent Overstreet wrote:
> On Mon, Jun 20, 2022 at 01:37:56PM +0200, Michal Hocko wrote:
> > On Sun 19-06-22 20:42:23, 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.
> > > 
> > > Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
> > 
> > I have asked for a justification two times already not hearing anything.
> > Please drop this patch. I do not see any actual advantage of the
> > conversion. The primary downside of the existing code is that an
> > internal buffer is exposed to the caller which is error prone and ugly.
> > The conversion doesn't really address that part.
> 
> Do you want to tone down the hostility? Yeesh.

I have merely pointed out you have ignored my review feedback _twice_
already. Ignoring the review feedback and posting new versions without
questions being addressed is wasting other people's time.

> This patch is part of a wider series that deletes seq_buf, if you missed it here
> you go: https://lore.kernel.org/all/20220620004233.3805-1-kent.overstreet@gmail.com/

Each patch should have its justification. If the reasoning is that
seq_buf is going away then I can live with that. That is not obvious
from this patch which I care about because it falls into area I maintain
and review. Unlike the rest of the large patchset which I do not really
have time to review in its entirety.

> > Moreover there is an inconsistency between failrure case where the
> > printbuf is destroyed by a docummented way (printbuf_exit) and when the
> > caller frees the buffer directly. If printbuf_exit evers need to do more
> > than just kfree (e.g. kvfree) then this is a subtle bug hard to find.
> 
> Ok, _that's_ a technical point we can talk about and address. I'll add
> documentation to the printbuf code that the buffer must be freeable with
> kfree().

Hmm, wouldn't that be just too restrictive without any good reasons?
Maybe there are no seq_buf users currently but if there ever raises a
need for larger buffers then you might want to use kvmalloc for the
allocation and you will need to change all users to use kvfree
(potentially missing some). You could either start requiring kvfree
since the beginning or get rid of exposing internal buffer altogether
and use printbuf_exit in all cases.
-- 
Michal Hocko
SUSE Labs

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

* RE: [PATCH v4 01/34] lib/printbuf: New data structure for printing strings
  2022-06-20 15:30     ` Kent Overstreet
@ 2022-06-20 15:53       ` David Laight
  2022-06-20 16:14         ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: David Laight @ 2022-06-20 15:53 UTC (permalink / raw)
  To: 'Kent Overstreet'
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

From: Kent Overstreet
> Sent: 20 June 2022 16:31
> 
> On Mon, Jun 20, 2022 at 04:44:10AM +0000, David Laight wrote:
> > From: Kent Overstreet
> > > Sent: 20 June 2022 01:42
> > >
> > > 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>
> > > ---
> > >  include/linux/printbuf.h | 122 +++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 122 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..8186c447ca
> > > --- /dev/null
> > > +++ b/include/linux/printbuf.h
> > > @@ -0,0 +1,122 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1+ */
> > > +/* Copyright (C) 2022 Kent Overstreet */
> > > +
> > > +#ifndef _LINUX_PRINTBUF_H
> > > +#define _LINUX_PRINTBUF_H
> > > +
> > > +#include <linux/kernel.h>
> > > +#include <linux/string.h>
> > > +
> > > +/*
> > > + * Printbufs: String buffer for outputting (printing) to, for vsnprintf
> > > + */
> > > +
> > > +struct printbuf {
> > > +	char			*buf;
> > > +	unsigned		size;
> > > +	unsigned		pos;
> >
> > No naked unsigneds.
> 
> This is the way I've _always_ written kernel code - single word type names.

I'm pretty sure the coding standards require 'int'.

> >
> > > +};
> > > +
> > > +/*
> > > + * Returns size remaining of output buffer:
> > > + */
> > > +static inline unsigned printbuf_remaining_size(struct printbuf *out)
> > > +{
> > > +	return out->pos < out->size ? out->size - out->pos : 0;
> > > +}
> > > +
> > > +/*
> > > + * Returns number of characters we can print to the output buffer - i.e.
> > > + * excluding the terminating nul:
> > > + */
> > > +static inline unsigned printbuf_remaining(struct printbuf *out)
> > > +{
> > > +	return out->pos < out->size ? out->size - out->pos - 1 : 0;
> > > +}
> >
> > Those two are so similar mistakes will be make.
> 
> If you've got ideas for better names I'd be happy to hear them - we discussed
> this and this was what we came up with.
> 
> > You can also just return negatives when the buffer has overlowed
> > and get the callers to test < or <= as required.
> 
> Yeesh, no.

Why not?
All the callers are internal.
It saves a test and branch (or cmove).

> > I also wonder it is necessary to count the total length
> > when the buffer isn't long enough?
> > Unless there is a real pressing need for it I'd not bother.
> > Setting pos == size (after writing the '\0') allows
> > overflow be detected without most of the dangers.
> 
> Because that's what snprintf() needs.
> 
> > > +
> > > +static inline unsigned printbuf_written(struct printbuf *out)
> > > +{
> > > +	return min(out->pos, out->size);
> >
> > That excludes the '\0' for short buffers but includes
> > it for overlong ones.
> 
> It actually doesn't.

If size is 2 it goes 0, 1, 2, 2, 2 as bytes are added.
But the string is "" "a" "a" "a" - never 2 characters. 

...
> > > +static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
> > > +{
> > > +	unsigned i, can_print = min(n, printbuf_remaining(out));
> > > +
> > > +	for (i = 0; i < can_print; i++)
> > > +		out->buf[out->pos++] = ((char *) b)[i];
> > > +	out->pos += n - can_print;
> > > +
> > > +	printbuf_nul_terminate(out);
> >
> > jeepers - that can be written so much better.
> > Something like:
> > 	unsigned int i, pos = out->pos;
> > 	int space = pos - out->size - 1;
> > 	char *tgt = out->buf + pos;
> > 	const char *src = b;
> > 	out->pos = pos + n;
> >
> > 	if (space <= 0)
> > 		return;
> > 	if (n > space)
> > 		n = space;
> >
> > 	for (i = 0; i < n; i++)
> > 		tgt[i] = src[i];
> > 	tgt[1] = 0;
> >
> 
> I find your version considerably harder to read, and I've stared at enough
> assembly that I trust the compiler to generate pretty equivalent code.

It can't because it can't assume that out->buf doesn't overlap 'out'.
I'm also pretty sure it can't optimise out the test before adding the '\0'.

> 
> > > +}
> > > +
> > > +static inline void prt_str(struct printbuf *out, const char *str)
> > > +{
> > > +	prt_bytes(out, str, strlen(str));
> >
> > Do you really need to call strlen() and then process
> > the buffer byte by byte?
> 
> Versus introducing a branch to check for nul into the inner loop of prt_bytes()?
> You're not serious, are you?

As opposed to the one in strlen() ?
I realise that there are shift and mask algorithms from strlen()
that are likely faster than a byte scan on 64 bit systems.
But they are likely slower than the check when you have a loop
that is scanning byte by byte.
This is especially true on out of order superscaler cpu when
the copy loop won't be using all the execution blocks.

What might be faster on cpu (like x86) where misaligned memory
access are almost entirely free is to copy 8 bytes at a time
while checking for a zero at the same time.

Remember kernel strings are quite often short, the overhead
costs for 'fast' routines slow things down.
(As you found when calling memcpy() and memset().)

	David

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


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

* Re: [PATCH v4 01/34] lib/printbuf: New data structure for printing strings
  2022-06-20 15:53       ` David Laight
@ 2022-06-20 16:14         ` Kent Overstreet
  0 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-20 16:14 UTC (permalink / raw)
  To: David Laight
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 03:53:38PM +0000, David Laight wrote:
> From: Kent Overstreet
> > Sent: 20 June 2022 16:31
> > 
> > On Mon, Jun 20, 2022 at 04:44:10AM +0000, David Laight wrote:
> > > From: Kent Overstreet
> > > > Sent: 20 June 2022 01:42
> > > >
> > > > 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>
> > > > ---
> > > >  include/linux/printbuf.h | 122 +++++++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 122 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..8186c447ca
> > > > --- /dev/null
> > > > +++ b/include/linux/printbuf.h
> > > > @@ -0,0 +1,122 @@
> > > > +/* SPDX-License-Identifier: LGPL-2.1+ */
> > > > +/* Copyright (C) 2022 Kent Overstreet */
> > > > +
> > > > +#ifndef _LINUX_PRINTBUF_H
> > > > +#define _LINUX_PRINTBUF_H
> > > > +
> > > > +#include <linux/kernel.h>
> > > > +#include <linux/string.h>
> > > > +
> > > > +/*
> > > > + * Printbufs: String buffer for outputting (printing) to, for vsnprintf
> > > > + */
> > > > +
> > > > +struct printbuf {
> > > > +	char			*buf;
> > > > +	unsigned		size;
> > > > +	unsigned		pos;
> > >
> > > No naked unsigneds.
> > 
> > This is the way I've _always_ written kernel code - single word type names.
> 
> I'm pretty sure the coding standards require 'int'.

I've been contributing code to the kernel for many years and I'm picky about my
style, I'm not about to change now.

> 
> > >
> > > > +};
> > > > +
> > > > +/*
> > > > + * Returns size remaining of output buffer:
> > > > + */
> > > > +static inline unsigned printbuf_remaining_size(struct printbuf *out)
> > > > +{
> > > > +	return out->pos < out->size ? out->size - out->pos : 0;
> > > > +}
> > > > +
> > > > +/*
> > > > + * Returns number of characters we can print to the output buffer - i.e.
> > > > + * excluding the terminating nul:
> > > > + */
> > > > +static inline unsigned printbuf_remaining(struct printbuf *out)
> > > > +{
> > > > +	return out->pos < out->size ? out->size - out->pos - 1 : 0;
> > > > +}
> > >
> > > Those two are so similar mistakes will be make.
> > 
> > If you've got ideas for better names I'd be happy to hear them - we discussed
> > this and this was what we came up with.
> > 
> > > You can also just return negatives when the buffer has overlowed
> > > and get the callers to test < or <= as required.
> > 
> > Yeesh, no.
> 
> Why not?
> All the callers are internal.
> It saves a test and branch (or cmove).

Because this is a subtle thing and having two separate helpers better documents
the _intent_ of the code. I prioritize having clear and understandable code over
shaving every branch.

printbuf_remaining() is the one almost all callers want to use;
printbuf_remaining_size() is only for a few callers that are doing weird things
and should probably be converted to something more standard.

> 
> > > I also wonder it is necessary to count the total length
> > > when the buffer isn't long enough?
> > > Unless there is a real pressing need for it I'd not bother.
> > > Setting pos == size (after writing the '\0') allows
> > > overflow be detected without most of the dangers.
> > 
> > Because that's what snprintf() needs.
> > 
> > > > +
> > > > +static inline unsigned printbuf_written(struct printbuf *out)
> > > > +{
> > > > +	return min(out->pos, out->size);
> > >
> > > That excludes the '\0' for short buffers but includes
> > > it for overlong ones.
> > 
> > It actually doesn't.
> 
> If size is 2 it goes 0, 1, 2, 2, 2 as bytes are added.
> But the string is "" "a" "a" "a" - never 2 characters. 

Ah, you're right. Ok, that's a bug, I'll fix that.

> As opposed to the one in strlen() ?
> I realise that there are shift and mask algorithms from strlen()
> that are likely faster than a byte scan on 64 bit systems.
> But they are likely slower than the check when you have a loop
> that is scanning byte by byte.
> This is especially true on out of order superscaler cpu when
> the copy loop won't be using all the execution blocks.
> 
> What might be faster on cpu (like x86) where misaligned memory
> access are almost entirely free is to copy 8 bytes at a time
> while checking for a zero at the same time.
> 
> Remember kernel strings are quite often short, the overhead
> costs for 'fast' routines slow things down.
> (As you found when calling memcpy() and memset().)

Look, from scanning the kernel log I get you're the kind of programmer who likes
shaving every branch and instruction.

I've spent a _lot_ of time in my career staring at profiles and assembly and
counting cycles (and was working with someone juinor doing that just last
night), and what I've found over the years is that time and again...  it's
memory accesses and cache misses that matter, and not much else.

I put a lot of effort into writing high performance code, and what I find is
that my time is better spent when I focus on writing clear and understandable
code, and making sure that things are laid out in memory intelligently, instead
of trying to generate the "perfect" assembly from all the code I write.

Perfect can be the enemy of good.

If you want to submit patches later optimizing this stuff, be my guest - _but_,
if it comes at the cost of making the code harder to read I'll want to see
benchmark improvements, and of more than a few percent here and there.

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20 15:07   ` Kent Overstreet
  2022-06-20 15:21     ` David Laight
@ 2022-06-21  0:38     ` Joe Perches
  2022-06-21  0:57       ` Kent Overstreet
  1 sibling, 1 reply; 86+ messages in thread
From: Joe Perches @ 2022-06-21  0:38 UTC (permalink / raw)
  To: Kent Overstreet, David Laight
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon, 2022-06-20 at 11:07 -0400, Kent Overstreet wrote:
> On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > I really think that is a bad idea.
> > printk() already uses a lot of stack, anything doing a recursive
> > call is just making that worse.
> > Especially since these calls can often be in error paths
> > which are not often tested and can already be on deep stacks.
> 
> We went over this before - this patch series drastically reduces stack usage of
> sprintf by eliminating a bunch of stack allocated buffers. Do try to keep up...

I generally agree with David.

I think Kent has not provided data that this actually _reduces_
stack usage.

Converting stack variables to call stack frames does not necessarily
reduce overall stack usage when the stack frame plus any locally
used stack in the called function is added together.


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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-21  0:38     ` Joe Perches
@ 2022-06-21  0:57       ` Kent Overstreet
  2022-06-21  1:26         ` Joe Perches
  0 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-21  0:57 UTC (permalink / raw)
  To: Joe Perches
  Cc: David Laight, linux-kernel, linux-mm, pmladek, rostedt,
	enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 05:38:51PM -0700, Joe Perches wrote:
> On Mon, 2022-06-20 at 11:07 -0400, Kent Overstreet wrote:
> > On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > > I really think that is a bad idea.
> > > printk() already uses a lot of stack, anything doing a recursive
> > > call is just making that worse.
> > > Especially since these calls can often be in error paths
> > > which are not often tested and can already be on deep stacks.
> > 
> > We went over this before - this patch series drastically reduces stack usage of
> > sprintf by eliminating a bunch of stack allocated buffers. Do try to keep up...
> 
> I generally agree with David.
> 
> I think Kent has not provided data that this actually _reduces_
> stack usage.

I think the people who are comfortable with reading C can discern that when
large stack allocated character arrays are deleted, frame size and stack usage
go down.

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-21  0:57       ` Kent Overstreet
@ 2022-06-21  1:26         ` Joe Perches
  2022-06-21  2:10           ` Joe Perches
  2022-06-21  2:31           ` [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
  0 siblings, 2 replies; 86+ messages in thread
From: Joe Perches @ 2022-06-21  1:26 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: David Laight, linux-kernel, linux-mm, pmladek, rostedt,
	enozhatsky, linux, willy

On Mon, 2022-06-20 at 20:57 -0400, Kent Overstreet wrote:
> On Mon, Jun 20, 2022 at 05:38:51PM -0700, Joe Perches wrote:
> > On Mon, 2022-06-20 at 11:07 -0400, Kent Overstreet wrote:
> > > On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > > > I really think that is a bad idea.
> > > > printk() already uses a lot of stack, anything doing a recursive
> > > > call is just making that worse.
> > > > Especially since these calls can often be in error paths
> > > > which are not often tested and can already be on deep stacks.
> > > 
> > > We went over this before - this patch series drastically reduces stack usage of
> > > sprintf by eliminating a bunch of stack allocated buffers. Do try to keep up...
> > 
> > I generally agree with David.
> > 
> > I think Kent has not provided data that this actually _reduces_
> > stack usage.
> 
> I think the people who are comfortable with reading C can discern that when
> large stack allocated character arrays are deleted, frame size and stack usage
> go down.

I am very comfortable reading C.

You have not provided any data.


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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-21  1:26         ` Joe Perches
@ 2022-06-21  2:10           ` Joe Perches
  2022-06-26 19:53             ` [RFC[ Alloc in vsprintf Joe Perches
  2022-06-21  2:31           ` [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
  1 sibling, 1 reply; 86+ messages in thread
From: Joe Perches @ 2022-06-21  2:10 UTC (permalink / raw)
  To: Kent Overstreet, Andrew Morton
  Cc: David Laight, linux-kernel, linux-mm, pmladek, rostedt,
	enozhatsky, linux, willy, Miguel Ojeda

On Mon, 2022-06-20 at 18:26 -0700, Joe Perches wrote:
> On Mon, 2022-06-20 at 20:57 -0400, Kent Overstreet wrote:
> > On Mon, Jun 20, 2022 at 05:38:51PM -0700, Joe Perches wrote:
> > > On Mon, 2022-06-20 at 11:07 -0400, Kent Overstreet wrote:
> > > > On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > > > > I really think that is a bad idea.
> > > > > printk() already uses a lot of stack, anything doing a recursive
> > > > > call is just making that worse.
> > > > > Especially since these calls can often be in error paths
> > > > > which are not often tested and can already be on deep stacks.
> > > > 
> > > > We went over this before - this patch series drastically reduces stack usage of
> > > > sprintf by eliminating a bunch of stack allocated buffers. Do try to keep up...
> > > 
> > > I generally agree with David.
> > > 
> > > I think Kent has not provided data that this actually _reduces_
> > > stack usage.
> > 
> > I think the people who are comfortable with reading C can discern that when
> > large stack allocated character arrays are deleted, frame size and stack usage
> > go down.
> 
> I am very comfortable reading C.
> 
> You have not provided any data.

In a brief looking around at stack uses in vsprintf, I believe
this is the largest stack declaration there.

Especially since KSYM_NAME_LEN was increased to 512 by 
commit 394dffa6680c ("kallsyms: increase maximum kernel symbol length to 512")

Perhaps this stack declaration should instead be an alloc/free
as it can be quite large.

I suppose one could quibble about the kzalloc vs kmalloc or the nominally
unnecessary initialization of sym.

I think this makes sense though and it reduces the #ifdef uses too.
---
 lib/vsprintf.c | 41 ++++++++++++++++++++++++-----------------
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index c414a8d9f1ea9..30113a30fd88a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -980,30 +980,37 @@ char *symbol_string(char *buf, char *end, void *ptr,
 		    struct printf_spec spec, const char *fmt)
 {
 	unsigned long value;
-#ifdef CONFIG_KALLSYMS
-	char sym[KSYM_SYMBOL_LEN];
-#endif
+	char *sym = NULL;
 
 	if (fmt[1] == 'R')
 		ptr = __builtin_extract_return_addr(ptr);
 	value = (unsigned long)ptr;
 
-#ifdef CONFIG_KALLSYMS
-	if (*fmt == 'B' && fmt[1] == 'b')
-		sprint_backtrace_build_id(sym, value);
-	else if (*fmt == 'B')
-		sprint_backtrace(sym, value);
-	else if (*fmt == 'S' && (fmt[1] == 'b' || (fmt[1] == 'R' && fmt[2] == 'b')))
-		sprint_symbol_build_id(sym, value);
-	else if (*fmt != 's')
-		sprint_symbol(sym, value);
-	else
-		sprint_symbol_no_offset(sym, value);
+	if (IS_ENABLED(CONFIG_KALLSYMS) &&
+	    (sym = kzalloc(KSYM_SYMBOL_LEN, GFP_NOWAIT | __GFP_NOWARN))) {
+		char *rtn;
+
+		if (*fmt == 'B' && fmt[1] == 'b')
+			sprint_backtrace_build_id(sym, value);
+		else if (*fmt == 'B')
+			sprint_backtrace(sym, value);
+		else if (*fmt == 'S' &&
+			 (fmt[1] == 'b' ||
+			  (fmt[1] == 'R' && fmt[2] == 'b')))
+			sprint_symbol_build_id(sym, value);
+		else if (*fmt != 's')
+			sprint_symbol(sym, value);
+		else
+			sprint_symbol_no_offset(sym, value);
+
+		rtn = string_nocheck(buf, end, sym, spec);
+
+		kfree(sym);
+
+		return rtn;
+	}
 
-	return string_nocheck(buf, end, sym, spec);
-#else
 	return special_hex_number(buf, end, value, sizeof(void *));
-#endif
 }
 
 static const struct printf_spec default_str_spec = {


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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-21  1:26         ` Joe Perches
  2022-06-21  2:10           ` Joe Perches
@ 2022-06-21  2:31           ` Kent Overstreet
  1 sibling, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-21  2:31 UTC (permalink / raw)
  To: Joe Perches
  Cc: David Laight, linux-kernel, linux-mm, pmladek, rostedt,
	enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 06:26:58PM -0700, Joe Perches wrote:
> On Mon, 2022-06-20 at 20:57 -0400, Kent Overstreet wrote:
> > On Mon, Jun 20, 2022 at 05:38:51PM -0700, Joe Perches wrote:
> > > On Mon, 2022-06-20 at 11:07 -0400, Kent Overstreet wrote:
> > > > On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> > > > > I really think that is a bad idea.
> > > > > printk() already uses a lot of stack, anything doing a recursive
> > > > > call is just making that worse.
> > > > > Especially since these calls can often be in error paths
> > > > > which are not often tested and can already be on deep stacks.
> > > > 
> > > > We went over this before - this patch series drastically reduces stack usage of
> > > > sprintf by eliminating a bunch of stack allocated buffers. Do try to keep up...
> > > 
> > > I generally agree with David.
> > > 
> > > I think Kent has not provided data that this actually _reduces_
> > > stack usage.
> > 
> > I think the people who are comfortable with reading C can discern that when
> > large stack allocated character arrays are deleted, frame size and stack usage
> > go down.
> 
> I am very comfortable reading C.
> 
> You have not provided any data.

It seems like neither of you have even bothered to check stack frame size in the
current code, and you guys are the one asserting that this is an issue. 

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  4:19 ` [PATCH v4 00/34] Printbufs - new data structure for building strings David Laight
  2022-06-20  4:54   ` Matthew Wilcox
  2022-06-20 15:07   ` Kent Overstreet
@ 2022-06-21  3:11   ` Kent Overstreet
  2 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-21  3:11 UTC (permalink / raw)
  To: David Laight
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, linux, willy

On Mon, Jun 20, 2022 at 04:19:31AM +0000, David Laight wrote:
> I really think that is a bad idea.
> printk() already uses a lot of stack, anything doing a recursive
> call is just making that worse.
> Especially since these calls can often be in error paths
> which are not often tested and can already be on deep stacks.

So it seems this is something you never actually checked, and I naively assumed
that you might actually know what you were talking about - an understandable
mistake, I think, because vsprintf.c is _a fucking mess_ and high stack usage
would be believable.

But the main part we're concerned with here, snprint() or prt_printf(), has no
such stack usage problems. On v5.18, the frame size is under 64 bytes. On my
branch, it's 72 bytes - higher because we do need to save arguments on the stack
for the pretty-printer invocation, and there's no way around that without
dropping to asm - although I'm allowing up to 8 arguments (besides the printbuf
itself), which is probably excessive.

So I'm not seeing what you're talking about.

In the leaf functions, the individual pretty-printers/%p extensions, those are
doing completely ridiculous things and I have fixed them all except
symbol_string() on my branch, and I'll get to that one.

Having a proper string library with useful helpers really makes things easier,
it turns out.

As for recursive %pf() invocations blowing the stack? I seriously fucking doubt
it, once you're in a pretty-printer where you've already got a printbuf you can
output to there's not much reason to be doing recursive calls to prt_printf()
passing it yet another pretty printer - that's not where %pf() is convenient,
what it makes convenient is using pretty printers when you're calling printk()
directly. In a pretty printer fuction, if you want to do recursive
pretty-printer calls you'd just call it directly! prt_printf(out, "%pf(%p)"),
foo_to_text, foo) is silly when you can just call foo_to_text(out, foo).

Now, I ask both of you please take your bureaucratic nitpicky nonsense and,
kindly, pretty please with sugar on top - stuff it. I much prefer to work with
people who don't waste my time, and who have actual _taste_.

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (34 preceding siblings ...)
  2022-06-20  4:19 ` [PATCH v4 00/34] Printbufs - new data structure for building strings David Laight
@ 2022-06-21  6:11 ` Rasmus Villemoes
  2022-06-21  8:01   ` Kent Overstreet
  2022-07-19 23:15 ` Steven Rostedt
  36 siblings, 1 reply; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  6:11 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

On 20/06/2022 02.41, Kent Overstreet wrote:

>   Rasmus pointed out that -fno-strict-aliasing is going to cause gcc to generate
>   nasty code, and indeed it unfortunately does but according to worst case
>   scenario microbenchmarks it's not a problem for actual performance. 

Well, that's not how I interpreted those numbers, but, except if they
showed an improvement, how much is acceptable is of course always a
matter of judgment.

However, what's really annoying and somewhat dishonest is that you're
not including those numbers, nor the methodology, in either the cover
letter or commit itself.

Rasmus

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

* Re: [PATCH v4 05/34] vsprintf: %pf(%p)
  2022-06-20  0:42 ` [PATCH v4 05/34] vsprintf: %pf(%p) Kent Overstreet
@ 2022-06-21  7:04   ` Rasmus Villemoes
  2022-06-21  7:51     ` Kent Overstreet
  2022-06-21 11:11     ` David Laight
  0 siblings, 2 replies; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  7:04 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

On 20/06/2022 02.42, Kent Overstreet wrote:
> 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:
> 
>  %pf(%p) - more compatible
>  %(%p)  - more prettier

No.

> diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
> index 5e89497ba3..8fc0b62af1 100644
> --- a/Documentation/core-api/printk-formats.rst
> +++ b/Documentation/core-api/printk-formats.rst
> @@ -625,6 +625,28 @@ Examples::
>  	%p4cc	Y10  little-endian (0x20303159)
>  	%p4cc	NV12 big-endian (0xb231564e)
>  
> +Calling a pretty printer function
> +---------------------------------
> +
> +::
> +
> +        %pf(%p)     pretty printer function taking one argument
> +        %pf(%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("%pf(%p)", foo_to_text, foo);
> +
> +Note that a pretty-printer may not sleep, if called from printk(). If called
> +from pr_buf() or sprintf() there are no such restrictions.

I know what you're trying to say, but if the sprintf() call itself is
from a non-sleepable context this is obviously not true. So please just
make the rule "A pretty-printer must not sleep.". That's much simpler
and less error-prone. Otherwise I guarantee you that somebody is going
to add a sleeping pretty-printer for their own need, use it in a couple
of safe places, and then somebody wants to add a printk() in that driver
and sees "hey, I can get all this state dumped very easily with this
pretty-printer".

> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index 7b24714674..5afa74dda5 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -436,7 +436,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 {
> @@ -2520,7 +2521,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;

NAK. Don't implement something that will never be tested nor used.
There's not a snowball's chance in hell that we'll ever build the kernel
without -Wformat.

>  
> +static void call_prt_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 *);

Sorry, but this is way too ugly, and the prospect of at some point in
the future invoking libffi to do something even naster... eww. We do not
need more functions with completely generic prototypes with no
typechecking and making it extremely hard to teach one of our static
analyzers (smatch has some %pX checking) to do that typechecking.

There are at least two ways you can achieve this passing of a variable
number of arguments with proper types.

(1) Each pretty-printer comes with a struct wrapping up its real
arguments and a macro for creating a compound literal passing those
arguments.

struct foo_pp {
  void (*func)(struct printbuf *pb, void *ctx); /* always first */
  int x;
  long y;
};
void foo_pp(struct printbuf *pb, void *ctx)
{
  struct foo_pp *f = ctx;
  pr_printf(pb, "%d %ld", f->x, f->y);
}

#define FOO_PP(_x, _y) (struct foo_pp){.func = foo_pp, .x = (_x), .y = (_y)}

printk("bla bla %pf\n", &FOO_PP(aa, bb));

(2) Let the pretty-printer itself extract the varargs it expects. To
portably pass around a va_list by reference it needs to be wrapped, so
this would be

struct wva { va_list ap; };

void foo_pp(struct printbuf *pb, struct wva *w)
{
  int x = va_arg(w->ap, int);
  long y = va_arg(w->ap, long);
  pr_printf(pb, "%d %ld", x, y);
}

printk("bla bla %pf(%d, %ld)\n", foo_pp, aa, bb)

with the core printf implementation internally using such a wrapped
va_list, and after a %pf( relying on the pretty-printer having consumed
the arguments up until the closing ). It would probably be a good idea
to give the pretty-printer a pointer to that opening '(' or one-past-it
as well so it could do a sanity check.

So the "core" implementation would be (or the printbuf version of the same)

wsnprintf(char *buf, size_t len, const char *fmt, struct wva *w)
{ ... }

while e.g. snprintf and vsnprintf would be

snprintf(char *buf, size_t len, const char *fmt, ...)
{
  struct wva w;
  int ret;
  va_start(w.ap, fmt);
  ret = wsnprintf(buf, len, fmt, &w);
  va_end(w.ap);
  return ret;
}
vsnprintf(char *buf, size_t len, const char *fmt, struct va_list ap)
{
  struct wva w;
  int ret;
  va_copy(w.ap, ap);
  ret = wsnprintf(buf, len, fmt, &w);
  va_end(w.ap);
  return ret;
}

[snprintf could as usual be implemented in terms of vsnprintf, but the
above would eliminate the state copying].

Rasmus

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

* Re: [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul
  2022-06-20  0:42 ` [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
@ 2022-06-21  7:19   ` Rasmus Villemoes
  2022-06-21  7:52     ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  7:19 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

On 20/06/2022 02.42, Kent Overstreet wrote:
> 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

And which of course you don't check anymore. So any statement about "all
tests passing after these patches" is not worth much.

So NAK in this form, but perhaps if/when my year-old patch gets picked
up (or you could include it in the series) we can talk about eliminating
the check for past-nul-before-end-of-buffer writes.

Rasmus

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

* Re: [PATCH v4 05/34] vsprintf: %pf(%p)
  2022-06-21  7:04   ` Rasmus Villemoes
@ 2022-06-21  7:51     ` Kent Overstreet
  2022-06-21  8:47       ` Rasmus Villemoes
  2022-06-21 11:11     ` David Laight
  1 sibling, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-21  7:51 UTC (permalink / raw)
  To: Rasmus Villemoes
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, willy

On Tue, Jun 21, 2022 at 09:04:38AM +0200, Rasmus Villemoes wrote:
> On 20/06/2022 02.42, Kent Overstreet wrote:
> > +Note that a pretty-printer may not sleep, if called from printk(). If called
> > +from pr_buf() or sprintf() there are no such restrictions.
> 
> I know what you're trying to say, but if the sprintf() call itself is
> from a non-sleepable context this is obviously not true. So please just
> make the rule "A pretty-printer must not sleep.". That's much simpler
> and less error-prone. Otherwise I guarantee you that somebody is going
> to add a sleeping pretty-printer for their own need, use it in a couple
> of safe places, and then somebody wants to add a printk() in that driver
> and sees "hey, I can get all this state dumped very easily with this
> pretty-printer".

Kernel programmers are used to having to consider the context they're in and
what the functions they're calling might do, a pretty-printer being called
indirectly via sprintf() is absolutely no different. 

> >  struct printf_spec {
> > @@ -2520,7 +2521,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;
> 
> NAK. Don't implement something that will never be tested nor used.
> There's not a snowball's chance in hell that we'll ever build the kernel
> without -Wformat.

We're not stopping here. Matthew is taking this to WG14 and I'll be working on
adding this functionality to glibc next, and %() is the syntax we intend to take
to the working group.

But the working group is naturally going to want to see that a working
implementation of it exists.

> Sorry, but this is way too ugly, and the prospect of at some point in
> the future invoking libffi to do something even naster... eww. We do not
> need more functions with completely generic prototypes with no
> typechecking and making it extremely hard to teach one of our static
> analyzers (smatch has some %pX checking) to do that typechecking.
> 
> There are at least two ways you can achieve this passing of a variable
> number of arguments with proper types.
> 
> (1) Each pretty-printer comes with a struct wrapping up its real
> arguments and a macro for creating a compound literal passing those
> arguments.
> 
> struct foo_pp {
>   void (*func)(struct printbuf *pb, void *ctx); /* always first */
>   int x;
>   long y;
> };
> void foo_pp(struct printbuf *pb, void *ctx)
> {
>   struct foo_pp *f = ctx;
>   pr_printf(pb, "%d %ld", f->x, f->y);
> }
> 
> #define FOO_PP(_x, _y) (struct foo_pp){.func = foo_pp, .x = (_x), .y = (_y)}
> 
> printk("bla bla %pf\n", &FOO_PP(aa, bb));

Hellllllllll no.

All that's missing right now is gcc checking that the function signature matches
the args specified within the (). That will come. No way in hell am I going to
implement some half baked hacked up macro crap - I intend to do this right.

> (2) Let the pretty-printer itself extract the varargs it expects. To
> portably pass around a va_list by reference it needs to be wrapped, so
> this would be
> 
> struct wva { va_list ap; };
> 
> void foo_pp(struct printbuf *pb, struct wva *w)
> {
>   int x = va_arg(w->ap, int);
>   long y = va_arg(w->ap, long);
>   pr_printf(pb, "%d %ld", x, y);
> }
> 
> printk("bla bla %pf(%d, %ld)\n", foo_pp, aa, bb)
> 
> with the core printf implementation internally using such a wrapped
> va_list, and after a %pf( relying on the pretty-printer having consumed
> the arguments up until the closing ). It would probably be a good idea
> to give the pretty-printer a pointer to that opening '(' or one-past-it
> as well so it could do a sanity check.

Also no. Varargs is terrible in most situations, and should only be used for
functions that actually want to take variable numbers of arguments - that
doesn't apply to pretty printers.

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

* Re: [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul
  2022-06-21  7:19   ` Rasmus Villemoes
@ 2022-06-21  7:52     ` Kent Overstreet
  0 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-21  7:52 UTC (permalink / raw)
  To: Rasmus Villemoes
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, willy

On Tue, Jun 21, 2022 at 09:19:55AM +0200, Rasmus Villemoes wrote:
> On 20/06/2022 02.42, Kent Overstreet wrote:
> > 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
> 
> And which of course you don't check anymore. So any statement about "all
> tests passing after these patches" is not worth much.
> 
> So NAK in this form, but perhaps if/when my year-old patch gets picked
> up (or you could include it in the series) we can talk about eliminating
> the check for past-nul-before-end-of-buffer writes.

I'm more than happy to pick up your patch, please send it to me.

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

* Re: [PATCH v4 07/34] lib/printbuf: Heap allocation
  2022-06-20  0:42 ` [PATCH v4 07/34] lib/printbuf: Heap allocation Kent Overstreet
@ 2022-06-21  7:58   ` Rasmus Villemoes
  0 siblings, 0 replies; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  7:58 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

On 20/06/2022 02.42, Kent Overstreet wrote:
> 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 | 120 +++++++++++++++++++++++++++++++++------
>  lib/Makefile             |   2 +-
>  lib/printbuf.c           |  71 +++++++++++++++++++++++
>  3 files changed, 175 insertions(+), 18 deletions(-)
>  create mode 100644 lib/printbuf.c
> 
> diff --git a/include/linux/printbuf.h b/include/linux/printbuf.h
> index 8186c447ca..382863afa7 100644
> --- a/include/linux/printbuf.h
> +++ b/include/linux/printbuf.h
> @@ -4,19 +4,69 @@
>  #ifndef _LINUX_PRINTBUF_H
>  #define _LINUX_PRINTBUF_H
>  
> -#include <linux/kernel.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;
> + *
> + *   prt_printf(&buf, "foo=");
> + *   foo_to_text(&buf, foo);
> + *   printk("%s", buf.buf);

So, if prt_printf() and foo_to_text() failed to make room, buf.buf would
still be NULL, right? Sure, vsnprintf() handles that gracefully, but
this is probably not what you intended.

>  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,				\
> +})
> +
>  /*
>   * Returns size remaining of output buffer:
>   */
> @@ -49,26 +99,36 @@ static inline bool printbuf_overflowed(struct printbuf *out)
>  
>  static inline void printbuf_nul_terminate(struct printbuf *out)
>  {
> +	printbuf_make_room(out, 1);
> +

Shouldn't this be printbuf_make_room(out, 0)?

Probably this should be split up, so that the functions that also do
printbuf_make_room() which ensures room for a nul-terminator could then
call __printbuf_nul_terminate(), which would just contain the below:

>  	if (out->pos < out->size)
>  		out->buf[out->pos] = 0;
>  	else if (out->size)
>  		out->buf[out->size - 1] = 0;
>  }



> -static inline void __prt_char(struct printbuf *out, char c)
> +/* Doesn't call printbuf_make_room(), doesn't nul terminate: */
> +static inline void __prt_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 __prt_char(struct printbuf *out, char c)
> +{
> +	printbuf_make_room(out, 1);
> +	__prt_char_reserved(out, c);
> +}
> +
>  static inline void prt_char(struct printbuf *out, char c)
>  {
>  	__prt_char(out, c);
>  	printbuf_nul_terminate(out);
>  }
>  
> -static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
> +static inline void __prt_chars_reserved(struct printbuf *out, char c, unsigned n)
>  {
>  	unsigned i, can_print = min(n, printbuf_remaining(out));
>  
> @@ -79,13 +139,18 @@ static inline void __prt_chars(struct printbuf *out, char c, unsigned n)
>  
>  static inline void prt_chars(struct printbuf *out, char c, unsigned n)
>  {
> -	__prt_chars(out, c, n);
> +	printbuf_make_room(out, n);
> +	__prt_chars_reserved(out, c, n);
>  	printbuf_nul_terminate(out);
>  }
>  
>  static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
>  {
> -	unsigned i, can_print = min(n, printbuf_remaining(out));
> +	unsigned i, can_print;
> +
> +	printbuf_make_room(out, n);
> +
> +	can_print = min(n, printbuf_remaining(out));
>  
>  	for (i = 0; i < can_print; i++)
>  		out->buf[out->pos++] = ((char *) b)[i];
> @@ -101,22 +166,43 @@ static inline void prt_str(struct printbuf *out, const char *str)
>  
>  static inline void prt_hex_byte(struct printbuf *out, u8 byte)
>  {
> -	__prt_char(out, hex_asc_hi(byte));
> -	__prt_char(out, hex_asc_lo(byte));
> +	printbuf_make_room(out, 2);
> +	__prt_char_reserved(out, hex_asc_hi(byte));
> +	__prt_char_reserved(out, hex_asc_lo(byte));
>  	printbuf_nul_terminate(out);
>  }
>  
>  static inline void prt_hex_byte_upper(struct printbuf *out, u8 byte)
>  {
> -	__prt_char(out, hex_asc_upper_hi(byte));
> -	__prt_char(out, hex_asc_upper_lo(byte));
> +	printbuf_make_room(out, 2);
> +	__prt_char_reserved(out, hex_asc_upper_hi(byte));
> +	__prt_char_reserved(out, hex_asc_upper_lo(byte));
>  	printbuf_nul_terminate(out);
>  }
>  
> -#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--;
> +}

So, if I have a printbuf in scope, and I do irq_disable() or spin_lock()
or whatnot, I'm supposed to also call printbuf_atomic_inc(), at least if
the printbuf is used within the locked region.

Honest question: An u8 used for this purpose cannot overflow?

>  #endif /* _LINUX_PRINTBUF_H */
> diff --git a/lib/Makefile b/lib/Makefile
> index 6b9ffc1bd1..b4609a4258 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..8c70128e31
> --- /dev/null
> +++ b/lib/printbuf.c
> @@ -0,0 +1,71 @@
> +// 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/err.h>
> +#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;

I think that ->allocation_failure should be sticky and make us return an
early error here; if we're under memory pressure we don't want each and
every prt_char() of whatever we're trying to print to end up trying to
do an allocation.

> +	/* Reserved space for terminating nul: */
> +	extra += 1;
> +
> +	if (out->pos + extra < out->size)
> +		return 0;

Are you sure you don't want to be careful about the possibility of
out->pos+extra overflowing? And since extra has been ++'ed, shouldn't
the comparison be <= ? If pos is 0, size is 2, and I want to add one
char (so on entry extra is 1), this should not require a reallocation?

> +	new_size = roundup_pow_of_two(out->size + extra);

Are you sure you don't want to be careful about the possibility of
out->size+extra overflowing, or hitting that with roundup_pow_of_two()
doing that?

> +	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

Pet peeve: Please use the spelling "nul" consistently.

> + * 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);

I think the documentation lacks some mention of lifetimes and caller
obligations or lack thereof. Especially since the return value could
become dangling not just if the printbuf is destroyed (printbuf_exit),
but also any other use of the printbuf which could cause a realloc.

Rasmus

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-21  6:11 ` Rasmus Villemoes
@ 2022-06-21  8:01   ` Kent Overstreet
  0 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-21  8:01 UTC (permalink / raw)
  To: Rasmus Villemoes
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, willy

On Tue, Jun 21, 2022 at 08:11:49AM +0200, Rasmus Villemoes wrote:
> On 20/06/2022 02.41, Kent Overstreet wrote:
> 
> >   Rasmus pointed out that -fno-strict-aliasing is going to cause gcc to generate
> >   nasty code, and indeed it unfortunately does but according to worst case
> >   scenario microbenchmarks it's not a problem for actual performance. 
> 
> Well, that's not how I interpreted those numbers, but, except if they
> showed an improvement, how much is acceptable is of course always a
> matter of judgment.
> 
> However, what's really annoying and somewhat dishonest is that you're
> not including those numbers, nor the methodology, in either the cover
> letter or commit itself.

There's nothing dishonest about it, and I wasn't claiming an improvement; merely
no regressions (some were a 5-10% percent up, some down by around the same
amount, overall it was a wash).

My priority simply isn't microoptimizing everything. I find that programmers who
chase optimizing every loop and are constantly trying to shave instructions
everywhere they can end up with code where the large scale structure is a mess,
and that's where you miss out on the _real_ performance opportunities.

My priority is clean, readable, simple, easy to work on code, because _that_ is
the code that becomes fast in the long run.

Premature optimization really is the root of all evil, and I am _absolutely_
going to try to drive the discussion away from shaving cycles when there's new
APIs to get right and messy refactorings to complete.

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

* Re: [PATCH v4 08/34] lib/printbuf: Tabstops, indenting
  2022-06-20  0:42 ` [PATCH v4 08/34] lib/printbuf: Tabstops, indenting Kent Overstreet
@ 2022-06-21  8:14   ` Rasmus Villemoes
  0 siblings, 0 replies; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  8:14 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

On 20/06/2022 02.42, Kent Overstreet wrote:
> 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.

I am really, really, really not convinced that we want or need this. But
as long as this doesn't add overhead to those not using it (in
particular, as long as it doesn't grow a "scan whatever contents was
just added for maybe a \n so ->last_newline can be updated"), meh.

> + *
> + * Make sure you use prt_newline() instead of \n in the format string for indent
> + * level and tabstops to work corretly.
>   */
>  
>  #include <linux/kernel.h>
> @@ -45,18 +62,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 prt_newline(struct printbuf *);
> +void printbuf_indent_add(struct printbuf *, unsigned);
> +void printbuf_indent_sub(struct printbuf *, unsigned);
> +void prt_tab(struct printbuf *);
> +void prt_tab_rjust(struct printbuf *);
> +
>  /* Initializer for a heap allocated printbuf: */
>  #define PRINTBUF ((struct printbuf) { .heap_allocated = true })
>  
> @@ -187,6 +215,8 @@ static inline void printbuf_reset(struct printbuf *buf)
>  {
>  	buf->pos		= 0;
>  	buf->allocation_failure	= 0;
> +	buf->indent		= 0;
> +	buf->tabstop		= 0;
>  }
>  
>  /**
> diff --git a/lib/printbuf.c b/lib/printbuf.c
> index 8c70128e31..a7f80f63ca 100644
> --- a/lib/printbuf.c
> +++ b/lib/printbuf.c
> @@ -12,6 +12,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;
> @@ -69,3 +74,123 @@ void printbuf_exit(struct printbuf *buf)
>  	}
>  }
>  EXPORT_SYMBOL(printbuf_exit);
> +
> +void prt_newline(struct printbuf *buf)
> +{
> +	unsigned i;
> +
> +	printbuf_make_room(buf, 1 + buf->indent);
> +
> +	__prt_char(buf, '\n');
> +
> +	buf->last_newline	= buf->pos;
> +
> +	for (i = 0; i < buf->indent; i++)
> +		__prt_char(buf, ' ');

Why the loop? Don't you have a _chars variant?

> +void printbuf_indent_add(struct printbuf *buf, unsigned spaces)
> +{
> +	if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
> +		spaces = 0;
> +
> +	buf->indent += spaces;
> +	while (spaces--)
> +		prt_char(buf, ' ');
> +}
> +EXPORT_SYMBOL(printbuf_indent_add);
> +
> +/**
> + * printbuf_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 printbuf_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(printbuf_indent_sub);
> +
> +/**
> + * prt_tab - Advance printbuf to the next tabstop
> + *
> + * @buf: printbuf to control
> + *
> + * Advance output to the next tabstop by printing spaces.
> + */
> +void prt_tab(struct printbuf *out)
> +{
> +	int spaces = max_t(int, 0, out->tabstops[out->tabstop] - printbuf_linelen(out));
> +
> +	BUG_ON(out->tabstop > ARRAY_SIZE(out->tabstops));

So this accesses out->tabstops first, then does a (buggy) bounds check.
And kills the machine if somebody managed to corrupt ->tabstop. Instead
of limping along and living with less-pretty-printed output.

I don't think you'll get this code accepted by the Great Penguin.

Rasmus

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

* Re: [PATCH v4 11/34] vsprintf: Improve number()
  2022-06-20  0:42 ` [PATCH v4 11/34] vsprintf: Improve number() Kent Overstreet
@ 2022-06-21  8:33   ` Rasmus Villemoes
  0 siblings, 0 replies; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  8:33 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

On 20/06/2022 02.42, Kent Overstreet wrote:

>  	/* generate full string in tmp[], in reverse order */
> -	i = 0;
> -	if (num < spec.base)
> -		tmp[i++] = hex_asc_upper[num] | locase;

Please don't remove that optimization in a patch titled "improve number()".

> -	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;

So avoiding a branch here may be a good idea, but I'm not sure ilog2()
is very efficient on all arches. Since we know that base is either 8 or
16, we could do "shift = (spec.base/8) + 2".

Rasmus

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

* Re: [PATCH v4 05/34] vsprintf: %pf(%p)
  2022-06-21  7:51     ` Kent Overstreet
@ 2022-06-21  8:47       ` Rasmus Villemoes
  0 siblings, 0 replies; 86+ messages in thread
From: Rasmus Villemoes @ 2022-06-21  8:47 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: linux-kernel, linux-mm, pmladek, rostedt, enozhatsky, willy

On 21/06/2022 09.51, Kent Overstreet wrote:

>>>  struct printf_spec {
>>> @@ -2520,7 +2521,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;
>>
>> NAK. Don't implement something that will never be tested nor used.
>> There's not a snowball's chance in hell that we'll ever build the kernel
>> without -Wformat.
> 
> We're not stopping here. Matthew is taking this to WG14 and I'll be working on
> adding this functionality to glibc next, and %() is the syntax we intend to take
> to the working group.
> 
> But the working group is naturally going to want to see that a working
> implementation of it exists.

OK. But implementation in glibc, musl and/or the kernel is much much
less interesting than support in gcc and clang for accepting %( in the
first place, and preferably also for not just treating %( as a synonym
for %p but actually checking that the argument is a function pointer and
 that its signature matches the arguments between (). Once _that_ has
landed in gcc 24 and the kernel requirements have been bumped to that
can we consider adding "%(" format strings (and the necessary support).

Yes, I appreciate the chicken-and-egg situation. No, I don't think the
mainline kernel is a suitable place for proof-of-concept and highly
experimental code.

Rasmus

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

* RE: [PATCH v4 05/34] vsprintf: %pf(%p)
  2022-06-21  7:04   ` Rasmus Villemoes
  2022-06-21  7:51     ` Kent Overstreet
@ 2022-06-21 11:11     ` David Laight
  1 sibling, 0 replies; 86+ messages in thread
From: David Laight @ 2022-06-21 11:11 UTC (permalink / raw)
  To: 'Rasmus Villemoes',
	Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: rostedt, enozhatsky, willy

...
> > +Calling a pretty printer function
> > +---------------------------------
> > +
> > +::
> > +
> > +        %pf(%p)     pretty printer function taking one argument
> > +        %pf(%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("%pf(%p)", foo_to_text, foo);
> > +
> > +Note that a pretty-printer may not sleep, if called from printk(). If called
> > +from pr_buf() or sprintf() there are no such restrictions.

I've lost the original email :-)

If you are going to implement this foo_to_text() needs to
be passed the field width, precision and flags.

Is there a real use for multiple arguments %pf(%p,%p) that can't
be implemented by requiring the caller use a relay structure?
That (sort of) solves the problem of people expecting to be
able to pass integers though.

An alternative would be using an array of a union type
to pass through the values extracted from the original va_list.

Or pass the first as a pointer (to get the possibility of compile
time format checking and any others as a union[].

	David

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

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

* RE: [PATCH v4 26/34] tools/testing/nvdimm: Convert to printbuf
  2022-06-20  0:42 ` [PATCH v4 26/34] tools/testing/nvdimm: " Kent Overstreet
@ 2022-06-24 19:32   ` Dan Williams
  2022-06-24 23:42     ` Santosh Sivaraj
  0 siblings, 1 reply; 86+ messages in thread
From: Dan Williams @ 2022-06-24 19:32 UTC (permalink / raw)
  To: Kent Overstreet, linux-kernel, linux-mm, pmladek
  Cc: Kent Overstreet, rostedt, enozhatsky, linux, willy, Dan Williams,
	Dave Hansen, nvdimm, santosh

[ add Santosh ]

Kent Overstreet wrote:
> 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

Acked-by: Dan Williams <dan.j.williams@intel.com>

This probably also wants a Tested-by from Santosh, but it looks ok to me.

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

* Re: [PATCH v4 26/34] tools/testing/nvdimm: Convert to printbuf
  2022-06-24 19:32   ` Dan Williams
@ 2022-06-24 23:42     ` Santosh Sivaraj
  2022-07-01  6:32       ` Shivaprasad G Bhat
  0 siblings, 1 reply; 86+ messages in thread
From: Santosh Sivaraj @ 2022-06-24 23:42 UTC (permalink / raw)
  To: Dan Williams
  Cc: Kent Overstreet, linux-kernel, linux-mm, Petr Mladek,
	Steven Rostedt, enozhatsky, Rasmus Villemoes, willy, Dave Hansen,
	Linux NVDIMM, Shivaprasad G Bhat, Vaibhav Jain

[-- Attachment #1: Type: text/plain, Size: 692 bytes --]

I don't have setup to test this now. Adding Shiva and Vaibhav who could
probably help.

Thanks,
Santosh

On Sat, 25 Jun, 2022, 1:03 am Dan Williams, <dan.j.williams@intel.com>
wrote:

> [ add Santosh ]
>
> Kent Overstreet wrote:
> > 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
>
> Acked-by: Dan Williams <dan.j.williams@intel.com>
>
> This probably also wants a Tested-by from Santosh, but it looks ok to me.
>

[-- Attachment #2: Type: text/html, Size: 1523 bytes --]

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

* [RFC[ Alloc in vsprintf
  2022-06-21  2:10           ` Joe Perches
@ 2022-06-26 19:53             ` Joe Perches
  2022-06-26 20:06               ` Kent Overstreet
  2022-06-26 20:19               ` Linus Torvalds
  0 siblings, 2 replies; 86+ messages in thread
From: Joe Perches @ 2022-06-26 19:53 UTC (permalink / raw)
  To: Andrew Morton, Linus Torvalds
  Cc: David Laight, Petr Mladek, Steven Rostedt, Sergey Senozhatsky,
	Rasmus Villemoes, Matthew Wilcox, Miguel Ojeda, Kent Overstreet,
	Andy Shevchenko, LKML, linux-mm

In a reply to the printbufs thread, I wrote a proposal to use an
alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.

No one has replied to this but I think it's somewhat sensible.

Thoughts?

On Mon, 2022-06-20 at 19:10 -0700, Joe Perches wrote:

> In a brief looking around at stack uses in vsprintf, I believe
> this is the largest stack declaration there.
> 
> Especially since KSYM_NAME_LEN was increased to 512 by 
> commit 394dffa6680c ("kallsyms: increase maximum kernel symbol length to 512")
> 
> Perhaps this stack declaration should instead be an alloc/free
> as it can be quite large.
> 
> I suppose one could quibble about the kzalloc vs kmalloc or the nominally
> unnecessary initialization of sym.
> 
> I think this makes sense though and it reduces the #ifdef uses too.
> ---
>  lib/vsprintf.c | 41 ++++++++++++++++++++++++-----------------
>  1 file changed, 24 insertions(+), 17 deletions(-)
> 
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index c414a8d9f1ea9..30113a30fd88a 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -980,30 +980,37 @@ char *symbol_string(char *buf, char *end, void *ptr,
>  		    struct printf_spec spec, const char *fmt)
>  {
>  	unsigned long value;
> -#ifdef CONFIG_KALLSYMS
> -	char sym[KSYM_SYMBOL_LEN];
> -#endif
> +	char *sym = NULL;
>  
>  	if (fmt[1] == 'R')
>  		ptr = __builtin_extract_return_addr(ptr);
>  	value = (unsigned long)ptr;
>  
> -#ifdef CONFIG_KALLSYMS
> -	if (*fmt == 'B' && fmt[1] == 'b')
> -		sprint_backtrace_build_id(sym, value);
> -	else if (*fmt == 'B')
> -		sprint_backtrace(sym, value);
> -	else if (*fmt == 'S' && (fmt[1] == 'b' || (fmt[1] == 'R' && fmt[2] == 'b')))
> -		sprint_symbol_build_id(sym, value);
> -	else if (*fmt != 's')
> -		sprint_symbol(sym, value);
> -	else
> -		sprint_symbol_no_offset(sym, value);
> +	if (IS_ENABLED(CONFIG_KALLSYMS) &&
> +	    (sym = kzalloc(KSYM_SYMBOL_LEN, GFP_NOWAIT | __GFP_NOWARN))) {
> +		char *rtn;
> +
> +		if (*fmt == 'B' && fmt[1] == 'b')
> +			sprint_backtrace_build_id(sym, value);
> +		else if (*fmt == 'B')
> +			sprint_backtrace(sym, value);
> +		else if (*fmt == 'S' &&
> +			 (fmt[1] == 'b' ||
> +			  (fmt[1] == 'R' && fmt[2] == 'b')))
> +			sprint_symbol_build_id(sym, value);
> +		else if (*fmt != 's')
> +			sprint_symbol(sym, value);
> +		else
> +			sprint_symbol_no_offset(sym, value);
> +
> +		rtn = string_nocheck(buf, end, sym, spec);
> +
> +		kfree(sym);
> +
> +		return rtn;
> +	}
>  
> -	return string_nocheck(buf, end, sym, spec);
> -#else
>  	return special_hex_number(buf, end, value, sizeof(void *));
> -#endif
>  }
>  
>  static const struct printf_spec default_str_spec = {
> 


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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 19:53             ` [RFC[ Alloc in vsprintf Joe Perches
@ 2022-06-26 20:06               ` Kent Overstreet
  2022-06-26 20:13                 ` Joe Perches
  2022-06-26 20:19               ` Linus Torvalds
  1 sibling, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-26 20:06 UTC (permalink / raw)
  To: Joe Perches
  Cc: Andrew Morton, Linus Torvalds, David Laight, Petr Mladek,
	Steven Rostedt, Sergey Senozhatsky, Rasmus Villemoes,
	Matthew Wilcox, Miguel Ojeda, Andy Shevchenko, LKML, linux-mm

On Sun, Jun 26, 2022 at 12:53:26PM -0700, Joe Perches wrote:
> In a reply to the printbufs thread, I wrote a proposal to use an
> alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.
> 
> No one has replied to this but I think it's somewhat sensible.
> 
> Thoughts?

As functions get converted to printbufs the separate stack allocated buffers
become unnecessary, because printbufs have helpers that do bounds checking and
make outputting to the vsprintf buffer painless. So it's not necessary - I
haven't fully converted symbol_string() yet but I'll do so by the time I mail
out the next round of patches.

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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 20:06               ` Kent Overstreet
@ 2022-06-26 20:13                 ` Joe Perches
  0 siblings, 0 replies; 86+ messages in thread
From: Joe Perches @ 2022-06-26 20:13 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: Andrew Morton, Linus Torvalds, David Laight, Petr Mladek,
	Steven Rostedt, Sergey Senozhatsky, Rasmus Villemoes,
	Matthew Wilcox, Miguel Ojeda, Andy Shevchenko, LKML, linux-mm

On Sun, 2022-06-26 at 16:06 -0400, Kent Overstreet wrote:
> On Sun, Jun 26, 2022 at 12:53:26PM -0700, Joe Perches wrote:
> > In a reply to the printbufs thread, I wrote a proposal to use an
> > alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.
> > 
> > No one has replied to this but I think it's somewhat sensible.
> > 
> > Thoughts?
> 
> As functions get converted to printbufs the separate stack allocated buffers
> become unnecessary, because printbufs have helpers that do bounds checking and
> make outputting to the vsprintf buffer painless. So it's not necessary - I
> haven't fully converted symbol_string() yet but I'll do so by the time I mail
> out the next round of patches.

_If_ the printfbufs patch series gets applied.
I think that series is not great yet.

Even if applied via something like the printbufs series, the
stack use of this function needs/could use attending.

~700 bytes of stack use here isn't great.


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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 19:53             ` [RFC[ Alloc in vsprintf Joe Perches
  2022-06-26 20:06               ` Kent Overstreet
@ 2022-06-26 20:19               ` Linus Torvalds
  2022-06-26 20:39                 ` Joe Perches
  2022-06-27  8:25                 ` David Laight
  1 sibling, 2 replies; 86+ messages in thread
From: Linus Torvalds @ 2022-06-26 20:19 UTC (permalink / raw)
  To: Joe Perches
  Cc: Andrew Morton, David Laight, Petr Mladek, Steven Rostedt,
	Sergey Senozhatsky, Rasmus Villemoes, Matthew Wilcox,
	Miguel Ojeda, Kent Overstreet, Andy Shevchenko, LKML, linux-mm

[-- Attachment #1: Type: text/plain, Size: 1500 bytes --]

On Sun, Jun 26, 2022 at 12:53 PM Joe Perches <joe@perches.com> wrote:
>
> In a reply to the printbufs thread, I wrote a proposal to use an
> alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.
>
> No one has replied to this but I think it's somewhat sensible.

I think that's a bad idea.

Those things are *literally* called from panic situations, which may
be while holding core memory allocation locks, or similar.

The last thing we want to do is make a hard-to-debug panic be even
*harder* to debug because you get a deadlock when oopsing.

(And yes, I realize that the symbol name lookup can have problems too,
but thats' kind of fundamental to %pS, while a kzmalloc isn't.

Now, you are correct that the stack buffer is annoying. But I think
the proper way to fix that is to say "we already *have* the target
buffer, let's use it".

That does require teaching the sprint_symbol() functions that they
need to take a "length of buffer" and return how much they used, but
that would seem to be a sensible thing anyway, and what the code
should always have done?

It's bad policy to just pass in a buffer without length, and I think
it was always broken. Nasty. That KSYM_SYMBOL_LEN is magically taking
care of it all, but it's ugly as heck, wouldn't you say?

NOTE! The attached patch is completely broken.  I did not do that
interface change to the kallsyms code. The patch is literally meant to
be just an explanation of what I mean, not a working patch.

                  Linus

[-- Attachment #2: patch.diff --]
[-- Type: text/x-patch, Size: 1434 bytes --]

 lib/vsprintf.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3c1853a9d1c0..032fa8bc5752 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -981,7 +981,7 @@ char *symbol_string(char *buf, char *end, void *ptr,
 {
 	unsigned long value;
 #ifdef CONFIG_KALLSYMS
-	char sym[KSYM_SYMBOL_LEN];
+	unsigned long maxlen;
 #endif
 
 	if (fmt[1] == 'R')
@@ -989,18 +989,22 @@ char *symbol_string(char *buf, char *end, void *ptr,
 	value = (unsigned long)ptr;
 
 #ifdef CONFIG_KALLSYMS
+	maxlen = end - buf;
+	if (maxlen > spec.precision)
+		maxlen = spec.precision;
+
 	if (*fmt == 'B' && fmt[1] == 'b')
-		sprint_backtrace_build_id(sym, value);
+		maxlen = sprint_backtrace_build_id(buf, maxlen, value);
 	else if (*fmt == 'B')
-		sprint_backtrace(sym, value);
+		maxlen = sprint_backtrace(buf, maxlen, value);
 	else if (*fmt == 'S' && (fmt[1] == 'b' || (fmt[1] == 'R' && fmt[2] == 'b')))
-		sprint_symbol_build_id(sym, value);
+		maxlen = sprint_symbol_build_id(buf, maxlen, value);
 	else if (*fmt != 's')
-		sprint_symbol(sym, value);
+		maxlen = sprint_symbol(buf, maxlen, value);
 	else
-		sprint_symbol_no_offset(sym, value);
+		maxlen = sprint_symbol_no_offset(buf, maxlen, value);
 
-	return string_nocheck(buf, end, sym, spec);
+	return widen_string(buf, maxlen, end, spec);
 #else
 	return special_hex_number(buf, end, value, sizeof(void *));
 #endif

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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 20:19               ` Linus Torvalds
@ 2022-06-26 20:39                 ` Joe Perches
  2022-06-26 20:51                   ` Kent Overstreet
  2022-06-26 20:54                   ` Linus Torvalds
  2022-06-27  8:25                 ` David Laight
  1 sibling, 2 replies; 86+ messages in thread
From: Joe Perches @ 2022-06-26 20:39 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Andrew Morton, David Laight, Petr Mladek, Steven Rostedt,
	Sergey Senozhatsky, Rasmus Villemoes, Matthew Wilcox,
	Miguel Ojeda, Kent Overstreet, Andy Shevchenko, LKML, linux-mm

On Sun, 2022-06-26 at 13:19 -0700, Linus Torvalds wrote:
> On Sun, Jun 26, 2022 at 12:53 PM Joe Perches <joe@perches.com> wrote:
> > 
> > In a reply to the printbufs thread, I wrote a proposal to use an
> > alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.
> > 
> > No one has replied to this but I think it's somewhat sensible.
> 
> I think that's a bad idea.

Somewhat sensible not sensible...

> Those things are *literally* called from panic situations, which may
> be while holding core memory allocation locks, or similar.

True, and special_hex_number was used on alloc failure.

> Now, you are correct that the stack buffer is annoying. But I think
> the proper way to fix that is to say "we already *have* the target
> buffer, let's use it".

OK, and that's true for all the temp stack buffers in every %p<foo>.

> That does require teaching the sprint_symbol() functions that they
> need to take a "length of buffer" and return how much they used, but
> that would seem to be a sensible thing anyway, and what the code
> should always have done?

Unnecessary stack and/or unnecessary buffers for printbufs are
just unnecessary. 

> It's bad policy to just pass in a buffer without length, and I think
> it was always broken. Nasty. That KSYM_SYMBOL_LEN is magically taking
> care of it all, but it's ugly as heck, wouldn't you say?

Yup.


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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 20:39                 ` Joe Perches
@ 2022-06-26 20:51                   ` Kent Overstreet
  2022-06-26 21:02                     ` Joe Perches
  2022-06-26 20:54                   ` Linus Torvalds
  1 sibling, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-06-26 20:51 UTC (permalink / raw)
  To: Joe Perches
  Cc: Linus Torvalds, Andrew Morton, David Laight, Petr Mladek,
	Steven Rostedt, Sergey Senozhatsky, Rasmus Villemoes,
	Matthew Wilcox, Miguel Ojeda, Andy Shevchenko, LKML, linux-mm

On Sun, Jun 26, 2022 at 01:39:01PM -0700, Joe Perches wrote:
> On Sun, 2022-06-26 at 13:19 -0700, Linus Torvalds wrote:
> > On Sun, Jun 26, 2022 at 12:53 PM Joe Perches <joe@perches.com> wrote:
> > > 
> > > In a reply to the printbufs thread, I wrote a proposal to use an
> > > alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.
> > > 
> > > No one has replied to this but I think it's somewhat sensible.
> > 
> > I think that's a bad idea.
> 
> Somewhat sensible not sensible...
> 
> > Those things are *literally* called from panic situations, which may
> > be while holding core memory allocation locks, or similar.
> 
> True, and special_hex_number was used on alloc failure.
> 
> > Now, you are correct that the stack buffer is annoying. But I think
> > the proper way to fix that is to say "we already *have* the target
> > buffer, let's use it".
> 
> OK, and that's true for all the temp stack buffers in every %p<foo>.

Serious question: why are you trying to half-ass just _one_ of these functions
when I've been working on solving stack allocations in all of them? I've already
killed every single stack buffer in the last patch series that was posted except
for symbol_string(). I would welcome the help if you wanted to tackle that one.

It's not completely trivial because while kallsyms_expand_symbol() can be
trivially converted to printbufs - it just outputs one char at a time, so that
becomes prt_char() - it's not a output-once-and-done algorithm:
cleanup_symbol_name() then goes back over the output buffer and modifies it in
place (ew), because of weird clang things that it's got #ifdefs for.

Specifically, it's truncating the output at the first period (easy to convert;
just stop when we see a period) but then it truncates at the last $ - why? do we
have symbols when building with gcc that legitimately have dollar signs in them?
One would hope we could drop the #ifdefs but that needs to be checked.

That's as far as I got looking at it just now. It will be some time before I
have time to do it properly and run tests and research what I'm doing (because
I am completely buried in bcachefs bugs to work through at the moment, and
getting sucked into working on test automation _again_ when I have a million
more valuable things I could be working on because the test infrastructure story
for the kernel is still a shitshow, but that's another rant).

Anayways, I'm trying to take the time to do this stuff right, but there's a
_lot_ of work involved, so if you want to help out instead of just slag what I'm
doing... well, it'd be nice...

Cheers,
Kent

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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 20:39                 ` Joe Perches
  2022-06-26 20:51                   ` Kent Overstreet
@ 2022-06-26 20:54                   ` Linus Torvalds
  1 sibling, 0 replies; 86+ messages in thread
From: Linus Torvalds @ 2022-06-26 20:54 UTC (permalink / raw)
  To: Joe Perches
  Cc: Andrew Morton, David Laight, Petr Mladek, Steven Rostedt,
	Sergey Senozhatsky, Rasmus Villemoes, Matthew Wilcox,
	Miguel Ojeda, Kent Overstreet, Andy Shevchenko, LKML, linux-mm

On Sun, Jun 26, 2022 at 1:39 PM Joe Perches <joe@perches.com> wrote:
>
> OK, and that's true for all the temp stack buffers in every %p<foo>.

Yup.

A lot of them are simply due to it just being simple, and when the
temp buffer is of a fairly limited size, "simple is good".

But yeah, that KSYM_SYMBOL_LEN thing was questionable before, and as
it grows with KSYM_NAME_LEN growing, it's getting pretty ridiculous.

For example, the buffer in "number()" looks very reasonable to me,
since it's not only pretty small (24 bytes on 64-bit architectures).
it has that special alignment requirement too.

So I don't think those temporary stack buffers are necessarily wrong
in general, but there's a point where they go from "ok, there's being
_simple_ and then there's _overly_simplistic_".

                   Linus

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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 20:51                   ` Kent Overstreet
@ 2022-06-26 21:02                     ` Joe Perches
  2022-06-26 21:10                       ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: Joe Perches @ 2022-06-26 21:02 UTC (permalink / raw)
  To: Kent Overstreet
  Cc: Linus Torvalds, Andrew Morton, David Laight, Petr Mladek,
	Steven Rostedt, Sergey Senozhatsky, Rasmus Villemoes,
	Matthew Wilcox, Miguel Ojeda, Andy Shevchenko, LKML, linux-mm

On Sun, 2022-06-26 at 16:51 -0400, Kent Overstreet wrote:
> On Sun, Jun 26, 2022 at 01:39:01PM -0700, Joe Perches wrote:
> > On Sun, 2022-06-26 at 13:19 -0700, Linus Torvalds wrote:
> > > On Sun, Jun 26, 2022 at 12:53 PM Joe Perches <joe@perches.com> wrote:
> > > > 
> > > > In a reply to the printbufs thread, I wrote a proposal to use an
> > > > alloc to reduce stack in vsprintf when CONFIG_KALLSYMS is enabled.
> > > > 
> > > > No one has replied to this but I think it's somewhat sensible.
> > > 
> > > I think that's a bad idea.
> > 
> > Somewhat sensible not sensible...
> > 
> > > Those things are *literally* called from panic situations, which may
> > > be while holding core memory allocation locks, or similar.
> > 
> > True, and special_hex_number was used on alloc failure.
> > 
> > > Now, you are correct that the stack buffer is annoying. But I think
> > > the proper way to fix that is to say "we already *have* the target
> > > buffer, let's use it".
> > 
> > OK, and that's true for all the temp stack buffers in every %p<foo>.
> 
> Serious question: why are you trying to half-ass just _one_ of these functions
> when I've been working on solving stack allocations in all of them?

Because the stack use in _this_ function is quite large.
Backporting to stable would be trivial.
No so with printbufs.

> if you want to help out instead of just slag what I'm
> doing... well, it'd be nice...

Also nice to _be_ nice.
Honestly Kent, I haven't seen much of that from you.


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

* Re: [RFC[ Alloc in vsprintf
  2022-06-26 21:02                     ` Joe Perches
@ 2022-06-26 21:10                       ` Kent Overstreet
  0 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-26 21:10 UTC (permalink / raw)
  To: Joe Perches
  Cc: Linus Torvalds, Andrew Morton, David Laight, Petr Mladek,
	Steven Rostedt, Sergey Senozhatsky, Rasmus Villemoes,
	Matthew Wilcox, Miguel Ojeda, Andy Shevchenko, LKML, linux-mm

On Sun, Jun 26, 2022 at 02:02:31PM -0700, Joe Perches wrote:
> Because the stack use in _this_ function is quite large.
> Backporting to stable would be trivial.
> No so with printbufs.

Yeah, I'm not so sure "do what's easiest to backport to stable" makes any sense
to me.

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

* RE: [RFC[ Alloc in vsprintf
  2022-06-26 20:19               ` Linus Torvalds
  2022-06-26 20:39                 ` Joe Perches
@ 2022-06-27  8:25                 ` David Laight
  2022-06-28  2:56                   ` Kent Overstreet
  1 sibling, 1 reply; 86+ messages in thread
From: David Laight @ 2022-06-27  8:25 UTC (permalink / raw)
  To: 'Linus Torvalds', Joe Perches
  Cc: Andrew Morton, Petr Mladek, Steven Rostedt, Sergey Senozhatsky,
	Rasmus Villemoes, Matthew Wilcox, Miguel Ojeda, Kent Overstreet,
	Andy Shevchenko, LKML, linux-mm

From: Linus Torvalds
> Sent: 26 June 2022 21:19
..
> That does require teaching the sprint_symbol() functions that they
> need to take a "length of buffer" and return how much they used, but
> that would seem to be a sensible thing anyway, and what the code
> should always have done?

It needs to return the 'length it would have used'.
While occasionally useful I'm pretty sure this is actually
a side effect of the was that libc snprintf() was originally
implemented (sprintf() had an on-stack FILE).

In any case it might be simplest to pass all these functions
the write pointer and buffer limit and have them return the
new write pointer.
It is likely to generate much better code that passing
a structure by reference.

Only the original caller needs to know where the buffer starts.
The original caller is also the only place that needs to
ensure that the string is correctly terminated.

You'd get helpers like:

char *add_char(char *wp, const char *lim, char add)
{
	if (lim < wp)
		*wp = add;
	return wp + 1;
}

char *add_chars(char *wp, const char *lim, const char *add, long int count)
{
	long int space = lim - wp;
	long int i;

	if (space > count)
		space = count;
	for (i = i; i < space; i++)
		wp[i] = add[i];
	
	return wp + count;
}

char *add_str(char *wp, const char *lim, const char *add)
{
	while (*add) {
		if (wp >= lim)
			return wp + strlen(add);
		*wp++ = *add++;
	}
	
	return wp;
}

	David

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

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

* Re: [RFC[ Alloc in vsprintf
  2022-06-27  8:25                 ` David Laight
@ 2022-06-28  2:56                   ` Kent Overstreet
  0 siblings, 0 replies; 86+ messages in thread
From: Kent Overstreet @ 2022-06-28  2:56 UTC (permalink / raw)
  To: David Laight, 'Linus Torvalds', Joe Perches
  Cc: Andrew Morton, Petr Mladek, Steven Rostedt, Sergey Senozhatsky,
	Rasmus Villemoes, Matthew Wilcox, Miguel Ojeda, Andy Shevchenko,
	LKML, linux-mm

On 6/27/22 04:25, David Laight wrote:
> From: Linus Torvalds
>> Sent: 26 June 2022 21:19
> ..
>> That does require teaching the sprint_symbol() functions that they
>> need to take a "length of buffer" and return how much they used, but
>> that would seem to be a sensible thing anyway, and what the code
>> should always have done?
> 
> It needs to return the 'length it would have used'.
> While occasionally useful I'm pretty sure this is actually
> a side effect of the was that libc snprintf() was originally
> implemented (sprintf() had an on-stack FILE).
> 
> In any case it might be simplest to pass all these functions
> the write pointer and buffer limit and have them return the
> new write pointer.
> It is likely to generate much better code that passing
> a structure by reference.

I've said it before, and now I'm going to say it again more forcefully:

This obsession with perfect machine code in every situation, regardless 
of whether it's shown up in benchmarks or profiles, regardless of what 
it does to the readability of the C code that we humans work with, has 
to stop.

Has. To. Stop. Full stop.

We have to be thinking about the people who come after us and have to 
read and maintain this stuff. Linux should still be in use 50, 100 years 
from now, and if it's not it's because we _fucked up_, and in software 
the way you fuck up is by writing code no one can understand - by 
writing code that people become afraid to touch without breaking it.

This happens, routinely, and it's _painful_ when it does.

A big learning experience for me when I was a much younger engineer, 
freshly starting at Google, was working next to a bunch of guys who were 
all chasing and fixing bugs in ext4 - and they weren't exactly enjoying 
it. bcache uncovered one or two of them too, and I got to debug that and 
then had to argue that it wasn't a bug in bcache (we were calling 
bio->bi_endio in process context, which uncovered a locking bug in 
ext4). The codebase had become a mess that they were too scared to 
refactor, in part because there were too many options that were 
impossible to test - my big lesson from that is that the code you're 
scared to refactor, that's the code that needs it the most.

And I could name some very senior kernel people who write code that's 
too brittle in the name of chasing performance - in fact I will name 
one, because I know he won't take it personally: the writeback 
throttling code that Jens wrote was buggy for _ages_ and at least one of 
my users was regularly tripping over it and I couldn't make out what the 
hell that code was trying to do, and not for lack of trying.

Other code nightmares:
  - The old O_DIRECT code, which was a huge performance sink but no one 
could touch it without breaking something (I spent months on a beautiful 
refactoring that cut it in half by LOC and improved performance 
drastically, but I couldn't get it to completely pass xfstests. That 
sucked).
  - The old generic_file_buffered_read(), which was a 250 line monster 
filled with gotos - all in the name of performance, mind you - that 
people barely touched, and when people absolutely had to they'd do so in 
the most minimal way possible that ended up just adding to the mess 
(e.g. the IOCB_NOWAIT changes) - up until I finally cut it apart, then 
curiously right after that a ton more patches landed. It's almost like 
cleaning stuff up and prioritizing readability makes it easier for 
people to work on.
  - merge_bvec_fn was also quite the tale - also done in the name of 
performance, noticing a theme here?

I like to write fast code too. Of course I do, I'm a kernel engineer, I 
wouldn't be a real one if I didn't.

But that means writing code that is _optimizable_, which means writing 
code that's easy to go back and modify and change when profiling 
discovers something. Which means keeping things as simple as is 
reasonably possible, and prioritizing good data types and abstractions 
and structure.

When I'm first writing code and thinking about performance, here's what 
I think about:
  - algorithmic complexity
  - good data structures (vectors instead of lists, where it matters - 
it often doesn't)
  - memory layout: keep pointer indirection at an absolute minimum
    memory layout matters
  - locking

And honestly, not much else. Because on modern machines, with the type 
of code we feed our CPUs running in the kernel, memory layout and 
locking are what matter and not much else. Not shaving every cycle.

I already demonstrated this with actual numbers in the printbuf 
discussion, to Rasmus - yes, the compiler constantly reloading is a 
shame and it shows up in the text size, and perhaps we'll want to 
revisit the -fno-strict-aliasing thing someday (I'm fully in agreement 
with Linus on why he hates strict aliasing, it was something the 
compiler people sprung on everyone else without discussion or a clear 
escape hatch or _tooling to deal with existing codebases_ but the 
tooling has improved since thing, it might not be complete insanity anymore)

...but if you look at the actual microbenchmarks I showed Rasmus? It 
turns out to not affect performance pretty much at all, it's in the 
noise. Waiting on loads from memory is what matters to us, and not much 
else.

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

* Re: [PATCH v4 26/34] tools/testing/nvdimm: Convert to printbuf
  2022-06-24 23:42     ` Santosh Sivaraj
@ 2022-07-01  6:32       ` Shivaprasad G Bhat
  0 siblings, 0 replies; 86+ messages in thread
From: Shivaprasad G Bhat @ 2022-07-01  6:32 UTC (permalink / raw)
  To: Santosh Sivaraj, Dan Williams
  Cc: Kent Overstreet, linux-kernel, linux-mm, Petr Mladek,
	Steven Rostedt, enozhatsky, Rasmus Villemoes, willy, Dave Hansen,
	Linux NVDIMM, Vaibhav Jain

On 6/25/22 05:12, Santosh Sivaraj wrote:
> I don't have setup to test this now. Adding Shiva and Vaibhav who could 
> probably help. Thanks, Santosh On Sat, 25 Jun, 2022, 1:03 am Dan 
> Williams, <dan.j.williams@intel.com> wrote: [ add Santosh ] Kent 
> Overstreet wrote: ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍
> ZjQcmQRYFpfptBannerStart
> This Message Is From an External Sender
> This message came from outside your organization.
> ZjQcmQRYFpfptBannerEnd
> I don't have setup to test this now. Adding Shiva and Vaibhav who could 
> probably help.
> 
> Thanks,
> Santosh
> 
> On Sat, 25 Jun, 2022, 1:03 am Dan Williams, <dan.j.williams@intel.com 
> <mailto:dan.j.williams@intel.com>> wrote:
> 
>     [ add Santosh ]
> 
>     Kent Overstreet wrote:
>      > 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
>     <mailto:kent.overstreet@gmail.com>>
>      > Cc: Dan Williams <dan.j.williams@intel.com
>     <mailto:dan.j.williams@intel.com>>
>      > Cc: Dave Hansen <dave.hansen@linux.intel.com
>     <mailto:dave.hansen@linux.intel.com>>
>      > Cc: nvdimm@lists.linux.dev <mailto:nvdimm@lists.linux.dev>
> 
>     Acked-by: Dan Williams <dan.j.williams@intel.com
>     <mailto:dan.j.williams@intel.com>>
> 

The ndtest build requires [1] as the build is currently broken from 
nd_namespace_blk/blk_region infrastructure removal.

Dan, Could you review [1] and see if it can be included as well ?

With [1], the this patch is tested, and works fine.

Tested-By: Shivaprasad G Bhat <sbhat@linux.ibm.com>

References:
[1] 
https://patchwork.kernel.org/project/linux-nvdimm/patch/165025395730.2821159.14794984437851867426.stgit@lep8c.aus.stglabs.ibm.com/


>     This probably also wants a Tested-by from Santosh, but it looks ok
>     to me.
> 

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
                   ` (35 preceding siblings ...)
  2022-06-21  6:11 ` Rasmus Villemoes
@ 2022-07-19 23:15 ` Steven Rostedt
  2022-07-19 23:43   ` Kent Overstreet
  36 siblings, 1 reply; 86+ messages in thread
From: Steven Rostedt @ 2022-07-19 23:15 UTC (permalink / raw)
  To: Kent Overstreet; +Cc: linux-kernel, linux-mm, pmladek, enozhatsky, linux, willy

On Sun, 19 Jun 2022 20:41:59 -0400
Kent Overstreet <kent.overstreet@gmail.com> wrote:

> Core idea: Wouldn't it be nice if we had a common data structure and calling
> convention for outputting strings?

Because seq_buf gives us this already, the cover letter really just needs
to state exactly what the benefit is to replace seq_buf with printbuf (and
why seq_buf can not be simply extended to do some extra features).

I just applied your series and ran the tracing selftests and several of
them failed.

 # cd tools/testing/selftests/ftrace/
 # ./ftracetest

This means that this is not a simple replacement and that there's going to
be regressions with this change. The question is, is the added benefits of
doing the change greater than the fallout of the regressions?

-- Steve

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-07-19 23:15 ` Steven Rostedt
@ 2022-07-19 23:43   ` Kent Overstreet
  2022-07-20  0:05     ` Steven Rostedt
  0 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-07-19 23:43 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-kernel, linux-mm, pmladek, enozhatsky, linux, willy

On 7/19/22 19:15, Steven Rostedt wrote:
> On Sun, 19 Jun 2022 20:41:59 -0400
> Kent Overstreet <kent.overstreet@gmail.com> wrote:
> 
>> Core idea: Wouldn't it be nice if we had a common data structure and calling
>> convention for outputting strings?
> 
> Because seq_buf gives us this already, the cover letter really just needs
> to state exactly what the benefit is to replace seq_buf with printbuf (and
> why seq_buf can not be simply extended to do some extra features).

  - seq_buf has the wrong semantics on overflow for what vsnprintf needs.
  - seq_buf is somewhat unnecessarily coupled to tracing needs - the 
readpos member has nothing to do with outputting formatting strings, and 
some of the pretty-printers are tracing specific and don't really belong 
in a generic pretty-printing library.

And, when I tried to talk to you about changing seq_buf to be more 
suitable you didn't respond - you just dropped off the IRC discussion we 
were having.

> 
> I just applied your series and ran the tracing selftests and several of
> them failed.
> 
>   # cd tools/testing/selftests/ftrace/
>   # ./ftracetest

Thank you for telling me where to find the tests. It would've saved us 
some back and forth (and I could've gotten on this sooner) if you'd 
responded when I asked before.

It may seem like the perfectly natural place to look to you - who works 
on the code - but to someone who works on a variety of subsystems, each 
of which puts their test code (if they have any!) in a different place, 
it wasn't.

However, when I enabled all the tracing kernel config options, your 
tests are now failing to run at all with:

db_root: cannot open: /etc/target

So now I've got to debug your tests, too. Gah.

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-07-19 23:43   ` Kent Overstreet
@ 2022-07-20  0:05     ` Steven Rostedt
  2022-07-20  0:17       ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: Steven Rostedt @ 2022-07-20  0:05 UTC (permalink / raw)
  To: Kent Overstreet; +Cc: linux-kernel, linux-mm, pmladek, enozhatsky, linux, willy

On Tue, 19 Jul 2022 19:43:46 -0400
Kent Overstreet <kent.overstreet@gmail.com> wrote:

> On 7/19/22 19:15, Steven Rostedt wrote:
> > On Sun, 19 Jun 2022 20:41:59 -0400
> > Kent Overstreet <kent.overstreet@gmail.com> wrote:
> >   
> >> Core idea: Wouldn't it be nice if we had a common data structure and calling
> >> convention for outputting strings?  
> > 
> > Because seq_buf gives us this already, the cover letter really just needs
> > to state exactly what the benefit is to replace seq_buf with printbuf (and
> > why seq_buf can not be simply extended to do some extra features).  
> 
>   - seq_buf has the wrong semantics on overflow for what vsnprintf needs.

More specific please.

>   - seq_buf is somewhat unnecessarily coupled to tracing needs - the 
> readpos member has nothing to do with outputting formatting strings, and 
> some of the pretty-printers are tracing specific and don't really belong 
> in a generic pretty-printing library.

That's not really a benefit between the two.

> 
> And, when I tried to talk to you about changing seq_buf to be more 
> suitable you didn't respond - you just dropped off the IRC discussion we 
> were having.

I told you I've been swamped and this wasn't the best time for me. I
can't drop everything for you.

> 
> > 
> > I just applied your series and ran the tracing selftests and several of
> > them failed.
> > 
> >   # cd tools/testing/selftests/ftrace/
> >   # ./ftracetest  
> 
> Thank you for telling me where to find the tests. It would've saved us 
> some back and forth (and I could've gotten on this sooner) if you'd 
> responded when I asked before.

It's in kernel selftests, they are not hard to find.

> 
> It may seem like the perfectly natural place to look to you - who works 
> on the code - but to someone who works on a variety of subsystems, each 
> of which puts their test code (if they have any!) in a different place, 
> it wasn't.

All the subsystems tests should be in tools/testing/selftests this
isn't just where tracing goes. It's the standard place.

> 
> However, when I enabled all the tracing kernel config options, your 
> tests are now failing to run at all with:
> 
> db_root: cannot open: /etc/target
> 
> So now I've got to debug your tests, too. Gah.

WTF?

-- Steve

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-07-20  0:05     ` Steven Rostedt
@ 2022-07-20  0:17       ` Kent Overstreet
  2022-07-20  1:11         ` Steven Rostedt
  0 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-07-20  0:17 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-kernel, linux-mm, pmladek, enozhatsky, linux, willy

On 7/19/22 20:05, Steven Rostedt wrote:
> On Tue, 19 Jul 2022 19:43:46 -0400
> Kent Overstreet <kent.overstreet@gmail.com> wrote:
> 
>> On 7/19/22 19:15, Steven Rostedt wrote:
>>> On Sun, 19 Jun 2022 20:41:59 -0400
>>> Kent Overstreet <kent.overstreet@gmail.com> wrote:
>>>    
>>>> Core idea: Wouldn't it be nice if we had a common data structure and calling
>>>> convention for outputting strings?
>>>
>>> Because seq_buf gives us this already, the cover letter really just needs
>>> to state exactly what the benefit is to replace seq_buf with printbuf (and
>>> why seq_buf can not be simply extended to do some extra features).
>>
>>    - seq_buf has the wrong semantics on overflow for what vsnprintf needs.
> 
> More specific please.

Steve, look at the man page for snprintf if you don't see what I mean. 
This discussion has become entirely too tedious, and your _only_ 
contribution to the discussion on pretty-printers has been "why isn't 
this using this thing I made?".

You haven't been contributing to the discussion, you haven't been 
helping figure out what the APIs, helpers, data structures should look 
like, IOW _actually_ building something that could serve as a low level 
string formatting library.

I get that you're busy - but look, we all are, and this patch series has 
already been set back what, a month and a half while I was waiting on you.

I've got the tests now, I'll CC you when v5 is posted.

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-07-20  0:17       ` Kent Overstreet
@ 2022-07-20  1:11         ` Steven Rostedt
  2022-07-20  1:31           ` Kent Overstreet
  0 siblings, 1 reply; 86+ messages in thread
From: Steven Rostedt @ 2022-07-20  1:11 UTC (permalink / raw)
  To: Kent Overstreet; +Cc: linux-kernel, linux-mm, pmladek, enozhatsky, linux, willy

On Tue, 19 Jul 2022 20:17:45 -0400
Kent Overstreet <kent.overstreet@gmail.com> wrote:

> > More specific please.  
> 
> Steve, look at the man page for snprintf if you don't see what I mean. 
> This discussion has become entirely too tedious, and your _only_ 
> contribution to the discussion on pretty-printers has been "why isn't 
> this using this thing I made?".

No, my response is, why should we replace something that is working
just fine?

The burden is on you. For this to get accepted you have to show why a
risk of regression in the Linux kernel is worth the update. We don't
just say "hey this is better, take it!". That's not how this works.
There needs to be real needs to be met.

> 
> You haven't been contributing to the discussion, you haven't been 
> helping figure out what the APIs, helpers, data structures should look 
> like, IOW _actually_ building something that could serve as a low level 
> string formatting library.

What exactly is it that is better. In stead of yelling about what I
haven't done, tell me what you plan on doing that will make *our* lives
better. Code is "pulled" not "pushed". If all you can do is push, then
you are going to end up being quite disappointed.

What exactly is the use case here?

> 
> I get that you're busy - but look, we all are, and this patch series has 
> already been set back what, a month and a half while I was waiting on you.

Sorry, but I did warn you. I did start a new job and then I had several
conferences that I was writing code to present. That is, the things I
was talking about wasn't even finished yet. So those were very *hard*
deadlines (on top of my day job). I told you upfront that it may be
months before I can look at it. I was fully transparent.

> 
> I've got the tests now, I'll CC you when v5 is posted.

And I expect you to have an explanation on why this is worth the effort.

-- Steve

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-07-20  1:11         ` Steven Rostedt
@ 2022-07-20  1:31           ` Kent Overstreet
  2022-07-20  1:37             ` Steven Rostedt
  0 siblings, 1 reply; 86+ messages in thread
From: Kent Overstreet @ 2022-07-20  1:31 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-kernel, linux-mm, pmladek, enozhatsky, linux, willy

On 7/19/22 21:11, Steven Rostedt wrote:
> On Tue, 19 Jul 2022 20:17:45 -0400
> Kent Overstreet <kent.overstreet@gmail.com> wrote:
> 
>>> More specific please.
>>
>> Steve, look at the man page for snprintf if you don't see what I mean.
>> This discussion has become entirely too tedious, and your _only_
>> contribution to the discussion on pretty-printers has been "why isn't
>> this using this thing I made?".
> 
> No, my response is, why should we replace something that is working
> just fine?
For you. For your code.

Look, Steve, I've tried to work with you. And I've given you reasons why 
seq_buf doesn't work for vsprintf.c, and more general cases. You have 
not responded _at all_ with technical reasons or discussion, all you've 
done from the very start is lecture me on process.

And, to be blunt, the time to have the printbuf vs. seq_buf discussion 
was months ago. I tried to start that discussion with you, and you 
ghosted on IRC when I started talking about the things in seq_buf that 
would have to change.

Like I said, I'll CC you when v5 is posted.

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

* Re: [PATCH v4 00/34] Printbufs - new data structure for building strings
  2022-07-20  1:31           ` Kent Overstreet
@ 2022-07-20  1:37             ` Steven Rostedt
  0 siblings, 0 replies; 86+ messages in thread
From: Steven Rostedt @ 2022-07-20  1:37 UTC (permalink / raw)
  To: Kent Overstreet; +Cc: linux-kernel, linux-mm, pmladek, linux, willy

On Tue, 19 Jul 2022 21:31:49 -0400
Kent Overstreet <kent.overstreet@gmail.com> wrote:

> > No, my response is, why should we replace something that is working
> > just fine?  
> For you. For your code.

For the current working kernel.

> 
> Look, Steve, I've tried to work with you. And I've given you reasons why 
> seq_buf doesn't work for vsprintf.c, and more general cases. You have 

Please post the lore links, I'll go back and read them.

> not responded _at all_ with technical reasons or discussion, all you've 
> done from the very start is lecture me on process.

That's because process *is* the way things get into upstream. I guess
you fail to understand that.

> 
> And, to be blunt, the time to have the printbuf vs. seq_buf discussion 
> was months ago. I tried to start that discussion with you, and you 
> ghosted on IRC when I started talking about the things in seq_buf that 
> would have to change.

Look, that was when I was traveling. And when I'm traveling I pretty
much do not respond to IRC.

> 
> Like I said, I'll CC you when v5 is posted.

OK.

-- Steve


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

end of thread, other threads:[~2022-07-20  1:42 UTC | newest]

Thread overview: 86+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-20  0:41 [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 01/34] lib/printbuf: New data structure for printing strings Kent Overstreet
2022-06-20  4:44   ` David Laight
2022-06-20 15:30     ` Kent Overstreet
2022-06-20 15:53       ` David Laight
2022-06-20 16:14         ` Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 02/34] lib/string_helpers: Convert string_escape_mem() to printbuf Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 03/34] vsprintf: Convert " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 04/34] lib/hexdump: " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 05/34] vsprintf: %pf(%p) Kent Overstreet
2022-06-21  7:04   ` Rasmus Villemoes
2022-06-21  7:51     ` Kent Overstreet
2022-06-21  8:47       ` Rasmus Villemoes
2022-06-21 11:11     ` David Laight
2022-06-20  0:42 ` [PATCH v4 06/34] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 07/34] lib/printbuf: Heap allocation Kent Overstreet
2022-06-21  7:58   ` Rasmus Villemoes
2022-06-20  0:42 ` [PATCH v4 08/34] lib/printbuf: Tabstops, indenting Kent Overstreet
2022-06-21  8:14   ` Rasmus Villemoes
2022-06-20  0:42 ` [PATCH v4 09/34] lib/printbuf: Unit specifiers Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 10/34] lib/pretty-printers: prt_string_option(), prt_bitflags() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 11/34] vsprintf: Improve number() Kent Overstreet
2022-06-21  8:33   ` Rasmus Villemoes
2022-06-20  0:42 ` [PATCH v4 12/34] vsprintf: prt_u64_minwidth(), prt_u64() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 13/34] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
2022-06-21  7:19   ` Rasmus Villemoes
2022-06-21  7:52     ` Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 14/34] vsprintf: Start consolidating printf_spec handling Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 15/34] vsprintf: Refactor resource_string() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 16/34] vsprintf: Refactor fourcc_string() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 17/34] vsprintf: Refactor ip_addr_string() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 18/34] vsprintf: Refactor mac_address_string() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 19/34] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 20/34] vsprintf: flags_string() " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 21/34] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 22/34] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 23/34] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 24/34] mm/memcontrol.c: Convert to printbuf Kent Overstreet
2022-06-20 11:37   ` Michal Hocko
2022-06-20 15:13     ` Kent Overstreet
2022-06-20 15:52       ` Michal Hocko
2022-06-20  0:42 ` [PATCH v4 25/34] clk: tegra: bpmp: " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 26/34] tools/testing/nvdimm: " Kent Overstreet
2022-06-24 19:32   ` Dan Williams
2022-06-24 23:42     ` Santosh Sivaraj
2022-07-01  6:32       ` Shivaprasad G Bhat
2022-06-20  0:42 ` [PATCH v4 27/34] powerpc: " Kent Overstreet
2022-06-20  0:42   ` Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 28/34] x86/resctrl: " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 29/34] PCI/P2PDMA: " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 30/34] tracing: trace_events_synth: " Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 31/34] d_path: prt_path() Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 32/34] ACPI/APEI: Add missing include Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 33/34] tracing: Convert to printbuf Kent Overstreet
2022-06-20  0:42 ` [PATCH v4 34/34] Delete seq_buf Kent Overstreet
2022-06-20  4:19 ` [PATCH v4 00/34] Printbufs - new data structure for building strings David Laight
2022-06-20  4:54   ` Matthew Wilcox
2022-06-20  8:00     ` David Laight
2022-06-20 15:07   ` Kent Overstreet
2022-06-20 15:21     ` David Laight
2022-06-21  0:38     ` Joe Perches
2022-06-21  0:57       ` Kent Overstreet
2022-06-21  1:26         ` Joe Perches
2022-06-21  2:10           ` Joe Perches
2022-06-26 19:53             ` [RFC[ Alloc in vsprintf Joe Perches
2022-06-26 20:06               ` Kent Overstreet
2022-06-26 20:13                 ` Joe Perches
2022-06-26 20:19               ` Linus Torvalds
2022-06-26 20:39                 ` Joe Perches
2022-06-26 20:51                   ` Kent Overstreet
2022-06-26 21:02                     ` Joe Perches
2022-06-26 21:10                       ` Kent Overstreet
2022-06-26 20:54                   ` Linus Torvalds
2022-06-27  8:25                 ` David Laight
2022-06-28  2:56                   ` Kent Overstreet
2022-06-21  2:31           ` [PATCH v4 00/34] Printbufs - new data structure for building strings Kent Overstreet
2022-06-21  3:11   ` Kent Overstreet
2022-06-21  6:11 ` Rasmus Villemoes
2022-06-21  8:01   ` Kent Overstreet
2022-07-19 23:15 ` Steven Rostedt
2022-07-19 23:43   ` Kent Overstreet
2022-07-20  0:05     ` Steven Rostedt
2022-07-20  0:17       ` Kent Overstreet
2022-07-20  1:11         ` Steven Rostedt
2022-07-20  1:31           ` Kent Overstreet
2022-07-20  1:37             ` Steven Rostedt

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.