Re: [PATCH 5/7] lib: rework bitmap_parse()

2019-09-09 Thread Andy Shevchenko
On Sun, Sep 08, 2019 at 08:30:19PM -0700, Yury Norov wrote:
> bitmap_parse() is ineffective and full of opaque variables and opencoded
> parts. It leads to hard understanding and usage of it. This rework
> includes:
>  - remove bitmap_shift_left() call from the cycle. Now it makes the
>complexity of the algorithm as O(nbits^2). In the suggested approach
>the input string is parsed in reverse direction, so no shifts needed;
>  - relax requirement on a single comma and no white spaces between chunks.
>It is considered useful in scripting, and it aligns with
>bitmap_parselist();
>  - split bitmap_parse() to small readable helpers;
>  - make an explicit calculation of the end of input line at the
>beginning, so users of the bitmap_parse() won't bother doing this.

> +static const char *bitmap_get_x32_reverse(const char *start,
> + const char *end, u32 *num)
> +{
> + u32 ret = 0;
> + int c, i;
> +

> + if (!isxdigit(*end))
> + return ERR_PTR(-EINVAL);

This seems redundant...

> +
> + for (i = 0; i < 32; i += 4) {

> + c = hex_to_bin(*end--);
> + if (c < 0)
> + return ERR_PTR(-EINVAL);

...because this will do the same check.

Am I right?

> +
> + ret |= c << i;
> +
> + if (start > end || __end_of_region(*end))
> + goto out;
> + }
> +

> + if (isxdigit(*end))
> + return ERR_PTR(-EOVERFLOW);

hex_to_bin() doesn't rely on ctype array, won't drain caches.
I guess it's not a fast path, so, either will work.

> +out:
> + *num = ret;
> + return end;
> +}

-- 
With Best Regards,
Andy Shevchenko




[PATCH 5/7] lib: rework bitmap_parse()

2019-09-08 Thread Yury Norov
bitmap_parse() is ineffective and full of opaque variables and opencoded
parts. It leads to hard understanding and usage of it. This rework
includes:
 - remove bitmap_shift_left() call from the cycle. Now it makes the
   complexity of the algorithm as O(nbits^2). In the suggested approach
   the input string is parsed in reverse direction, so no shifts needed;
 - relax requirement on a single comma and no white spaces between chunks.
   It is considered useful in scripting, and it aligns with
   bitmap_parselist();
 - split bitmap_parse() to small readable helpers;
 - make an explicit calculation of the end of input line at the
   beginning, so users of the bitmap_parse() won't bother doing this.

Signed-off-by: Yury Norov 
---
 include/linux/bitmap.h |   8 +-
 lib/bitmap.c   | 178 -
 2 files changed, 87 insertions(+), 99 deletions(-)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 90528f12bdfa6..bad43c4a5eaca 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -176,7 +176,7 @@ bitmap_find_next_zero_area(unsigned long *map,
  align_mask, 0);
 }
 
-extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,
+extern int bitmap_parse(const char *buf, unsigned int buflen,
unsigned long *dst, int nbits);
 extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
unsigned long *dst, int nbits);
@@ -431,12 +431,6 @@ static inline void bitmap_shift_left(unsigned long *dst, 
const unsigned long *sr
__bitmap_shift_left(dst, src, shift, nbits);
 }
 
-static inline int bitmap_parse(const char *buf, unsigned int buflen,
-   unsigned long *maskp, int nmaskbits)
-{
-   return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits);
-}
-
 /**
  * BITMAP_FROM_U64() - Represent u64 value in the format suitable for bitmap.
  * @n: u64 value
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 51ee14577166e..008f53714950a 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -353,97 +353,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off);
  * second version by Paul Jackson, third by Joe Korty.
  */
 
