Commit 0a5ce083 authored by Yury Norov's avatar Yury Norov Committed by Linus Torvalds

lib/bitmap.c: make bitmap_parselist() thread-safe and much faster

Current implementation of bitmap_parselist() uses a static variable to
save local state while setting bits in the bitmap.  It is obviously wrong
if we assume execution in multiprocessor environment.  Fortunately, it's
possible to rewrite this portion of code to avoid using the static
variable.

It is also possible to set bits in the mask per-range with bitmap_set(),
not per-bit, as it is implemented now, with set_bit(); which is way
faster.

The important side effect of this change is that setting bits in this
function from now is not per-bit atomic and less memory-ordered.  This is
because set_bit() guarantees the order of memory accesses, while
bitmap_set() does not.  I think that it is the advantage of the new
approach, because the bitmap_parselist() is intended to initialise bit
arrays, and user should protect the whole bitmap during initialisation if
needed.  So protecting individual bits looks expensive and useless.  Also,
other range-oriented functions in lib/bitmap.c don't worry much about
atomicity.

With all that, setting 2k bits in map with the pattern like 0-2047:128/256
becomes ~50 times faster after applying the patch in my testing
environment (arm64 hosted on qemu).

The second patch of the series adds the test for bitmap_parselist().  It's
not intended to cover all tricky cases, just to make sure that I didn't
screw up during rework.

Link: http://lkml.kernel.org/r/20170807225438.16161-1-ynorov@caviumnetworks.comSigned-off-by: default avatarYury Norov <ynorov@caviumnetworks.com>
Cc: Noam Camus <noamca@mellanox.com>
Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Cc: Matthew Wilcox <mawilcox@microsoft.com>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e4dace36
...@@ -513,7 +513,7 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen, ...@@ -513,7 +513,7 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen,
int nmaskbits) int nmaskbits)
{ {
unsigned int a, b, old_a, old_b; unsigned int a, b, old_a, old_b;
unsigned int group_size, used_size; unsigned int group_size, used_size, off;
int c, old_c, totaldigits, ndigits; int c, old_c, totaldigits, ndigits;
const char __user __force *ubuf = (const char __user __force *)buf; const char __user __force *ubuf = (const char __user __force *)buf;
int at_start, in_range, in_partial_range; int at_start, in_range, in_partial_range;
...@@ -599,6 +599,8 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen, ...@@ -599,6 +599,8 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen,
a = old_a; a = old_a;
b = old_b; b = old_b;
old_a = old_b = 0; old_a = old_b = 0;
} else {
used_size = group_size = b - a + 1;
} }
/* if no digit is after '-', it's wrong*/ /* if no digit is after '-', it's wrong*/
if (at_start && in_range) if (at_start && in_range)
...@@ -608,17 +610,9 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen, ...@@ -608,17 +610,9 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen,
if (b >= nmaskbits) if (b >= nmaskbits)
return -ERANGE; return -ERANGE;
while (a <= b) { while (a <= b) {
if (in_partial_range) { off = min(b - a + 1, used_size);
static int pos_in_group = 1; bitmap_set(maskp, a, off);
a += group_size;
if (pos_in_group <= used_size)
set_bit(a, maskp);
if (a == b || ++pos_in_group > group_size)
pos_in_group = 1;
} else
set_bit(a, maskp);
a++;
} }
} while (buflen && c == ','); } while (buflen && c == ',');
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment