Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib/vsprintf: implement bitmap printing through '%*pb[l]'

bitmap and its derivatives such as cpumask and nodemask currently only
provide formatting functions which put the output string into the
provided buffer; however, how long this buffer should be isn't defined
anywhere and given that some of these bitmaps can be too large to be
formatted into an on-stack buffer it users sometimes are unnecessarily
forced to come up with creative solutions and compromises for the
buffer just to printk these bitmaps.

There have been a couple different attempts at making this easier.

1. Way back, PeterZ tried printk '%pb' extension with the precision
for bit width - '%.*pb'. This was intuitive and made sense but
unfortunately triggered a compile warning about using precision
for a pointer.

http://lkml.kernel.org/g/1336577562.2527.58.camel@twins

2. I implemented bitmap_pr_cont[_list]() and its wrappers for cpumask
and nodemask. This works but PeterZ pointed out that pr_cont's
tendency to produce broken lines when multiple CPUs are printing is
bothering considering the usages.

http://lkml.kernel.org/g/1418226774-30215-3-git-send-email-tj@kernel.org

So, this patch is another attempt at teaching printk and friends how
to print bitmaps. It's almost identical to what PeterZ tried with
precision but it uses the field width for the number of bits instead
of precision. The format used is '%*pb[l]', with the optional
trailing 'l' specifying list format instead of hex masks.

This is a valid format string and doesn't trigger compiler warnings;
however, it does make it impossible to specify output field width when
printing bitmaps. I think this is an acceptable trade-off given how
much easier it makes printing bitmaps and that we don't have any
in-kernel user which is using the field width specification. If any
future user wants to use field width with a bitmap, it'd have to
format the bitmap into a string buffer and then print that buffer with
width spec, which isn't different from how it should be done now.

This patch implements bitmap[_list]_string() which are called from the
vsprintf pointer() formatting function. The implementation is mostly
identical to bitmap_scn[list]printf() except that the output is
performed in the vsprintf way. These functions handle formatting into
too small buffers and sprintf() family of functions report the correct
overrun output length.

bitmap_scn[list]printf() are now thin wrappers around scnprintf().

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
Cc: "John W. Linville" <linville@tuxdriver.com>
Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Chris Zankel <chris@zankel.net>
Cc: Christoph Lameter <cl@linux.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Mike Travis <travis@sgi.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tony Luck <tony.luck@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Tejun Heo and committed by
Linus Torvalds
dbc760bc 513e3d2d

