aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2015-02-13 17:36:53 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-14 00:21:36 -0500
commitdbc760bcc150cc27160f0131b15db76350df4334 (patch)
tree5cfa5aaa30e299e0286f81bc0dcdbe2582449ff3 /lib
parent513e3d2d11c9f05db1edc70deb18a82555cf9309 (diff)
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>
Diffstat (limited to 'lib')
-rw-r--r--lib/bitmap.c61
-rw-r--r--lib/vsprintf.c94
2 files changed, 96 insertions, 59 deletions
diff --git a/lib/bitmap.c b/lib/bitmap.c
index a13c7f4e325a..e85040ba1f22 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -383,28 +383,7 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off);
383int bitmap_scnprintf(char *buf, unsigned int buflen, 383int bitmap_scnprintf(char *buf, unsigned int buflen,
384 const unsigned long *maskp, int nmaskbits) 384 const unsigned long *maskp, int nmaskbits)
385{ 385{
386 int i, word, bit, len = 0; 386 return scnprintf(buf, buflen, "%*pb", nmaskbits, maskp);
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;
408} 387}
409EXPORT_SYMBOL(bitmap_scnprintf); 388EXPORT_SYMBOL(bitmap_scnprintf);
410 389
@@ -521,25 +500,6 @@ int bitmap_parse_user(const char __user *ubuf,
521} 500}
522EXPORT_SYMBOL(bitmap_parse_user); 501EXPORT_SYMBOL(bitmap_parse_user);
523 502
524/*
525 * bscnl_emit(buf, buflen, rbot, rtop, bp)
526 *
527 * Helper routine for bitmap_scnlistprintf(). Write decimal number
528 * or range to buf, suppressing output past buf+buflen, with optional
529 * comma-prefix. Return len of what was written to *buf, excluding the
530 * trailing \0.
531 */
532static inline int bscnl_emit(char *buf, int buflen, int rbot, int rtop, int len)
533{
534 if (len > 0)
535 len += scnprintf(buf + len, buflen - len, ",");
536 if (rbot == rtop)
537 len += scnprintf(buf + len, buflen - len, "%d", rbot);
538 else
539 len += scnprintf(buf + len, buflen - len, "%d-%d", rbot, rtop);
540 return len;
541}
542
543/** 503/**
544 * bitmap_scnlistprintf - convert bitmap to list format ASCII string 504 * bitmap_scnlistprintf - convert bitmap to list format ASCII string
545 * @buf: byte buffer into which string is placed 505 * @buf: byte buffer into which string is placed
@@ -559,24 +519,7 @@ static inline int bscnl_emit(char *buf, int buflen, int rbot, int rtop, int len)
559int bitmap_scnlistprintf(char *buf, unsigned int buflen, 519int bitmap_scnlistprintf(char *buf, unsigned int buflen,
560 const unsigned long *maskp, int nmaskbits) 520 const unsigned long *maskp, int nmaskbits)
561{ 521{
562 int len = 0; 522 return scnprintf(buf, buflen, "%*pbl", nmaskbits, maskp);
563 /* current bit is 'cur', most recently seen range is [rbot, rtop] */
564 int cur, rbot, rtop;
565
566 if (buflen == 0)
567 return 0;
568 buf[0] = 0;
569
570 rbot = cur = find_first_bit(maskp, nmaskbits);
571 while (cur < nmaskbits) {
572 rtop = cur;
573 cur = find_next_bit(maskp, nmaskbits, cur+1);
574 if (cur >= nmaskbits || cur > rtop + 1) {
575 len = bscnl_emit(buf, buflen, rbot, rtop, len);
576 rbot = cur;
577 }
578 }
579 return len;
580} 523}
581EXPORT_SYMBOL(bitmap_scnlistprintf); 524EXPORT_SYMBOL(bitmap_scnlistprintf);
582 525
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 602d2081e713..b235c96167d3 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -794,6 +794,87 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
794} 794}
795 795
796static noinline_for_stack 796static noinline_for_stack
797char *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
837static noinline_for_stack
838char *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
877static noinline_for_stack
797char *mac_address_string(char *buf, char *end, u8 *addr, 878char *mac_address_string(char *buf, char *end, u8 *addr,
798 struct printf_spec spec, const char *fmt) 879 struct printf_spec spec, const char *fmt)
799{ 880{
@@ -1258,6 +1339,10 @@ int kptr_restrict __read_mostly;
1258 * - 'B' For backtraced symbolic direct pointers with offset 1339 * - 'B' For backtraced symbolic direct pointers with offset
1259 * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref] 1340 * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]
1260 * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201] 1341 * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201]
1342 * - 'b[l]' For a bitmap, the number of bits is determined by the field
1343 * width which must be explicitly specified either as part of the
1344 * format string '%32b[l]' or through '%*b[l]', [l] selects
1345 * range-list format instead of hex format
1261 * - 'M' For a 6-byte MAC address, it prints the address in the 1346 * - 'M' For a 6-byte MAC address, it prints the address in the
1262 * usual colon-separated hex notation 1347 * usual colon-separated hex notation
1263 * - 'm' For a 6-byte MAC address, it prints the hex address without colons 1348 * - 'm' For a 6-byte MAC address, it prints the hex address without colons
@@ -1354,6 +1439,13 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
1354 return resource_string(buf, end, ptr, spec, fmt); 1439 return resource_string(buf, end, ptr, spec, fmt);
1355 case 'h': 1440 case 'h':
1356 return hex_string(buf, end, ptr, spec, fmt); 1441 return hex_string(buf, end, ptr, spec, fmt);
1442 case 'b':
1443 switch (fmt[1]) {
1444 case 'l':
1445 return bitmap_list_string(buf, end, ptr, spec, fmt);
1446 default:
1447 return bitmap_string(buf, end, ptr, spec, fmt);
1448 }
1357 case 'M': /* Colon separated: 00:01:02:03:04:05 */ 1449 case 'M': /* Colon separated: 00:01:02:03:04:05 */
1358 case 'm': /* Contiguous: 000102030405 */ 1450 case 'm': /* Contiguous: 000102030405 */
1359 /* [mM]F (FDDI) */ 1451 /* [mM]F (FDDI) */
@@ -1689,6 +1781,8 @@ qualifier:
1689 * %pB output the name of a backtrace symbol with its offset 1781 * %pB output the name of a backtrace symbol with its offset
1690 * %pR output the address range in a struct resource with decoded flags 1782 * %pR output the address range in a struct resource with decoded flags
1691 * %pr output the address range in a struct resource with raw flags 1783 * %pr output the address range in a struct resource with raw flags
1784 * %pb output the bitmap with field width as the number of bits
1785 * %pbl output the bitmap as range list with field width as the number of bits
1692 * %pM output a 6-byte MAC address with colons 1786 * %pM output a 6-byte MAC address with colons
1693 * %pMR output a 6-byte MAC address with colons in reversed order 1787 * %pMR output a 6-byte MAC address with colons in reversed order
1694 * %pMF output a 6-byte MAC address with dashes 1788 * %pMF output a 6-byte MAC address with dashes