From: Lukas Funke <lukas.fu...@weidmueller.com> The commit enables vsprintf() to handle the '%*pb[l]' format specifier in order to print bitmaps and its derivatives such as cpumask and nodemask [1]. This can be used to derive kernel boot parameters from bitmaks such as 'isolcpu' or 'nohz_full' [2].
[1] https://www.kernel.org/doc/Documentation/printk-formats.txt [2] https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html Signed-off-by: Lukas Funke <lukas.fu...@weidmueller.com> --- (no changes since v1) lib/vsprintf.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e14c6ca9f9..abbd80ea9c 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -25,6 +25,7 @@ #include <linux/err.h> #include <linux/types.h> #include <linux/string.h> +#include <linux/bitmap.h> /* we use this so that we can do without the ctype library */ #define is_digit(c) ((c) >= '0' && (c) <= '9') @@ -390,6 +391,71 @@ static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, flags & ~SPECIAL); } +static char *bitmap_string(char *buf, char *end, const unsigned long *bitmap, + int field_width, int precision, int flags) +{ + const int CHUNKSIZE = 32; + int nr_bits = max_t(int, field_width, 0); + int i, chunksz; + int first = 1; + + chunksz = nr_bits & (CHUNKSIZE - 1); + if (chunksz == 0) + chunksz = CHUNKSIZE; + + i = ALIGN(nr_bits, CHUNKSIZE) - CHUNKSIZE; + for (; i >= 0; i -= CHUNKSIZE) { + u32 chunkmask, val; + int word, bit; + + chunkmask = ((1ULL << chunksz) - 1); + word = i / BITS_PER_LONG; + bit = i % BITS_PER_LONG; + val = (bitmap[word] >> bit) & chunkmask; + + if (!first) { + if (buf < end) + *buf = ','; + buf++; + } + first = 0; + + field_width = DIV_ROUND_UP(chunksz, 4); + buf = number(buf, end, val, 16, field_width, precision, + (SMALL | ZEROPAD)); + + chunksz = CHUNKSIZE; + } + return buf; +} + +static char *bitmap_list_string(char *buf, char *end, unsigned long *addr, + int field_width, int precision, int flags) +{ + int nr_bits = max_t(int, field_width, 0); + int first = 1; + int rbot, rtop; + + for_each_set_bitrange(rbot, rtop, addr, nr_bits) { + if (!first) { + if (buf < end) + *buf = ','; + buf++; + } + first = 0; + + buf = number(buf, end, rbot, 10, 0, -1, 0); + if (rtop == rbot + 1) + continue; + + if (buf < end) + *buf = '-'; + buf = number(++buf, end, rtop - 1, 10, 0, -1, 0); + } + + return buf; +} + #ifdef CONFIG_LIB_UUID /* * This works (roughly) the same way as Linux's. @@ -503,6 +569,15 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, precision, flags); flags &= ~SPECIAL; break; + case 'b': + switch (fmt[1]) { + case 'l': + return bitmap_list_string(buf, end, ptr, field_width, + precision, flags); + default: + return bitmap_string(buf, end, ptr, field_width, + precision, flags); + } #ifdef CONFIG_LIB_UUID case 'U': return uuid_string(buf, end, ptr, field_width, precision, -- 2.30.2