From 9af7706492f985867d070861fe39fee0fe41326f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:14 +0300 Subject: lib/vsprintf: Remove support for %pF and %pf in favour of %pS and %ps %pS and %ps are now the preferred conversion specifiers to print function names. The functionality is equivalent; remove the old, deprecated %pF and %pf support. Depends-on: commit 2d44d165e939 ("scsi: lpfc: Convert existing %pf users to %ps") Depends-on: commit b295c3e39c13 ("tools lib traceevent: Convert remaining %p[fF] users to %p[sS]") Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Petr Mladek Signed-off-by: Rafael J. Wysocki --- lib/vsprintf.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e78017a3e1bd..7eb6417b3aae 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -918,7 +918,7 @@ char *symbol_string(char *buf, char *end, void *ptr, #ifdef CONFIG_KALLSYMS if (*fmt == 'B') sprint_backtrace(sym, value); - else if (*fmt != 'f' && *fmt != 's') + else if (*fmt != 's') sprint_symbol(sym, value); else sprint_symbol_no_offset(sym, value); @@ -2016,9 +2016,7 @@ static char *kobject_string(char *buf, char *end, void *ptr, * * - 'S' For symbolic direct pointers (or function descriptors) with offset * - 's' For symbolic direct pointers (or function descriptors) without offset - * - 'F' Same as 'S' - * - 'f' Same as 's' - * - '[FfSs]R' as above with __builtin_extract_return_addr() translation + * - '[Ss]R' as above with __builtin_extract_return_addr() translation * - 'B' For backtraced symbolic direct pointers with offset * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref] * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201] @@ -2121,8 +2119,6 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, struct printf_spec spec) { switch (*fmt) { - case 'F': - case 'f': case 'S': case 's': ptr = dereference_symbol_descriptor(ptr); @@ -2819,8 +2815,6 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) /* Dereference of functions is still OK */ case 'S': case 's': - case 'F': - case 'f': case 'x': case 'K': save_arg(void *); -- cgit v1.2.3 From 1586c5ae2f9310235b5e70abe712c73fc32eb98f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:15 +0300 Subject: lib/vsprintf: Add a note on re-using %pf or %pF Add a note warning of re-use of obsolete %pf or %pF extensions. Signed-off-by: Sakari Ailus Suggested-by: Steven Rostedt (VMware) Reviewed-by: Petr Mladek Signed-off-by: Rafael J. Wysocki --- lib/vsprintf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 7eb6417b3aae..ac9f3207a8d6 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -2017,6 +2017,8 @@ static char *kobject_string(char *buf, char *end, void *ptr, * - 'S' For symbolic direct pointers (or function descriptors) with offset * - 's' For symbolic direct pointers (or function descriptors) without offset * - '[Ss]R' as above with __builtin_extract_return_addr() translation + * - '[Ff]' %pf and %pF were obsoleted and later removed in favor of + * %ps and %pS. Be careful when re-using these specifiers. * - 'B' For backtraced symbolic direct pointers with offset * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref] * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201] -- cgit v1.2.3 From a92eb7621b9fb2c28a588ce333d226f56fab6a85 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:16 +0300 Subject: lib/vsprintf: Make use of fwnode API to obtain node names and separators Instead of implementing our own means of discovering parent nodes, node names or counting how many parents a node has, use the newly added functions in the fwnode API to obtain that information. Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Petr Mladek Signed-off-by: Rafael J. Wysocki --- lib/vsprintf.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index ac9f3207a8d6..6917d36f7ec9 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef CONFIG_BLOCK #include #endif @@ -1872,32 +1873,25 @@ char *flags_string(char *buf, char *end, void *flags_ptr, return format_flags(buf, end, flags, names); } -static const char *device_node_name_for_depth(const struct device_node *np, int depth) -{ - for ( ; np && depth; depth--) - np = np->parent; - - return kbasename(np->full_name); -} - static noinline_for_stack -char *device_node_gen_full_name(const struct device_node *np, char *buf, char *end) +char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf, + char *end) { int depth; - const struct device_node *parent = np->parent; - /* special case for root node */ - if (!parent) - return string_nocheck(buf, end, "/", default_str_spec); + /* Loop starting from the root node to the current node. */ + for (depth = fwnode_count_parents(fwnode); depth >= 0; depth--) { + struct fwnode_handle *__fwnode = + fwnode_get_nth_parent(fwnode, depth); - for (depth = 0; parent->parent; depth++) - parent = parent->parent; - - for ( ; depth >= 0; depth--) { - buf = string_nocheck(buf, end, "/", default_str_spec); - buf = string(buf, end, device_node_name_for_depth(np, depth), + buf = string(buf, end, fwnode_get_name_prefix(__fwnode), + default_str_spec); + buf = string(buf, end, fwnode_get_name(__fwnode), default_str_spec); + + fwnode_handle_put(__fwnode); } + return buf; } @@ -1942,10 +1936,11 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, switch (*fmt) { case 'f': /* full_name */ - buf = device_node_gen_full_name(dn, buf, end); + buf = fwnode_full_name_string(of_fwnode_handle(dn), buf, + end); break; case 'n': /* name */ - p = kbasename(of_node_full_name(dn)); + 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); @@ -1955,7 +1950,7 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, buf = number(buf, end, (unsigned int)dn->phandle, num_spec); break; case 'P': /* path-spec */ - p = kbasename(of_node_full_name(dn)); + p = fwnode_get_name(of_fwnode_handle(dn)); if (!p[1]) p = "/"; buf = string(buf, end, p, str_spec); -- cgit v1.2.3 From 83abc5a77f3b028b8c845c39ce4053119e1de35b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:17 +0300 Subject: lib/vsprintf: OF nodes are first and foremost, struct device_nodes Factor out static kobject_string() function that simply calls device_node_string(), and thus remove references to kobjects (as these are struct device_node). Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Petr Mladek Signed-off-by: Rafael J. Wysocki --- lib/vsprintf.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 6917d36f7ec9..4b766ee00dea 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1915,6 +1915,9 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, struct printf_spec str_spec = spec; str_spec.field_width = -1; + if (fmt[0] != 'F') + return error_string(buf, end, "(%pO?)", spec); + if (!IS_ENABLED(CONFIG_OF)) return error_string(buf, end, "(%pOF?)", spec); @@ -1988,17 +1991,6 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, return widen_string(buf, buf - buf_start, end, spec); } -static char *kobject_string(char *buf, char *end, void *ptr, - struct printf_spec spec, const char *fmt) -{ - switch (fmt[1]) { - case 'F': - return device_node_string(buf, end, ptr, spec, fmt + 1); - } - - return error_string(buf, end, "(%pO?)", spec); -} - /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -2177,7 +2169,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case 'G': return flags_string(buf, end, ptr, spec, fmt); case 'O': - return kobject_string(buf, end, ptr, spec, fmt); + return device_node_string(buf, end, ptr, spec, fmt + 1); case 'x': return pointer_string(buf, end, ptr, spec); } -- cgit v1.2.3 From 3bd32d6a2ee62db3c5b06caf7b163b6a4d459ea1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:18 +0300 Subject: lib/vsprintf: Add %pfw conversion specifier for printing fwnode names Add support for %pfw conversion specifier (with "f" and "P" modifiers) to support printing full path of the node, including its name ("f") and only the node's name ("P") in the printk family of functions. The two flags have equivalent functionality to existing %pOF with the same two modifiers ("f" and "P") on OF based systems. The ability to do the same on ACPI based systems is added by this patch. On ACPI based systems the resulting strings look like \_SB.PCI0.CIO2.port@1.endpoint@0 where the nodes are separated by a dot (".") and the first three are ACPI device nodes and the latter two ACPI data nodes. Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Petr Mladek Signed-off-by: Rafael J. Wysocki --- Documentation/core-api/printk-formats.rst | 24 +++++++++++++++++++++ lib/vsprintf.c | 36 +++++++++++++++++++++++++++++++ scripts/checkpatch.pl | 8 +++++-- 3 files changed, 66 insertions(+), 2 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index 0c081edbe97e..a6df7e99e6c9 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -418,6 +418,30 @@ Examples:: Passed by reference. +Fwnode handles +-------------- + +:: + + %pfw[fP] + +For printing information on fwnode handles. The default is to print the full +node name, including the path. The modifiers are functionally equivalent to +%pOF above. + + - f - full name of the node, including the path + - P - the name of the node including an address (if there is one) + +Examples (ACPI):: + + %pfwf \_SB.PCI0.CIO2.port@1.endpoint@0 - Full node name + %pfwP endpoint@0 - Node name + +Examples (OF):: + + %pfwf /ocp@68000000/i2c@48072000/camera@10/port/endpoint - Full name + %pfwP endpoint - Node name + Time and date (struct rtc_time) ------------------------------- diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 4b766ee00dea..65dd5804a93b 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1991,6 +1991,36 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, return widen_string(buf, buf - buf_start, end, spec); } +static noinline_for_stack +char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode, + struct printf_spec spec, const char *fmt) +{ + struct printf_spec str_spec = spec; + char *buf_start = buf; + + str_spec.field_width = -1; + + if (*fmt != 'w') + return error_string(buf, end, "(%pf?)", spec); + + if (check_pointer(&buf, end, fwnode, spec)) + return buf; + + fmt++; + + switch (*fmt) { + case 'P': /* name */ + buf = string(buf, end, fwnode_get_name(fwnode), str_spec); + break; + case 'f': /* full_name */ + default: + buf = fwnode_full_name_string(fwnode, buf, end); + break; + } + + return widen_string(buf, buf - buf_start, end, spec); +} + /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -2095,6 +2125,10 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, * F device node flags * c major compatible string * C full compatible string + * - 'fw[fP]' For a firmware node (struct fwnode_handle) pointer + * Without an option prints the full name of the node + * f full name + * P node name, including a possible unit address * - 'x' For printing the address. Equivalent to "%lx". * * ** When making changes please also update: @@ -2170,6 +2204,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, return flags_string(buf, end, ptr, spec, fmt); case 'O': return device_node_string(buf, end, ptr, spec, fmt + 1); + case 'f': + return fwnode_string(buf, end, ptr, spec, fmt + 1); case 'x': return pointer_string(buf, end, ptr, spec); } diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 03c574a49e1a..3d1f08fa091c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -6015,14 +6015,18 @@ sub process { for (my $count = $linenr; $count <= $lc; $count++) { my $specifier; my $extension; + my $qualifier; my $bad_specifier = ""; my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); $fmt =~ s/%%//g; - while ($fmt =~ /(\%[\*\d\.]*p(\w))/g) { + while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) { $specifier = $1; $extension = $2; - if ($extension !~ /[SsBKRraEhMmIiUDdgVCbGNOxt]/) { + $qualifier = $3; + if ($extension !~ /[SsBKRraEhMmIiUDdgVCbGNOxtf]/ || + ($extension eq "f" && + defined $qualifier && $qualifier !~ /^w/)) { $bad_specifier = $specifier; last; } -- cgit v1.2.3 From 57f5677e535ba24b8926a7125be2ef8d7f09323c Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 15 Oct 2019 21:07:05 +0200 Subject: printf: add support for printing symbolic error names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been suggested several times to extend vsnprintf() to be able to convert the numeric value of ENOSPC to print "ENOSPC". This implements that as a %p extension: With %pe, one can do if (IS_ERR(foo)) { pr_err("Sorry, can't do that: %pe\n", foo); return PTR_ERR(foo); } instead of what is seen in quite a few places in the kernel: if (IS_ERR(foo)) { pr_err("Sorry, can't do that: %ld\n", PTR_ERR(foo)); return PTR_ERR(foo); } If the value passed to %pe is an ERR_PTR, but the library function errname() added here doesn't know about the value, the value is simply printed in decimal. If the value passed to %pe is not an ERR_PTR, we treat it as an ordinary %p and thus print the hashed value (passing non-ERR_PTR values to %pe indicates a bug in the caller, but we can't do much about that). With my embedded hat on, and because it's not very invasive to do, I've made it possible to remove this. The errname() function and associated lookup tables take up about 3K. For most, that's probably quite acceptable and a price worth paying for more readable dmesg (once this starts getting used), while for those that disable printk() it's of very little use - I don't see a procfs/sysfs/seq_printf() file reasonably making use of this - and they clearly want to squeeze vmlinux as much as possible. Hence the default y if PRINTK. The symbols to include have been found by massaging the output of find arch include -iname 'errno*.h' | xargs grep -E 'define\s*E' In the cases where some common aliasing exists (e.g. EAGAIN=EWOULDBLOCK on all platforms, EDEADLOCK=EDEADLK on most), I've moved the more popular one (in terms of 'git grep -w Efoo | wc) to the bottom so that one takes precedence. Link: http://lkml.kernel.org/r/20191015190706.15989-1-linux@rasmusvillemoes.dk To: "Jonathan Corbet" To: linux-kernel@vger.kernel.org Cc: "Andy Shevchenko" Cc: "Andrew Morton" Cc: "Joe Perches" Cc: linux-doc@vger.kernel.org Signed-off-by: Rasmus Villemoes Acked-by: Uwe Kleine-König Reviewed-by: Petr Mladek [andy.shevchenko@gmail.com: use abs()] Acked-by: Andy Shevchenko Signed-off-by: Petr Mladek --- Documentation/core-api/printk-formats.rst | 12 ++ include/linux/errname.h | 16 +++ lib/Kconfig.debug | 9 ++ lib/Makefile | 1 + lib/errname.c | 223 ++++++++++++++++++++++++++++++ lib/test_printf.c | 21 +++ lib/vsprintf.c | 27 ++++ 7 files changed, 309 insertions(+) create mode 100644 include/linux/errname.h create mode 100644 lib/errname.c (limited to 'lib/vsprintf.c') diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index 75d2bbe9813f..dffd5c0b24f8 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -79,6 +79,18 @@ has the added benefit of providing a unique identifier. On 64-bit machines the first 32 bits are zeroed. The kernel will print ``(ptrval)`` until it gathers enough entropy. If you *really* want the address see %px below. +Error Pointers +-------------- + +:: + + %pe -ENOSPC + +For printing error pointers (i.e. a pointer for which IS_ERR() is true) +as a symbolic error name. Error values for which no symbolic name is +known are printed in decimal, while a non-ERR_PTR passed as the +argument to %pe gets treated as ordinary %p. + Symbols/Function Pointers ------------------------- diff --git a/include/linux/errname.h b/include/linux/errname.h new file mode 100644 index 000000000000..e8576ad90cb7 --- /dev/null +++ b/include/linux/errname.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_ERRNAME_H +#define _LINUX_ERRNAME_H + +#include + +#ifdef CONFIG_SYMBOLIC_ERRNAME +const char *errname(int err); +#else +static inline const char *errname(int err) +{ + return NULL; +} +#endif + +#endif /* _LINUX_ERRNAME_H */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 06d9c9d70385..4934b69c7b39 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -164,6 +164,15 @@ config DYNAMIC_DEBUG See Documentation/admin-guide/dynamic-debug-howto.rst for additional information. +config SYMBOLIC_ERRNAME + bool "Support symbolic error names in printf" + default y if PRINTK + help + If you say Y here, the kernel's printf implementation will + be able to print symbolic error names such as ENOSPC instead + of the number 28. It makes the kernel image slightly larger + (about 3KB), but can make the kernel logs easier to read. + endmenu # "printk and dmesg options" menu "Compile-time checks and compiler options" diff --git a/lib/Makefile b/lib/Makefile index d3daedf93c5a..907759f28916 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -183,6 +183,7 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o obj-$(CONFIG_DYNAMIC_DEBUG) += dynamic_debug.o +obj-$(CONFIG_SYMBOLIC_ERRNAME) += errname.o obj-$(CONFIG_NLATTR) += nlattr.o diff --git a/lib/errname.c b/lib/errname.c new file mode 100644 index 000000000000..0c4d3e66170e --- /dev/null +++ b/lib/errname.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +/* + * Ensure these tables do not accidentally become gigantic if some + * huge errno makes it in. On most architectures, the first table will + * only have about 140 entries, but mips and parisc have more sparsely + * allocated errnos (with EHWPOISON = 257 on parisc, and EDQUOT = 1133 + * on mips), so this wastes a bit of space on those - though we + * special case the EDQUOT case. + */ +#define E(err) [err + BUILD_BUG_ON_ZERO(err <= 0 || err > 300)] = "-" #err +static const char *names_0[] = { + E(E2BIG), + E(EACCES), + E(EADDRINUSE), + E(EADDRNOTAVAIL), + E(EADV), + E(EAFNOSUPPORT), + E(EALREADY), + E(EBADE), + E(EBADF), + E(EBADFD), + E(EBADMSG), + E(EBADR), + E(EBADRQC), + E(EBADSLT), + E(EBFONT), + E(EBUSY), +#ifdef ECANCELLED + E(ECANCELLED), +#endif + E(ECHILD), + E(ECHRNG), + E(ECOMM), + E(ECONNABORTED), + E(ECONNRESET), + E(EDEADLOCK), + E(EDESTADDRREQ), + E(EDOM), + E(EDOTDOT), +#ifndef CONFIG_MIPS + E(EDQUOT), +#endif + E(EEXIST), + E(EFAULT), + E(EFBIG), + E(EHOSTDOWN), + E(EHOSTUNREACH), + E(EHWPOISON), + E(EIDRM), + E(EILSEQ), +#ifdef EINIT + E(EINIT), +#endif + E(EINPROGRESS), + E(EINTR), + E(EINVAL), + E(EIO), + E(EISCONN), + E(EISDIR), + E(EISNAM), + E(EKEYEXPIRED), + E(EKEYREJECTED), + E(EKEYREVOKED), + E(EL2HLT), + E(EL2NSYNC), + E(EL3HLT), + E(EL3RST), + E(ELIBACC), + E(ELIBBAD), + E(ELIBEXEC), + E(ELIBMAX), + E(ELIBSCN), + E(ELNRNG), + E(ELOOP), + E(EMEDIUMTYPE), + E(EMFILE), + E(EMLINK), + E(EMSGSIZE), + E(EMULTIHOP), + E(ENAMETOOLONG), + E(ENAVAIL), + E(ENETDOWN), + E(ENETRESET), + E(ENETUNREACH), + E(ENFILE), + E(ENOANO), + E(ENOBUFS), + E(ENOCSI), + E(ENODATA), + E(ENODEV), + E(ENOENT), + E(ENOEXEC), + E(ENOKEY), + E(ENOLCK), + E(ENOLINK), + E(ENOMEDIUM), + E(ENOMEM), + E(ENOMSG), + E(ENONET), + E(ENOPKG), + E(ENOPROTOOPT), + E(ENOSPC), + E(ENOSR), + E(ENOSTR), +#ifdef ENOSYM + E(ENOSYM), +#endif + E(ENOSYS), + E(ENOTBLK), + E(ENOTCONN), + E(ENOTDIR), + E(ENOTEMPTY), + E(ENOTNAM), + E(ENOTRECOVERABLE), + E(ENOTSOCK), + E(ENOTTY), + E(ENOTUNIQ), + E(ENXIO), + E(EOPNOTSUPP), + E(EOVERFLOW), + E(EOWNERDEAD), + E(EPERM), + E(EPFNOSUPPORT), + E(EPIPE), +#ifdef EPROCLIM + E(EPROCLIM), +#endif + E(EPROTO), + E(EPROTONOSUPPORT), + E(EPROTOTYPE), + E(ERANGE), + E(EREMCHG), +#ifdef EREMDEV + E(EREMDEV), +#endif + E(EREMOTE), + E(EREMOTEIO), +#ifdef EREMOTERELEASE + E(EREMOTERELEASE), +#endif + E(ERESTART), + E(ERFKILL), + E(EROFS), +#ifdef ERREMOTE + E(ERREMOTE), +#endif + E(ESHUTDOWN), + E(ESOCKTNOSUPPORT), + E(ESPIPE), + E(ESRCH), + E(ESRMNT), + E(ESTALE), + E(ESTRPIPE), + E(ETIME), + E(ETIMEDOUT), + E(ETOOMANYREFS), + E(ETXTBSY), + E(EUCLEAN), + E(EUNATCH), + E(EUSERS), + E(EXDEV), + E(EXFULL), + + E(ECANCELED), /* ECANCELLED */ + E(EAGAIN), /* EWOULDBLOCK */ + E(ECONNREFUSED), /* EREFUSED */ + E(EDEADLK), /* EDEADLOCK */ +}; +#undef E + +#define E(err) [err - 512 + BUILD_BUG_ON_ZERO(err < 512 || err > 550)] = "-" #err +static const char *names_512[] = { + E(ERESTARTSYS), + E(ERESTARTNOINTR), + E(ERESTARTNOHAND), + E(ENOIOCTLCMD), + E(ERESTART_RESTARTBLOCK), + E(EPROBE_DEFER), + E(EOPENSTALE), + E(ENOPARAM), + + E(EBADHANDLE), + E(ENOTSYNC), + E(EBADCOOKIE), + E(ENOTSUPP), + E(ETOOSMALL), + E(ESERVERFAULT), + E(EBADTYPE), + E(EJUKEBOX), + E(EIOCBQUEUED), + E(ERECALLCONFLICT), +}; +#undef E + +static const char *__errname(unsigned err) +{ + if (err < ARRAY_SIZE(names_0)) + return names_0[err]; + if (err >= 512 && err - 512 < ARRAY_SIZE(names_512)) + return names_512[err - 512]; + /* But why? */ + if (IS_ENABLED(CONFIG_MIPS) && err == EDQUOT) /* 1133 */ + return "-EDQUOT"; + return NULL; +} + +/* + * errname(EIO) -> "EIO" + * errname(-EIO) -> "-EIO" + */ +const char *errname(int err) +{ + const char *name = __errname(abs(err)); + if (!name) + return NULL; + + return err > 0 ? name + 1 : name; +} diff --git a/lib/test_printf.c b/lib/test_printf.c index 5d94cbff2120..030daeb4fe21 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -593,6 +593,26 @@ flags(void) kfree(cmp_buffer); } +static void __init +errptr(void) +{ + test("-1234", "%pe", ERR_PTR(-1234)); + + /* Check that %pe with a non-ERR_PTR gets treated as ordinary %p. */ + BUILD_BUG_ON(IS_ERR(PTR)); + test_hashed("%pe", PTR); + +#ifdef CONFIG_SYMBOLIC_ERRNAME + test("(-ENOTSOCK)", "(%pe)", ERR_PTR(-ENOTSOCK)); + test("(-EAGAIN)", "(%pe)", ERR_PTR(-EAGAIN)); + BUILD_BUG_ON(EAGAIN != EWOULDBLOCK); + test("(-EAGAIN)", "(%pe)", ERR_PTR(-EWOULDBLOCK)); + test("[-EIO ]", "[%-8pe]", ERR_PTR(-EIO)); + test("[ -EIO]", "[%8pe]", ERR_PTR(-EIO)); + test("-EPROBE_DEFER", "%pe", ERR_PTR(-EPROBE_DEFER)); +#endif +} + static void __init test_pointer(void) { @@ -615,6 +635,7 @@ test_pointer(void) bitmap(); netdev_features(); flags(); + errptr(); } static void __init selftest(void) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e78017a3e1bd..b54d252b398e 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -21,6 +21,7 @@ #include #include #include +#include #include /* for KSYM_SYMBOL_LEN */ #include #include @@ -613,6 +614,25 @@ static char *string_nocheck(char *buf, char *end, const char *s, return widen_string(buf, len, end, spec); } +static char *err_ptr(char *buf, char *end, 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); +} + /* 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) @@ -2187,6 +2207,11 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, return kobject_string(buf, end, ptr, spec, fmt); case 'x': return pointer_string(buf, end, ptr, spec); + case 'e': + /* %pe with a non-ERR_PTR gets treated as plain %p */ + if (!IS_ERR(ptr)) + break; + return err_ptr(buf, end, ptr, spec); } /* default is to _not_ leak addresses, hash before printing */ @@ -2823,6 +2848,7 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) case 'f': case 'x': case 'K': + case 'e': save_arg(void *); break; default: @@ -2999,6 +3025,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) case 'f': case 'x': case 'K': + case 'e': process = true; break; default: -- cgit v1.2.3 From e4dcad204d3a281be6f8573e0a82648a4ad84e69 Mon Sep 17 00:00:00 2001 From: "Joel Fernandes (Google)" Date: Sat, 30 Nov 2019 17:50:33 -0800 Subject: rss_stat: add support to detect RSS updates of external mm When a process updates the RSS of a different process, the rss_stat tracepoint appears in the context of the process doing the update. This can confuse userspace that the RSS of process doing the update is updated, while in reality a different process's RSS was updated. This issue happens in reclaim paths such as with direct reclaim or background reclaim. This patch adds more information to the tracepoint about whether the mm being updated belongs to the current process's context (curr field). We also include a hash of the mm pointer so that the process who the mm belongs to can be uniquely identified (mm_id field). Also vsprintf.c is refactored a bit to allow reuse of hashing code. [akpm@linux-foundation.org: remove unused local `str'] [joelaf@google.com: inline call to ptr_to_hashval] Link: http://lore.kernel.org/r/20191113153816.14b95acd@gandalf.local.home Link: http://lkml.kernel.org/r/20191114164622.GC233237@google.com Link: http://lkml.kernel.org/r/20191106024452.81923-1-joel@joelfernandes.org Signed-off-by: Joel Fernandes (Google) Reported-by: Ioannis Ilkos Acked-by: Petr Mladek [lib/vsprintf.c] Cc: Tim Murray Cc: Michal Hocko Cc: Carmen Jackson Cc: Mayank Gupta Cc: Daniel Colascione Cc: Steven Rostedt (VMware) Cc: Minchan Kim Cc: "Aneesh Kumar K.V" Cc: Dan Williams Cc: Jerome Glisse Cc: Matthew Wilcox Cc: Ralph Campbell Cc: Vlastimil Babka Cc: Steven Rostedt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 8 ++++---- include/linux/string.h | 2 ++ include/trace/events/kmem.h | 32 +++++++++++++++++++++++++++++--- lib/vsprintf.c | 40 +++++++++++++++++++++++++++++----------- mm/memory.c | 4 ++-- 5 files changed, 66 insertions(+), 20 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/include/linux/mm.h b/include/linux/mm.h index 935383081397..b5b2523c80af 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1643,27 +1643,27 @@ static inline unsigned long get_mm_counter(struct mm_struct *mm, int member) return (unsigned long)val; } -void mm_trace_rss_stat(int member, long count); +void mm_trace_rss_stat(struct mm_struct *mm, int member, long count); static inline void add_mm_counter(struct mm_struct *mm, int member, long value) { long count = atomic_long_add_return(value, &mm->rss_stat.count[member]); - mm_trace_rss_stat(member, count); + mm_trace_rss_stat(mm, member, count); } static inline void inc_mm_counter(struct mm_struct *mm, int member) { long count = atomic_long_inc_return(&mm->rss_stat.count[member]); - mm_trace_rss_stat(member, count); + mm_trace_rss_stat(mm, member, count); } static inline void dec_mm_counter(struct mm_struct *mm, int member) { long count = atomic_long_dec_return(&mm->rss_stat.count[member]); - mm_trace_rss_stat(member, count); + mm_trace_rss_stat(mm, member, count); } /* Optimized variant when page is already known not to be PageAnon */ diff --git a/include/linux/string.h b/include/linux/string.h index b6ccdc2c7f02..02894e417565 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -216,6 +216,8 @@ int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4); extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, const void *from, size_t available); +int ptr_to_hashval(const void *ptr, unsigned long *hashval_out); + /** * strstarts - does @str start with @prefix? * @str: string to examine diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h index 5a0666bfcf85..ad7e642bd497 100644 --- a/include/trace/events/kmem.h +++ b/include/trace/events/kmem.h @@ -316,24 +316,50 @@ TRACE_EVENT(mm_page_alloc_extfrag, __entry->change_ownership) ); +/* + * Required for uniquely and securely identifying mm in rss_stat tracepoint. + */ +#ifndef __PTR_TO_HASHVAL +static unsigned int __maybe_unused mm_ptr_to_hash(const void *ptr) +{ + int ret; + unsigned long hashval; + + ret = ptr_to_hashval(ptr, &hashval); + if (ret) + return 0; + + /* The hashed value is only 32-bit */ + return (unsigned int)hashval; +} +#define __PTR_TO_HASHVAL +#endif + TRACE_EVENT(rss_stat, - TP_PROTO(int member, + TP_PROTO(struct mm_struct *mm, + int member, long count), - TP_ARGS(member, count), + TP_ARGS(mm, member, count), TP_STRUCT__entry( + __field(unsigned int, mm_id) + __field(unsigned int, curr) __field(int, member) __field(long, size) ), TP_fast_assign( + __entry->mm_id = mm_ptr_to_hash(mm); + __entry->curr = !!(current->mm == mm); __entry->member = member; __entry->size = (count << PAGE_SHIFT); ), - TP_printk("member=%d size=%ldB", + TP_printk("mm_id=%u curr=%d member=%d size=%ldB", + __entry->mm_id, + __entry->curr, __entry->member, __entry->size) ); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index dee8fc467fcf..7c488a1ce318 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -761,11 +761,38 @@ static int __init initialize_ptr_random(void) early_initcall(initialize_ptr_random); /* Maps a pointer to a 32 bit unique identifier. */ +static inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out) +{ + unsigned long hashval; + + if (static_branch_unlikely(¬_filled_random_ptr_key)) + return -EAGAIN; + +#ifdef CONFIG_64BIT + hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); + /* + * Mask off the first 32 bits, this makes explicit that we have + * modified the address (and 32 bits is plenty for a unique ID). + */ + hashval = hashval & 0xffffffff; +#else + hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key); +#endif + *hashval_out = hashval; + return 0; +} + +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) { const char *str = sizeof(ptr) == 8 ? "(____ptrval____)" : "(ptrval)"; unsigned long hashval; + int ret; /* When debugging early boot use non-cryptographically secure hash. */ if (unlikely(debug_boot_weak_hash)) { @@ -773,22 +800,13 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr, return pointer_string(buf, end, (const void *)hashval, spec); } - if (static_branch_unlikely(¬_filled_random_ptr_key)) { + 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); } -#ifdef CONFIG_64BIT - hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); - /* - * Mask off the first 32 bits, this makes explicit that we have - * modified the address (and 32 bits is plenty for a unique ID). - */ - hashval = hashval & 0xffffffff; -#else - hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key); -#endif return pointer_string(buf, end, (const void *)hashval, spec); } diff --git a/mm/memory.c b/mm/memory.c index 57c910aaba45..62b5cce653f6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -154,9 +154,9 @@ static int __init init_zero_pfn(void) } core_initcall(init_zero_pfn); -void mm_trace_rss_stat(int member, long count) +void mm_trace_rss_stat(struct mm_struct *mm, int member, long count) { - trace_rss_stat(member, count); + trace_rss_stat(mm, member, count); } #if defined(SPLIT_RSS_COUNTING) -- cgit v1.2.3