-#define CHUNKSZ32
-#define nbits_to_hold_value(val)   fls(val)
-#define BASEDEC 10 /* fancier cpuset lists input in decimal */
-
-/**
- * __bitmap_parse - convert an ASCII hex string into a bitmap.
- * @buf: pointer to buffer containing string.
- * @buflen: buffer size in bytes.  If string is smaller than this
- *then it must be terminated with a \0.
- * @is_user: location of buffer, 0 indicates kernel space
- * @maskp: pointer to bitmap array that will contain result.
- * @nmaskbits: size of bitmap, in bits.
- *
- * Commas group hex digits into chunks.  Each chunk defines exactly 32
- * bits of the resultant bitmask.  No chunk may specify a value larger
- * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
- * then leading 0-bits are prepended.  %-EINVAL is returned for illegal
- * characters and for grouping errors such as "1,,5", ",44", "," and "".
- * Leading and trailing whitespace accepted, but not embedded whitespace.
- */
-int __bitmap_parse(const char *buf, unsigned int buflen,
-   int is_user, unsigned long *maskp,
-   int nmaskbits)
-{
-   int c, old_c, totaldigits, ndigits, nchunks, nbits;
-   u32 chunk;
-   const char __user __force *ubuf = (const char __user __force *)buf;
-
-   bitmap_zero(maskp, nmaskbits);
-
-   nchunks = nbits = totaldigits = c = 0;
-   do {
-   chunk = 0;
-   ndigits = totaldigits;
-
-   /* Get the next chunk of the bitmap */
-   while (buflen) {
-   old_c = c;
-   if (is_user) {
-   if (__get_user(c, ubuf++))
-   return -EFAULT;
-   }
-   else
-   c = *buf++;
-   buflen--;
-   if (isspace(c))
-   continue;
-
-   /*
-* If the last character was a space and the current
-* character isn't '\0', we've got embedded whitespace.
-* This is a no-no, so throw an error.
-*/
-   if (totaldigits && c && isspace(old_c))
-   return -EINVAL;
-
-   /* A '\0' or a ',' signal the end of the chunk */
-   if (c == '\0' || c == ',')
-   break;
-
-   if (!isxdigit(c))
-   return -EINVAL;
-
-   /*
-* Make sure there are at least 4 free bits in 'chunk'.
-

Re: [PATCH 5/7] lib: rework bitmap_parse()

2019-07-22 Thread Andy Shevchenko
On Sun, Jul 21, 2019 at 02:27:51PM -0700, Yury Norov wrote:
> From Yury Norov 
> 
> bitmap_parse() is ineffective and full of opaque variables and opencoded
> parts. It leads to hard understanding and usage of it. This rework
> includes:
>  - remove bitmap_shift_left() call from the cycle. Now it makes the
>complexity of the algorithm as O(nbits^2). In the suggested approach
>the input string is parsed in reverse direction, so no shifts needed;
>  - relax requirement on a single comma and no white spaces between chunks.
>It is considered useful in scripting, and it aligns with
>bitmap_parselist();
>  - split bitmap_parse() to small readable helpers;
>  - make an explicit calculation of the end of input line at the
>beginning, so users of the bitmap_parse() won't bother doing this.

> Signed-off-by: Yury Norov 
> Signed-off-by: Yury Norov 

I'm not sure this is good to have two SoB tags for one person.

> + if (hex_to_bin(*end) < 0)

Isn't it simple isxdigit() check?

> + return ERR_PTR(-EINVAL);
> +

> + for (i = 0; i < 32; i += 4) {
> + c = hex_to_bin(*end--);
> + if (c < 0)
> + return ERR_PTR(-EINVAL);
> +
> + ret |= c << i;

Dunno if it's for this series, but perhaps we would like to have something like

/* Convert hex representation of u32 value to binary format */
static inline int hex2bin32(const char *buf, u32 *result)
{
u8 value[4];
int ret;

ret = hex2bin(value, buf, sizeof(u32));
if (ret)
return ret;

*result = get_unaligned((u32 *)value);
// I guess it's aligned and thus can be done with:
//   *result = be32_to_cpup((__force __be32 *)value);
// just don't like enforcing bitwise types
return 0;
}

// also can be your variant with for-loop.

At least here for now and then can be moved to the kernel.h / hexdump.c.

> +
> + if (start > end || __end_of_region(*end))
> + goto out;
> + }
> +

> + if (hex_to_bin(*end) >= 0)

Isn't it simple isxdigit() check?

> + return ERR_PTR(-EOVERFLOW);

-- 
With Best Regards,
Andy Shevchenko




[PATCH 5/7] lib: rework bitmap_parse()

2019-07-21 Thread Yury Norov
>From Yury Norov 

bitmap_parse() is ineffective and full of opaque variables and opencoded
parts. It leads to hard understanding and usage of it. This rework
includes:
 - remove bitmap_shift_left() call from the cycle. Now it makes the
   complexity of the algorithm as O(nbits^2). In the suggested approach
   the input string is parsed in reverse direction, so no shifts needed;
 - relax requirement on a single comma and no white spaces between chunks.
   It is considered useful in scripting, and it aligns with
   bitmap_parselist();
 - split bitmap_parse() to small readable helpers;
 - make an explicit calculation of the end of input line at the
   beginning, so users of the bitmap_parse() won't bother doing this.

Signed-off-by: Yury Norov 
Signed-off-by: Yury Norov 
---
 include/linux/bitmap.h |   8 +-
 lib/bitmap.c   | 178 -
 2 files changed, 87 insertions(+), 99 deletions(-)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index f58e97446abcf..c3e84864cc335 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -172,7 +172,7 @@ bitmap_find_next_zero_area(unsigned long *map,
  align_mask, 0);
 }
 
-extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,
+extern int bitmap_parse(const char *buf, unsigned int buflen,
unsigned long *dst, int nbits);
 extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
unsigned long *dst, int nbits);
@@ -408,12 +408,6 @@ static inline void bitmap_shift_left(unsigned long *dst, 
const unsigned long *sr
__bitmap_shift_left(dst, src, shift, nbits);
 }
 