+96 -59
+2 -59
lib/bitmap.c
··· 383 383 int bitmap_scnprintf(char *buf, unsigned int buflen, 384 384 const unsigned long *maskp, int nmaskbits) 385 385 { 386 - int i, word, bit, len = 0; 387 - unsigned long val; 388 - const char *sep = ""; 389 - int chunksz; 390 - u32 chunkmask; 391 - 392 - chunksz = nmaskbits & (CHUNKSZ - 1); 393 - if (chunksz == 0) 394 - chunksz = CHUNKSZ; 395 - 396 - i = ALIGN(nmaskbits, CHUNKSZ) - CHUNKSZ; 397 - for (; i >= 0; i -= CHUNKSZ) { 398 - chunkmask = ((1ULL << chunksz) - 1); 399 - word = i / BITS_PER_LONG; 400 - bit = i % BITS_PER_LONG; 401 - val = (maskp[word] >> bit) & chunkmask; 402 - len += scnprintf(buf+len, buflen-len, "%s%0*lx", sep, 403 - (chunksz+3)/4, val); 404 - chunksz = CHUNKSZ; 405 - sep = ","; 406 - } 407 - return len; 386 + return scnprintf(buf, buflen, "%*pb", nmaskbits, maskp); 408 387 } 409 388 EXPORT_SYMBOL(bitmap_scnprintf); 410 389 ··· 500 521 } 501 522 EXPORT_SYMBOL(bitmap_parse_user); 502 523 503 - /* 504 - * bscnl_emit(buf, buflen, rbot, rtop, bp) 505 - * 506 - * Helper routine for bitmap_scnlistprintf(). Write decimal number 507 - * or range to buf, suppressing output past buf+buflen, with optional 508 - * comma-prefix. Return len of what was written to *buf, excluding the 509 - * trailing \0. 510 - */ 511 - static inline int bscnl_emit(char *buf, int buflen, int rbot, int rtop, int len) 512 - { 513 - if (len > 0) 514 - len += scnprintf(buf + len, buflen - len, ","); 515 - if (rbot == rtop) 516 - len += scnprintf(buf + len, buflen - len, "%d", rbot); 517 - else 518 - len += scnprintf(buf + len, buflen - len, "%d-%d", rbot, rtop); 519 - return len; 520 - } 521 - 522 524 /** 523 525 * bitmap_scnlistprintf - convert bitmap to list format ASCII string 524 526 * @buf: byte buffer into which string is placed ··· 519 559 int bitmap_scnlistprintf(char *buf, unsigned int buflen, 520 560 const unsigned long *maskp, int nmaskbits) 521 561 { 522 - int len = 0; 523 - /* current bit is 'cur', most recently seen range is [rbot, rtop] */ 524 - int cur, rbot, rtop; 525 - 526 - if (buflen == 0) 527 - return 0; 528 - buf[0] = 0; 529 - 530 - rbot = cur = find_first_bit(maskp, nmaskbits); 531 - while (cur < nmaskbits) { 532 - rtop = cur; 533 - cur = find_next_bit(maskp, nmaskbits, cur+1); 534 - if (cur >= nmaskbits || cur > rtop + 1) { 535 - len = bscnl_emit(buf, buflen, rbot, rtop, len); 536 - rbot = cur; 537 - } 538 - } 539 - return len; 562 + return scnprintf(buf, buflen, "%*pbl", nmaskbits, maskp); 540 563 } 541 564 EXPORT_SYMBOL(bitmap_scnlistprintf); 542 565
+94
lib/vsprintf.c
··· 794 794 } 795 795 796 796 static noinline_for_stack 797 + char *bitmap_string(char *buf, char *end, unsigned long *bitmap, 798 + struct printf_spec spec, const char *fmt) 799 + { 800 + const int CHUNKSZ = 32; 801 + int nr_bits = max_t(int, spec.field_width, 0); 802 + int i, chunksz; 803 + bool first = true; 804 + 805 + /* reused to print numbers */ 806 + spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 }; 807 + 808 + chunksz = nr_bits & (CHUNKSZ - 1); 809 + if (chunksz == 0) 810 + chunksz = CHUNKSZ; 811 + 812 + i = ALIGN(nr_bits, CHUNKSZ) - CHUNKSZ; 813 + for (; i >= 0; i -= CHUNKSZ) { 814 + u32 chunkmask, val; 815 + int word, bit; 816 + 817 + chunkmask = ((1ULL << chunksz) - 1); 818 + word = i / BITS_PER_LONG; 819 + bit = i % BITS_PER_LONG; 820 + val = (bitmap[word] >> bit) & chunkmask; 821 + 822 + if (!first) { 823 + if (buf < end) 824 + *buf = ','; 825 + buf++; 826 + } 827 + first = false; 828 + 829 + spec.field_width = DIV_ROUND_UP(chunksz, 4); 830 + buf = number(buf, end, val, spec); 831 + 832 + chunksz = CHUNKSZ; 833 + } 834 + return buf; 835 + } 836 + 837 + static noinline_for_stack 838 + char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap, 839 + struct printf_spec spec, const char *fmt) 840 + { 841 + int nr_bits = max_t(int, spec.field_width, 0); 842 + /* current bit is 'cur', most recently seen range is [rbot, rtop] */ 843 + int cur, rbot, rtop; 844 + bool first = true; 845 + 846 + /* reused to print numbers */ 847 + spec = (struct printf_spec){ .base = 10 }; 848 + 849 + rbot = cur = find_first_bit(bitmap, nr_bits); 850 + while (cur < nr_bits) { 851 + rtop = cur; 852 + cur = find_next_bit(bitmap, nr_bits, cur + 1); 853 + if (cur < nr_bits && cur <= rtop + 1) 854 + continue; 855 + 856 + if (!first) { 857 + if (buf < end) 858 + *buf = ','; 859 + buf++; 860 + } 861 + first = false; 862 + 863 + buf = number(buf, end, rbot, spec); 864 + if (rbot < rtop) { 865 + if (buf < end) 866 + *buf = '-'; 867 + buf++; 868 + 869 + buf = number(buf, end, rtop, spec); 870 + } 871 + 872 + rbot = cur; 873 + } 874 + return buf; 875 + } 876 + 877 + static noinline_for_stack 797 878 char *mac_address_string(char *buf, char *end, u8 *addr, 798 879 struct printf_spec spec, const char *fmt) 799 880 { ··· 1339 1258 * - 'B' For backtraced symbolic direct pointers with offset 1340 1259 * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref] 1341 1260 * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201] 1261 + * - 'b[l]' For a bitmap, the number of bits is determined by the field 1262 + * width which must be explicitly specified either as part of the 1263 + * format string '%32b[l]' or through '%*b[l]', [l] selects 1264 + * range-list format instead of hex format 1342 1265 * - 'M' For a 6-byte MAC address, it prints the address in the 1343 1266 * usual colon-separated hex notation 1344 1267 * - 'm' For a 6-byte MAC address, it prints the hex address without colons ··· 1439 1354 return resource_string(buf, end, ptr, spec, fmt); 1440 1355 case 'h': 1441 1356 return hex_string(buf, end, ptr, spec, fmt); 1357 + case 'b': 1358 + switch (fmt[1]) { 1359 + case 'l': 1360 + return bitmap_list_string(buf, end, ptr, spec, fmt); 1361 + default: 1362 + return bitmap_string(buf, end, ptr, spec, fmt); 1363 + } 1442 1364 case 'M': /* Colon separated: 00:01:02:03:04:05 */ 1443 1365 case 'm': /* Contiguous: 000102030405 */ 1444 1366 /* [mM]F (FDDI) */ ··· 1781 1689 * %pB output the name of a backtrace symbol with its offset 1782 1690 * %pR output the address range in a struct resource with decoded flags 1783 1691 * %pr output the address range in a struct resource with raw flags 1692 + * %pb output the bitmap with field width as the number of bits 1693 + * %pbl output the bitmap as range list with field width as the number of bits 1784 1694 * %pM output a 6-byte MAC address with colons 1785 1695 * %pMR output a 6-byte MAC address with colons in reversed order 1786 1696 * %pMF output a 6-byte MAC address with dashes