Re: [PATCH 4/5] lib: vsprintf: enable '%*pb[l]' format specifier

2023-12-11 Thread Heinrich Schuchardt

On 11.12.23 13:20, lukas.funke-...@weidmueller.com wrote:

From: Lukas Funke 

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 


Please, add a change for doc/develop/printf.rst to the patch describing
the new format.

Best regards

Heinrich


---

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

  /* 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,




[PATCH 4/5] lib: vsprintf: enable '%*pb[l]' format specifier

2023-12-11 Thread lukas . funke-oss
From: Lukas Funke 

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

 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 
 #include 
 #include 
+#include 
 
 /* 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