-static inline int bitmap_parse(const char *buf, unsigned int buflen,
-   unsigned long *maskp, int nmaskbits)
-{
-   return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits);
-}
-
 /**
  * BITMAP_FROM_U64() - Represent u64 value in the format suitable for bitmap.
  * @n: u64 value
diff --git a/lib/bitmap.c b/lib/bitmap.c
index fbd4121c540b8..8fc918e74116d 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -333,97 +333,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off);
  * second version by Paul Jackson, third by Joe Korty.
  */
 
-#define CHUNKSZ32
-#define nbits_to_hold_value(val)   fls(val)
-#define BASEDEC 10 /* fancier cpuset lists input in decimal */
-
-/**
- * __bitmap_parse - convert an ASCII hex string into a bitmap.
- * @buf: pointer to buffer containing string.
- * @buflen: buffer size in bytes.  If string is smaller than this
- *then it must be terminated with a \0.
- * @is_user: location of buffer, 0 indicates kernel space
- * @maskp: pointer to bitmap array that will contain result.
- * @nmaskbits: size of bitmap, in bits.
- *
- * Commas group hex digits into chunks.  Each chunk defines exactly 32
- * bits of the resultant bitmask.  No chunk may specify a value larger
- * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
- * then leading 0-bits are prepended.  %-EINVAL is returned for illegal
- * characters and for grouping errors such as "1,,5", ",44", "," and "".
- * Leading and trailing whitespace accepted, but not embedded whitespace.
- */
-int __bitmap_parse(const char *buf, unsigned int buflen,
-   int is_user, unsigned long *maskp,
-   int nmaskbits)
-{
-   int c, old_c, totaldigits, ndigits, nchunks, nbits;
-   u32 chunk;
-   const char __user __force *ubuf = (const char __user __force *)buf;
-
-   bitmap_zero(maskp, nmaskbits);
-
-   nchunks = nbits = totaldigits = c = 0;
-   do {
-   chunk = 0;
-   ndigits = totaldigits;
-
-   /* Get the next chunk of the bitmap */
-   while (buflen) {
-   old_c = c;
-   if (is_user) {
-   if (__get_user(c, ubuf++))
-   return -EFAULT;
-   }
-   else
-   c = *buf++;
-   buflen--;
-   if (isspace(c))
-   continue;
-
-   /*
-* If the last character was a space and the current
-* character isn't '\0', we've got embedded whitespace.
-* This is a no-no, so throw an error.
-*/
-   if (totaldigits && c && isspace(old_c))
-   return -EINVAL;
-
-   /* A '\0' or a ',' signal the end of the chunk */
-   if (c == '\0' || c == ',')
-   break;
-
-   if (!isxdigit(c))
-   return -EINVAL;
-
-   /*
-* Make sure there are at least

Re: [PATCH 5/7] lib: rework bitmap_parse()

2019-05-23 Thread Andrew Morton
On Thu, 9 May 2019 19:26:33 -0700 Yury Norov  wrote:

> Hi Andy,
> 
> Thanks for thorough review.
> 
> On Wed, May 08, 2019 at 11:46:32AM +0300, Andy Shevchenko wrote:
> > On Tue, Apr 30, 2019 at 06:06:34PM -0700, Yury Norov wrote:
> > > bitmap_parse() is ineffective and full of opaque variables and opencoded
> > > parts. It leads to hard understanding and usage of it. This rework
> > > includes:
> > >  - remove bitmap_shift_left() call from the cycle. Now it makes the
> > >complexity of the algorithm as O(nbits^2). In the suggested approach
> > >the input string is parsed in reverse direction, so no shifts needed;
> > >  - relax requirement on a single comma and no white spaces between chunks.
> > >It is considered useful in scripting, and it aligns with
> > >bitmap_parselist();
> > >  - split bitmap_parse() to small readable helpers;
> > >  - make an explicit calculation of the end of input line at the
> > >beginning, so users of the bitmap_parse() won't bother doing this.
> > 
> > > +static inline bool in_str(const char *start, const char *ptr)
> > > +{
> > > + return start <= ptr;
> > > +}
> > > +
> > 
> > The explicit use of the conditional is better.
> > 
> > -- 
> > With Best Regards,
> > Andy Shevchenko
> 
> I still think that is_str() is more verbose, but it's minor issue
> anyways, so I obey. Below is the patch that removes the function.
> It's up to Andrew finally, either apply it or not.

I agree with Andy - open-coding the comparisons makes it easier to
understand the varoius in_str() callsites, IMO.

> @@ -653,7 +648,7 @@ int bitmap_parse(const char *start, unsigned int buflen,
>   u32 *bitmap = (u32 *)maskp;
>   int unset_bit;
>  
> - while (in_str(start, (end = bitmap_find_region_reverse(start, end {
> + while (start <= (end = bitmap_find_region_reverse(start, end))) {

This statement hurts my little brain.  Can it be broken into easier to digest
chunks?

>   if (!chunks--)
>   return -EOVERFLOW;
>  


Re: [PATCH 5/7] lib: rework bitmap_parse()

2019-05-09 Thread Yury Norov
Hi Andy,

Thanks for thorough review.

On Wed, May 08, 2019 at 11:46:32AM +0300, Andy Shevchenko wrote:
> On Tue, Apr 30, 2019 at 06:06:34PM -0700, Yury Norov wrote:
> > bitmap_parse() is ineffective and full of opaque variables and opencoded
> > parts. It leads to hard understanding and usage of it. This rework
> > includes:
> >  - remove bitmap_shift_left() call from the cycle. Now it makes the
> >complexity of the algorithm as O(nbits^2). In the suggested approach
> >the input string is parsed in reverse direction, so no shifts needed;
> >  - relax requirement on a single comma and no white spaces between chunks.
> >It is considered useful in scripting, and it aligns with
> >bitmap_parselist();
> >  - split bitmap_parse() to small readable helpers;
> >  - make an explicit calculation of the end of input line at the
> >beginning, so users of the bitmap_parse() won't bother doing this.
> 
> > +static inline bool in_str(const char *start, const char *ptr)
> > +{
> > +   return start <= ptr;
> > +}
> > +
> 
> The explicit use of the conditional is better.
> 
> -- 
> With Best Regards,
> Andy Shevchenko

I still think that is_str() is more verbose, but it's minor issue
anyways, so I obey. Below is the patch that removes the function.
It's up to Andrew finally, either apply it or not.

Thanks,
Yury

>From 7438c15a0b165032a3e5a6d87daabe877dc8cbc8 Mon Sep 17 00:00:00 2001
From: Yury Norov 
Date: Thu, 9 May 2019 17:54:23 -0700
Subject: [PATCH] lib: opencode in_str()

Signed-off-by: Yury Norov 
---
 lib/bitmap.c | 11 +++
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/lib/bitmap.c b/lib/bitmap.c
index ebcf4700ebed..ecf93d2982a5 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -454,11 +454,6 @@ static inline bool end_of_region(char c)
return __end_of_region(c) || end_of_str(c);
 }
 
-static inline bool in_str(const char *start, const char *ptr)
-{
-   return start <= ptr;
-}
-
 /*
  * The format allows commas and whitespases at the beginning
  * of the region.
@@ -473,7 +468,7 @@ static const char *bitmap_find_region(const char *str)
 
 static const char *bitmap_find_region_reverse(const char *start, const char 
*end)
 {
-   while (in_str(start, end) && __end_of_region(*end))
+   while (start <= end && __end_of_region(*end))
end--;
 
return end;
@@ -618,7 +613,7 @@ static const char *bitmap_get_x32_reverse(const char *start,
 
ret |= c << i;
 
-   if (!in_str(start, end) || __end_of_region(*end))
+   if (start > end || __end_of_region(*end))
goto out;
}
 
@@ -653,7 +648,7 @@ int bitmap_parse(const char *start, unsigned int buflen,
u32 *bitmap = (u32 *)maskp;
int unset_bit;
 
-   while (in_str(start, (end = bitmap_find_region_reverse(start, end {
+   while (start <= (end = bitmap_find_region_reverse(start, end))) {
if (!chunks--)
return -EOVERFLOW;
 
-- 
2.17.1



Re: [PATCH 5/7] lib: rework bitmap_parse()

2019-05-08 Thread Andy Shevchenko
On Tue, Apr 30, 2019 at 06:06:34PM -0700, Yury Norov wrote:
> bitmap_parse() is ineffective and full of opaque variables and opencoded
> parts. It leads to hard understanding and usage of it. This rework
> includes:
>  - remove bitmap_shift_left() call from the cycle. Now it makes the
>complexity of the algorithm as O(nbits^2). In the suggested approach
>the input string is parsed in reverse direction, so no shifts needed;
>  - relax requirement on a single comma and no white spaces between chunks.
>It is considered useful in scripting, and it aligns with
>bitmap_parselist();
>  - split bitmap_parse() to small readable helpers;
>  - make an explicit calculation of the end of input line at the
>beginning, so users of the bitmap_parse() won't bother doing this.

> +static inline bool in_str(const char *start, const char *ptr)
> +{
> + return start <= ptr;
> +}
> +

The explicit use of the conditional is better.

-- 
With Best Regards,
Andy Shevchenko




[PATCH 5/7] lib: rework bitmap_parse()

2019-04-30 Thread Yury Norov
bitmap_parse() is ineffective and full of opaque variables and opencoded
parts. It leads to hard understanding and usage of it. This rework
includes:
 - remove bitmap_shift_left() call from the cycle. Now it makes the
   complexity of the algorithm as O(nbits^2). In the suggested approach
   the input string is parsed in reverse direction, so no shifts needed;
 - relax requirement on a single comma and no white spaces between chunks.
   It is considered useful in scripting, and it aligns with
   bitmap_parselist();
 - split bitmap_parse() to small readable helpers;
 - make an explicit calculation of the end of input line at the
   beginning, so users of the bitmap_parse() won't bother doing this.

Signed-off-by: Yury Norov 
---
 include/linux/bitmap.h |   8 +-
 lib/bitmap.c   | 179 -
 2 files changed, 88 insertions(+), 99 deletions(-)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index f58e97446abc..c3e84864cc33 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -172,7 +172,7 @@ bitmap_find_next_zero_area(unsigned long *map,
  align_mask, 0);
 }
 
-extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user,
+extern int bitmap_parse(const char *buf, unsigned int buflen,
unsigned long *dst, int nbits);
 extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
unsigned long *dst, int nbits);
@@ -408,12 +408,6 @@ static inline void bitmap_shift_left(unsigned long *dst, 
const unsigned long *sr
__bitmap_shift_left(dst, src, shift, nbits);
 }
 
-static inline int bitmap_parse(const char *buf, unsigned int buflen,
-   unsigned long *maskp, int nmaskbits)
-{
-   return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits);
-}
-
 /**
  * BITMAP_FROM_U64() - Represent u64 value in the format suitable for bitmap.
  * @n: u64 value
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 300732031fad..ebcf4700ebed 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -335,97 +335,6 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area_off);
  * second version by Paul Jackson, third by Joe Korty.
  */
 
-#define CHUNKSZ32
-#define nbits_to_hold_value(val)   fls(val)
-#define BASEDEC 10 /* fancier cpuset lists input in decimal */
-
-/**
- * __bitmap_parse - convert an ASCII hex string into a bitmap.
- * @buf: pointer to buffer containing string.
- * @buflen: buffer size in bytes.  If string is smaller than this
- *then it must be terminated with a \0.
- * @is_user: location of buffer, 0 indicates kernel space
- * @maskp: pointer to bitmap array that will contain result.
- * @nmaskbits: size of bitmap, in bits.
- *
- * Commas group hex digits into chunks.  Each chunk defines exactly 32
- * bits of the resultant bitmask.  No chunk may specify a value larger
- * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value
- * then leading 0-bits are prepended.  %-EINVAL is returned for illegal
- * characters and for grouping errors such as "1,,5", ",44", "," and "".
- * Leading and trailing whitespace accepted, but not embedded whitespace.
- */
-int __bitmap_parse(const char *buf, unsigned int buflen,
-   int is_user, unsigned long *maskp,
-   int nmaskbits)
-{
-   int c, old_c, totaldigits, ndigits, nchunks, nbits;
-   u32 chunk;
-   const char __user __force *ubuf = (const char __user __force *)buf;
-
-   bitmap_zero(maskp, nmaskbits);
-
-   nchunks = nbits = totaldigits = c = 0;
-   do {
-   chunk = 0;
-   ndigits = totaldigits;
-
-   /* Get the next chunk of the bitmap */
-   while (buflen) {
-   old_c = c;
-   if (is_user) {
-   if (__get_user(c, ubuf++))
-   return -EFAULT;
-   }
-   else
-   c = *buf++;
-   buflen--;
-   if (isspace(c))
-   continue;
-
-   /*
-* If the last character was a space and the current
-* character isn't '\0', we've got embedded whitespace.
-* This is a no-no, so throw an error.
-*/
-   if (totaldigits && c && isspace(old_c))
-   return -EINVAL;
-
-   /* A '\0' or a ',' signal the end of the chunk */
-   if (c == '\0' || c == ',')
-   break;
-
-   if (!isxdigit(c))
-   return -EINVAL;
-
-   /*
-* Make sure there are at least 4 free bits in 'chunk'.
-