diff mbox series

[net] lib: packing: fix shift wrapping in bit_reverse()

Message ID Y5B3sAcS6qKSt+lS@kili (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [net] lib: packing: fix shift wrapping in bit_reverse() | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 2 this patch: 2
netdev/cc_maintainers success CCed 3 of 3 maintainers
netdev/build_clang success Errors and warnings before: 5 this patch: 5
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 2 this patch: 2
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 14 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Dan Carpenter Dec. 7, 2022, 11:23 a.m. UTC
The bit_reverse() function is clearly supposed to be able to handle
64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
are not enough to handle more than 32 bits.

Fixes: 554aae35007e ("lib: Add support for generic packing operations")
Signed-off-by: Dan Carpenter <error27@gmail.com>
---
 lib/packing.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

Comments

Vladimir Oltean Dec. 7, 2022, 12:19 p.m. UTC | #1
On Wed, Dec 07, 2022 at 02:23:28PM +0300, Dan Carpenter wrote:
> The bit_reverse() function is clearly supposed to be able to handle
> 64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
> are not enough to handle more than 32 bits.
> 
> Fixes: 554aae35007e ("lib: Add support for generic packing operations")
> Signed-off-by: Dan Carpenter <error27@gmail.com>
> ---
>  lib/packing.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/lib/packing.c b/lib/packing.c
> index 9a72f4bbf0e2..9d7418052f5a 100644
> --- a/lib/packing.c
> +++ b/lib/packing.c
> @@ -32,12 +32,11 @@ static int get_reverse_lsw32_offset(int offset, size_t len)
>  static u64 bit_reverse(u64 val, unsigned int width)
>  {
>  	u64 new_val = 0;
> -	unsigned int bit;
>  	unsigned int i;
>  
>  	for (i = 0; i < width; i++) {
> -		bit = (val & (1 << i)) != 0;
> -		new_val |= (bit << (width - i - 1));
> +		if (val & BIT_ULL(1))

hmm, why 1 and not i?

> +			new_val |= BIT_ULL(width - i - 1);
>  	}
>  	return new_val;
>  }
> -- 
> 2.35.1
>
Dan Carpenter Dec. 7, 2022, 12:21 p.m. UTC | #2
On Wed, Dec 07, 2022 at 02:19:36PM +0200, Vladimir Oltean wrote:
> On Wed, Dec 07, 2022 at 02:23:28PM +0300, Dan Carpenter wrote:
> > The bit_reverse() function is clearly supposed to be able to handle
> > 64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
> > are not enough to handle more than 32 bits.
> > 
> > Fixes: 554aae35007e ("lib: Add support for generic packing operations")
> > Signed-off-by: Dan Carpenter <error27@gmail.com>
> > ---
> >  lib/packing.c | 5 ++---
> >  1 file changed, 2 insertions(+), 3 deletions(-)
> > 
> > diff --git a/lib/packing.c b/lib/packing.c
> > index 9a72f4bbf0e2..9d7418052f5a 100644
> > --- a/lib/packing.c
> > +++ b/lib/packing.c
> > @@ -32,12 +32,11 @@ static int get_reverse_lsw32_offset(int offset, size_t len)
> >  static u64 bit_reverse(u64 val, unsigned int width)
> >  {
> >  	u64 new_val = 0;
> > -	unsigned int bit;
> >  	unsigned int i;
> >  
> >  	for (i = 0; i < width; i++) {
> > -		bit = (val & (1 << i)) != 0;
> > -		new_val |= (bit << (width - i - 1));
> > +		if (val & BIT_ULL(1))
> 
> hmm, why 1 and not i?

Because I'm a moron.  Let me resend.

regards,
dan carpenter
Vladimir Oltean Dec. 7, 2022, 12:22 p.m. UTC | #3
On Wed, Dec 07, 2022 at 03:21:04PM +0300, Dan Carpenter wrote:
> On Wed, Dec 07, 2022 at 02:19:36PM +0200, Vladimir Oltean wrote:
> > On Wed, Dec 07, 2022 at 02:23:28PM +0300, Dan Carpenter wrote:
> > > The bit_reverse() function is clearly supposed to be able to handle
> > > 64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
> > > are not enough to handle more than 32 bits.
> > > 
> > > Fixes: 554aae35007e ("lib: Add support for generic packing operations")
> > > Signed-off-by: Dan Carpenter <error27@gmail.com>
> > > ---
> > >  lib/packing.c | 5 ++---
> > >  1 file changed, 2 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/lib/packing.c b/lib/packing.c
> > > index 9a72f4bbf0e2..9d7418052f5a 100644
> > > --- a/lib/packing.c
> > > +++ b/lib/packing.c
> > > @@ -32,12 +32,11 @@ static int get_reverse_lsw32_offset(int offset, size_t len)
> > >  static u64 bit_reverse(u64 val, unsigned int width)
> > >  {
> > >  	u64 new_val = 0;
> > > -	unsigned int bit;
> > >  	unsigned int i;
> > >  
> > >  	for (i = 0; i < width; i++) {
> > > -		bit = (val & (1 << i)) != 0;
> > > -		new_val |= (bit << (width - i - 1));
> > > +		if (val & BIT_ULL(1))
> > 
> > hmm, why 1 and not i?
> 
> Because I'm a moron.  Let me resend.

Wait a second, I deliberately wrote the code without conditionals.
Let me look at the code disassembly before and after the patch and see
what they look like.
Dan Carpenter Dec. 7, 2022, 12:51 p.m. UTC | #4
On Wed, Dec 07, 2022 at 02:22:54PM +0200, Vladimir Oltean wrote:
> On Wed, Dec 07, 2022 at 03:21:04PM +0300, Dan Carpenter wrote:
> > On Wed, Dec 07, 2022 at 02:19:36PM +0200, Vladimir Oltean wrote:
> > > On Wed, Dec 07, 2022 at 02:23:28PM +0300, Dan Carpenter wrote:
> > > > The bit_reverse() function is clearly supposed to be able to handle
> > > > 64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
> > > > are not enough to handle more than 32 bits.
> > > > 
> > > > Fixes: 554aae35007e ("lib: Add support for generic packing operations")
> > > > Signed-off-by: Dan Carpenter <error27@gmail.com>
> > > > ---
> > > >  lib/packing.c | 5 ++---
> > > >  1 file changed, 2 insertions(+), 3 deletions(-)
> > > > 
> > > > diff --git a/lib/packing.c b/lib/packing.c
> > > > index 9a72f4bbf0e2..9d7418052f5a 100644
> > > > --- a/lib/packing.c
> > > > +++ b/lib/packing.c
> > > > @@ -32,12 +32,11 @@ static int get_reverse_lsw32_offset(int offset, size_t len)
> > > >  static u64 bit_reverse(u64 val, unsigned int width)
> > > >  {
> > > >  	u64 new_val = 0;
> > > > -	unsigned int bit;
> > > >  	unsigned int i;
> > > >  
> > > >  	for (i = 0; i < width; i++) {
> > > > -		bit = (val & (1 << i)) != 0;
> > > > -		new_val |= (bit << (width - i - 1));
> > > > +		if (val & BIT_ULL(1))
> > > 
> > > hmm, why 1 and not i?
> > 
> > Because I'm a moron.  Let me resend.
> 
> Wait a second, I deliberately wrote the code without conditionals.
> Let me look at the code disassembly before and after the patch and see
> what they look like.

My crappy benchmark says that the if statement is faster.  22 vs 26
seconds.

regards,
dan carpenter

#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>

#define BIT(n) (1 << (n))
#define BIT_ULL(n) (1ULL << (n))

#define u64 unsigned long long
#define u32 unsigned int
#define u16 unsigned short
#define u8  unsigned char

static u64 bit_reverse1(u64 val, unsigned int width)
{
	u64 new_val = 0;
	unsigned int i;

	for (i = 0; i < width; i++) {
		if (val & BIT_ULL(i))
			new_val |= BIT_ULL(width - i - 1);
	}
	return new_val;
}

static u64 bit_reverse2(u64 val, unsigned int width)
{
	u64 new_val = 0;
	u64 bit;
	unsigned int i;

	for (i = 0; i < width; i++) {
		bit = (val & BIT_ULL(i)) != 0;
		new_val |= (bit << (width - i - 1));
	}
	return new_val;
}

int main(void)
{
	unsigned long long val;

	for (val = ULLONG_MAX - INT_MAX; val; val++)
		bit_reverse1(val, 2);

	return 0;
}
Vladimir Oltean Dec. 7, 2022, 1:02 p.m. UTC | #5
On Wed, Dec 07, 2022 at 02:22:54PM +0200, Vladimir Oltean wrote:
> Wait a second, I deliberately wrote the code without conditionals.
> Let me look at the code disassembly before and after the patch and see
> what they look like.

Before (make lib/packing.lst on arm64):

	for (i = 0; i < width; i++) {
  1c:	310004ec 	adds	w12, w7, #0x1
  20:	540001c0 	b.eq	58 <adjust_for_msb_right_quirk+0x58>  // b.none
  24:	52800004 	mov	w4, #0x0                   	// #0
		bit = (val & (1 << i)) != 0;
  28:	5280002b 	mov	w11, #0x1                   	// #1
  2c:	d503201f 	nop
  30:	1ac42166 	lsl	w6, w11, w4
		new_val |= (bit << (width - i - 1));
  34:	4b0400e9 	sub	w9, w7, w4
		bit = (val & (1 << i)) != 0;
  38:	93407cc6 	sxtw	x6, w6		/* We don't want sign extension */
  3c:	ea0a00df 	tst	x6, x10
  40:	1a9f07e6 	cset	w6, ne  // ne = any
	for (i = 0; i < width; i++) {
  44:	6b07009f 	cmp	w4, w7
  48:	11000484 	add	w4, w4, #0x1
		new_val |= (bit << (width - i - 1));
  4c:	1ac920c6 	lsl	w6, w6, w9
  50:	aa060108 	orr	x8, x8, x6
	for (i = 0; i < width; i++) {
  54:	54fffee1 	b.ne	30 <adjust_for_msb_right_quirk+0x30>  // b.any


Then this modified code:

static u64 bit_reverse(u64 val, unsigned int width)
{
	u64 new_val = 0;
	unsigned int i;

	for (i = 0; i < width; i++)
		if (val & BIT_ULL(i))
			new_val |= BIT_ULL(width - i - 1);

	return new_val;
}

compiles to this:

	for (i = 0; i < width; i++) {
  1c:	310004eb 	adds	w11, w7, #0x1
  20:	540001c0 	b.eq	58 <adjust_for_msb_right_quirk+0x58>  // b.none
  24:	52800005 	mov	w5, #0x0                   	// #0
			new_val |= BIT_ULL(width - i - 1);
  28:	d280002a 	mov	x10, #0x1                   	// #1
  2c:	14000002 	b	34 <adjust_for_msb_right_quirk+0x34>	/* this is an unconditional jump to "sub w4, w7, w5", skipping the assignment to w5 */
  30:	2a0803e5 	mov	w5, w8			/* this is only hit by the backwards jump b.ne 30 at the end */
  34:	4b0500e4 	sub	w4, w7, w5
		if (val & BIT_ULL(i))
  38:	9ac52528 	lsr	x8, x9, x5
			new_val |= BIT_ULL(width - i - 1);
  3c:	f240011f 	tst	x8, #0x1
	for (i = 0; i < width; i++) {
  40:	110004a8 	add	w8, w5, #0x1
			new_val |= BIT_ULL(width - i - 1);
  44:	9ac42144 	lsl	x4, x10, x4
  48:	aa0400c4 	orr	x4, x6, x4
  4c:	9a861086 	csel	x6, x4, x6, ne  // ne = any
	for (i = 0; i < width; i++) {
  50:	6b0700bf 	cmp	w5, w7
  54:	54fffee1 	b.ne	30 <adjust_for_msb_right_quirk+0x30>  // b.any

which indeed has no branch in the main loop (between 0x30 and 0x54),
because as I explained, the "b 34" doesn't count - it's not in the loop.

Additionally, this rewritten code which is considerably more obscure in C:

static u64 bit_reverse(u64 val, unsigned int width)
{
	u64 new_val = 0;
	unsigned int i;

	for (i = 0; i < width; i++)
		new_val |= !!(val & BIT_ULL(i)) ?
			   BIT_ULL(width - i - 1) : 0;

	return new_val;
}

compiles to the exact same assembly code as the variant with "if":

	for (i = 0; i < width; i++)
  1c:	310004eb 	adds	w11, w7, #0x1
  20:	540001c0 	b.eq	58 <adjust_for_msb_right_quirk+0x58>  // b.none
  24:	52800005 	mov	w5, #0x0                   	// #0
		new_val |= !!(val & BIT_ULL(i)) ?
  28:	d280002a 	mov	x10, #0x1                   	// #1
  2c:	14000002 	b	34 <adjust_for_msb_right_quirk+0x34>
  30:	2a0803e5 	mov	w5, w8
  34:	4b0500e4 	sub	w4, w7, w5
  38:	9ac52528 	lsr	x8, x9, x5
  3c:	f240011f 	tst	x8, #0x1
	for (i = 0; i < width; i++)
  40:	110004a8 	add	w8, w5, #0x1
		new_val |= !!(val & BIT_ULL(i)) ?
  44:	9ac42144 	lsl	x4, x10, x4
  48:	aa0400c4 	orr	x4, x6, x4
  4c:	9a861086 	csel	x6, x4, x6, ne  // ne = any
	for (i = 0; i < width; i++)
  50:	6b0700bf 	cmp	w5, w7
  54:	54fffee1 	b.ne	30 <adjust_for_msb_right_quirk+0x30>  // b.any

So this is good with the "if".
Vladimir Oltean Dec. 7, 2022, 1:06 p.m. UTC | #6
On Wed, Dec 07, 2022 at 03:51:08PM +0300, Dan Carpenter wrote:
> My crappy benchmark says that the if statement is faster.  22 vs 26
> seconds.

Sure that's not just noise in the measurements? :)
David Laight Dec. 7, 2022, 7:41 p.m. UTC | #7
From: Dan Carpenter
> Sent: 07 December 2022 12:21
....
> > > -		new_val |= (bit << (width - i - 1));
> > > +		if (val & BIT_ULL(1))
> >
> > hmm, why 1 and not i?
> 
> Because I'm a moron.  Let me resend.

Since we're not writing FORTRAN-IV why not use a variable
name that is harder to confuse with 1?

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Vladimir Oltean Dec. 8, 2022, 4:58 p.m. UTC | #8
On Wed, Dec 07, 2022 at 07:41:28PM +0000, David Laight wrote:
> From: Dan Carpenter
> > Sent: 07 December 2022 12:21
> ....
> > > > -		new_val |= (bit << (width - i - 1));
> > > > +		if (val & BIT_ULL(1))
> > >
> > > hmm, why 1 and not i?
> > 
> > Because I'm a moron.  Let me resend.
> 
> Since we're not writing FORTRAN-IV why not use a variable
> name that is harder to confuse with 1?

How about 'k'?
Uladzislau Koshchanka Dec. 9, 2022, 8:21 a.m. UTC | #9
On Wed, 7 Dec 2022 at 14:30, Dan Carpenter <error27@gmail.com> wrote:
>
> The bit_reverse() function is clearly supposed to be able to handle
> 64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
> are not enough to handle more than 32 bits.

It seems from the surrounding code that this function is only called
for width of up to a byte (but correct me if I'm wrong). There are
fast implementations of bit-reverse in include/linux/bitrev.h. It's
better to just remove this function entirely and call bitrev8, which
is just a precalc-table lookup. While at it, also sort includes.

Signed-off-by: Uladzislau Koshchanka <koshchanka@gmail.com>

 lib/packing.c | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/lib/packing.c b/lib/packing.c
index 9a72f4bbf0e2..47ea47c1198a 100644
--- a/lib/packing.c
+++ b/lib/packing.c
@@ -2,10 +2,11 @@
 /* Copyright 2016-2018 NXP
  * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
  */
-#include <linux/packing.h>
-#include <linux/module.h>
 #include <linux/bitops.h>
+#include <linux/bitrev.h>
 #include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/packing.h>
 #include <linux/types.h>

 static int get_le_offset(int offset)
@@ -29,19 +30,6 @@ static int get_reverse_lsw32_offset(int offset, size_t len)
        return word_index * 4 + offset;
 }

-static u64 bit_reverse(u64 val, unsigned int width)
-{
-       u64 new_val = 0;
-       unsigned int bit;
-       unsigned int i;
-
-       for (i = 0; i < width; i++) {
-               bit = (val & (1 << i)) != 0;
-               new_val |= (bit << (width - i - 1));
-       }
-       return new_val;
-}
-
 static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
                                       int *box_end_bit, u8 *box_mask)
 {
@@ -49,7 +37,7 @@ static void adjust_for_msb_right_quirk(u64
*to_write, int *box_start_bit,
        int new_box_start_bit, new_box_end_bit;

        *to_write >>= *box_end_bit;
-       *to_write = bit_reverse(*to_write, box_bit_width);
+       *to_write = bitrev8(*to_write) >> (8 - box_bit_width);
        *to_write <<= *box_end_bit;

        new_box_end_bit   = box_bit_width - *box_start_bit - 1;
Vladimir Oltean Dec. 9, 2022, 2:30 p.m. UTC | #10
Hi Uladzislau,

On Fri, Dec 09, 2022 at 11:21:21AM +0300, Uladzislau Koshchanka wrote:
> On Wed, 7 Dec 2022 at 14:30, Dan Carpenter <error27@gmail.com> wrote:
> >
> > The bit_reverse() function is clearly supposed to be able to handle
> > 64 bit values, but the types for "(1 << i)" and "bit << (width - i - 1)"
> > are not enough to handle more than 32 bits.
> 
> It seems from the surrounding code that this function is only called
> for width of up to a byte (but correct me if I'm wrong).

This observation is quite true. I was quite lazy to look and remember
whether this is the case, but the comment says it quite clearly:

		/* Bit indices into the currently accessed 8-bit box */
		int box_start_bit, box_end_bit, box_addr;

> There are fast implementations of bit-reverse in include/linux/bitrev.h.
> It's better to just remove this function entirely and call bitrev8,
> which is just a precalc-table lookup. While at it, also sort includes.

The problem I see with bitrev8 is that the byte_rev_table[] can
seemingly be built as a module (the BITREVERSE Kconfig knob is tristate,
and btw your patch doesn't make PACKING select BITREVERSE). But PACKING
is bool. IIRC, I got comments during review that it's not worth making
packing a module, but I may remember wrong.

> @@ -49,7 +37,7 @@ static void adjust_for_msb_right_quirk(u64
> *to_write, int *box_start_bit,
>         int new_box_start_bit, new_box_end_bit;
> 
>         *to_write >>= *box_end_bit;
> -       *to_write = bit_reverse(*to_write, box_bit_width);
> +       *to_write = bitrev8(*to_write) >> (8 - box_bit_width);
>         *to_write <<= *box_end_bit;
> 
>         new_box_end_bit   = box_bit_width - *box_start_bit - 1;

Anyway, the patch works in principle. I know this because I wrote the
following patch to check:

From 17099a86291713d2bcf8137473daea5f390a2ef4 Mon Sep 17 00:00:00 2001
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: Fri, 9 Dec 2022 16:23:35 +0200
Subject: [PATCH] lib: packing: add boot-time selftests

In case people want to make changes to the packing() implementation but
they aren't sure it's going to keep working, provide 16 boot-time calls
to packing() which exercise all combinations of quirks plus PACK |
UNPACK.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 lib/Kconfig   |   9 +++
 lib/packing.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 195 insertions(+)

diff --git a/lib/Kconfig b/lib/Kconfig
index 9bbf8a4b2108..54b8deaf44fc 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -39,6 +39,15 @@ config PACKING
 
 	  When in doubt, say N.
 
+config PACKING_SELFTESTS
+	bool "Selftests for packing library"
+	depends on PACKING
+	help
+	  Boot-time selftests to make sure that the packing and unpacking
+	  functions work properly.
+
+	  When in doubt, say N.
+
 config BITREVERSE
 	tristate
 
diff --git a/lib/packing.c b/lib/packing.c
index 9a72f4bbf0e2..aff70853b0c4 100644
--- a/lib/packing.c
+++ b/lib/packing.c
@@ -210,5 +210,191 @@ int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
 }
 EXPORT_SYMBOL(packing);
 
+#if IS_ENABLED(CONFIG_PACKING_SELFTESTS)
+
+#define PBUF_LEN 16
+
+/* These selftests pack and unpack a magic 64-bit value (0xcafedeadbeefcafe) at
+ * a fixed logical offset (32) within an otherwise zero array of 128 bits
+ * (16 bytes). They test all possible bit layouts of the 128 bit buffer.
+ */
+static bool test_pack(u8 expected_pbuf[PBUF_LEN], u8 quirks)
+{
+	u64 uval = 0xcafedeadbeefcafe;
+	u8 pbuf[PBUF_LEN];
+	int err, i;
+
+	memset(pbuf, 0, PBUF_LEN);
+	err = packing(pbuf, &uval, 95, 32, PBUF_LEN, PACK, quirks);
+	if (err) {
+		pr_err("packing() returned %pe\n", ERR_PTR(err));
+		return false;
+	}
+
+	for (i = 0; i < PBUF_LEN; i++) {
+		if (pbuf[i] != expected_pbuf[i]) {
+			print_hex_dump(KERN_ERR, "pbuf:     ", DUMP_PREFIX_NONE,
+				       16, 1, pbuf, PBUF_LEN, false);
+			print_hex_dump(KERN_ERR, "expected: ", DUMP_PREFIX_NONE,
+				       16, 1, expected_pbuf, PBUF_LEN, false);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool test_unpack(u8 pbuf[PBUF_LEN], u8 quirks)
+{
+	u64 uval, expected_uval = 0xcafedeadbeefcafe;
+	int err;
+
+	err = packing(pbuf, &uval, 95, 32, PBUF_LEN, UNPACK, quirks);
+	if (err) {
+		pr_err("packing() returned %pe\n", ERR_PTR(err));
+		return false;
+	}
+
+	if (uval != expected_uval) {
+		pr_err("uval: 0x%llx expected 0x%llx\n", uval, expected_uval);
+		return false;
+	}
+
+	return true;
+}
+
+static void test_no_quirks(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0xca, 0xfe, 0xde, 0xad,
+			     0xbe, 0xef, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, 0);
+	pr_info("packing with no quirks: %s\n", ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, 0);
+	pr_info("unpacking with no quirks: %s\n", ret ? "OK" : "FAIL");
+}
+
+static void test_msb_right(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0x53, 0x7f, 0x7b, 0xb5,
+			     0x7d, 0xf7, 0x53, 0x7f, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("packing with QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("unpacking with QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+}
+
+static void test_le(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0xad, 0xde, 0xfe, 0xca,
+			     0xfe, 0xca, 0xef, 0xbe, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_LITTLE_ENDIAN);
+	pr_info("packing with QUIRK_LITTLE_ENDIAN: %s\n", ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_LITTLE_ENDIAN);
+	pr_info("unpacking with QUIRK_LITTLE_ENDIAN: %s\n",
+		ret ? "OK" : "FAIL");
+}
+
+static void test_le_msb_right(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0xb5, 0x7b, 0x7f, 0x53,
+			     0x7f, 0x53, 0xf7, 0x7d, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_LITTLE_ENDIAN | QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("packing with QUIRK_LITTLE_ENDIAN | QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_LITTLE_ENDIAN | QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("unpacking with QUIRK_LITTLE_ENDIAN | QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+}
+
+static void test_lsw32_first(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0xbe, 0xef, 0xca, 0xfe,
+			     0xca, 0xfe, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_LSW32_IS_FIRST);
+	pr_info("packing with QUIRK_LSW32_IS_FIRST: %s\n", ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_LSW32_IS_FIRST);
+	pr_info("unpacking with QUIRK_LSW32_IS_FIRST: %s\n", ret ? "OK" : "FAIL");
+}
+
+static void test_lsw32_first_msb_right(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0x7d, 0xf7, 0x53, 0x7f,
+			     0x53, 0x7f, 0x7b, 0xb5, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_LSW32_IS_FIRST | QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("packing with QUIRK_LSW32_IS_FIRST | QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_LSW32_IS_FIRST | QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("unpacking with QUIRK_LSW32_IS_FIRST | QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+}
+
+static void test_lsw32_first_le(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0xfe, 0xca, 0xef, 0xbe,
+			     0xad, 0xde, 0xfe, 0xca, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN);
+	pr_info("packing with QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN: %s\n",
+		ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN);
+	pr_info("unpacking with QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN: %s\n",
+		ret ? "OK" : "FAIL");
+}
+
+static void test_lsw32_first_le_msb_right(void)
+{
+	u8 pbuf[PBUF_LEN] = {0x00, 0x00, 0x00, 0x00, 0x7f, 0x53, 0xf7, 0x7d,
+			     0xb5, 0x7b, 0x7f, 0x53, 0x00, 0x00, 0x00, 0x00};
+	bool ret;
+
+	ret = test_pack(pbuf, QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN |
+			QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("packing with QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN | QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+
+	ret = test_unpack(pbuf, QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN |
+			  QUIRK_MSB_ON_THE_RIGHT);
+	pr_info("unpacking with QUIRK_LSW32_IS_FIRST | QUIRK_LITTLE_ENDIAN | QUIRK_MSB_ON_THE_RIGHT: %s\n",
+		ret ? "OK" : "FAIL");
+}
+
+static int __init packing_init(void)
+{
+	test_no_quirks();
+	test_msb_right();
+	test_le();
+	test_le_msb_right();
+	test_lsw32_first();
+	test_lsw32_first_msb_right();
+	test_lsw32_first_le();
+	test_lsw32_first_le_msb_right();
+
+	return 0;
+}
+module_init(packing_init);
+#endif
+
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Generic bitfield packing and unpacking");
Uladzislau Koshchanka Dec. 9, 2022, 9:01 p.m. UTC | #11
Hi Vladimir,

> The problem I see with bitrev8 is that the byte_rev_table[] can
> seemingly be built as a module (the BITREVERSE Kconfig knob is tristate,
> and btw your patch doesn't make PACKING select BITREVERSE). But PACKING
> is bool. IIRC, I got comments during review that it's not worth making
> packing a module, but I may remember wrong.

Do you really think it's a problem? I personally would just select
BITREVERSE with/without making PACKING tristate. BITREVERSE is already
selected by CRC32 which defaults to y, so just adding a select isn't a
change in the default. Can't think of a practical point in avoiding
linking against 256 bytes here.

In any case, it just doesn't look right to have multiple bit-reverse
implementations only because of Kconfig relations.
Vladimir Oltean Dec. 9, 2022, 10:06 p.m. UTC | #12
On Sat, Dec 10, 2022 at 12:01:28AM +0300, Uladzislau Koshchanka wrote:
> Hi Vladimir,
> 
> > The problem I see with bitrev8 is that the byte_rev_table[] can
> > seemingly be built as a module (the BITREVERSE Kconfig knob is tristate,
> > and btw your patch doesn't make PACKING select BITREVERSE). But PACKING
> > is bool. IIRC, I got comments during review that it's not worth making
> > packing a module, but I may remember wrong.
> 
> Do you really think it's a problem? I personally would just select
> BITREVERSE with/without making PACKING tristate. BITREVERSE is already
> selected by CRC32 which defaults to y, so just adding a select isn't a
> change in the default. Can't think of a practical point in avoiding
> linking against 256 bytes here.
> 
> In any case, it just doesn't look right to have multiple bit-reverse
> implementations only because of Kconfig relations.

Ok, let's use BITREVERSE then. Could you submit your patch formally please?
Vladimir Oltean Dec. 9, 2022, 10:07 p.m. UTC | #13
On Sat, Dec 10, 2022 at 12:06:51AM +0200, Vladimir Oltean wrote:
> On Sat, Dec 10, 2022 at 12:01:28AM +0300, Uladzislau Koshchanka wrote:
> > Hi Vladimir,
> > 
> > > The problem I see with bitrev8 is that the byte_rev_table[] can
> > > seemingly be built as a module (the BITREVERSE Kconfig knob is tristate,
> > > and btw your patch doesn't make PACKING select BITREVERSE). But PACKING
> > > is bool. IIRC, I got comments during review that it's not worth making
> > > packing a module, but I may remember wrong.
> > 
> > Do you really think it's a problem? I personally would just select
> > BITREVERSE with/without making PACKING tristate. BITREVERSE is already
> > selected by CRC32 which defaults to y, so just adding a select isn't a
> > change in the default. Can't think of a practical point in avoiding
> > linking against 256 bytes here.
> > 
> > In any case, it just doesn't look right to have multiple bit-reverse
> > implementations only because of Kconfig relations.
> 
> Ok, let's use BITREVERSE then. Could you submit your patch formally please?

Also, none of that 'while at it, do XYZ unrelated stuff'. One patch per
logical change please.
diff mbox series

Patch

diff --git a/lib/packing.c b/lib/packing.c
index 9a72f4bbf0e2..9d7418052f5a 100644
--- a/lib/packing.c
+++ b/lib/packing.c
@@ -32,12 +32,11 @@  static int get_reverse_lsw32_offset(int offset, size_t len)
 static u64 bit_reverse(u64 val, unsigned int width)
 {
 	u64 new_val = 0;
-	unsigned int bit;
 	unsigned int i;
 
 	for (i = 0; i < width; i++) {
-		bit = (val & (1 << i)) != 0;
-		new_val |= (bit << (width - i - 1));
+		if (val & BIT_ULL(1))
+			new_val |= BIT_ULL(width - i - 1);
 	}
 	return new_val;
 }