Re: [RFC PATCH 1/2] bits: introduce ffs_val()

From: Petr Tesarik

Date: Fri Jan 09 2026 - 12:46:19 EST


On Fri, 9 Jan 2026 12:16:06 -0500
Yury Norov <ynorov@xxxxxxxxxx> wrote:

> On Fri, Jan 09, 2026 at 05:37:56PM +0100, Petr Tesarik wrote:
> > Introduce a macro that can efficiently extract the least significant
> > non-zero bit from a value.
> >
> > Interestingly, this bit-twiddling trick is open-coded in some places, but
> > it also appears to be little known, leading to various inefficient
> > implementations in other places. Let's make it part of the standard bitops
> > arsenal.
> >
> > Define the macro in a separate header file included from <linux/bitops.h>,
> > to allow using it in very low-level header files that may not want to
> > include all of <linux/bitops.h>.
>
> Nice catch. Thanks!

It's been on my TODO list for months, but my first attempt failed
because of a coccinelle bug, and then I never got to it again.

> > Signed-off-by: Petr Tesarik <ptesarik@xxxxxxxx>
> > ---
> > MAINTAINERS | 1 +
> > include/linux/bitops.h | 1 +
> > include/linux/ffs_val.h | 21 +++++++++++++++++++++
> > 3 files changed, 23 insertions(+)
> > create mode 100644 include/linux/ffs_val.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index a0dd762f5648b..8f15c76a67ea2 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -4466,6 +4466,7 @@ F: arch/*/lib/bitops.c
> > F: include/asm-generic/bitops
> > F: include/asm-generic/bitops.h
> > F: include/linux/bitops.h
> > +F: include/linux/ffs_val.h
>
> No need for a separate header. Just put int straight in bitops.h.

Well, <linux/bitops.h> is a bit heavy, so I was afraid of spoiling
build times if I include it from <asm-generic/div64.h>, but if you say
it's fine, yes, why not, let's put it into bitops.h somewhere before
#include <asm/bitops.h>.

> > F: lib/hweight.c
> > F: lib/test_bitops.c
> > F: tools/*/bitops*
> > diff --git a/include/linux/bitops.h b/include/linux/bitops.h
> > index ea7898cc59039..209f0c3e07b9e 100644
> > --- a/include/linux/bitops.h
> > +++ b/include/linux/bitops.h
> > @@ -4,6 +4,7 @@
> >
> > #include <asm/types.h>
> > #include <linux/bits.h>
> > +#include <linux/ffs_val.h>
> > #include <linux/typecheck.h>
> >
> > #include <uapi/linux/kernel.h>
> > diff --git a/include/linux/ffs_val.h b/include/linux/ffs_val.h
> > new file mode 100644
> > index 0000000000000..193ec86d2b53b
> > --- /dev/null
> > +++ b/include/linux/ffs_val.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_LINUX_FFS_VAL_H_
> > +#define _ASM_LINUX_FFS_VAL_H_
> > +
> > +/**
> > + * ffs_val - find the value of the first set bit
>
> By definition, the value of 1st set bit is 1, just like any other set
> bit. :)

I'm struggling with suitable wording. The trouble is that "find first
set bit" is generally understood as find the bit _position_. Maybe I
should say _isolate_ the bit, or something like that.

> > + * @x: the value to search
> > + *
> > + * Unlike ffs(), which returns a bit position, ffs_val() returns the bit
> > + * value itself.
> > + *
> > + * Returns:
> > + * least significant non-zero bit, 0 if all bits are zero
> > + */
> > +#define ffs_val(x) \
> > +({ \
> > + const typeof(x) val__ = (x); \
>
> const auto? Also, are you sure it works OK with unsigned types? No
> warnings? Maybe add a test?

The "const auto" is good idea. Regarding unsigned types, indeed, the
result of applying unary minus to any non-zero value is out of bounds
of an unsigned type. However, the C standard has this much to say:
"C’s unsigned integer types are ‘‘modulo’’ in the LIA−1 sense in that
overflows or out-of-bounds results silently wrap."

Besides, this patch series does not change anything, it merely puts the
arithmetic inside a macro.

> > + val__ & -val__; \
> > +})
>
> This macro returns in fact a mask containing LSB only, so I'd suggest
> to choose a name like lsb_mask().

Mask is a terrible word, because it doesn't say if the masked bit is
set or clear. Even if I limit myself to the Linux kernel, it's used for
both in different contexts.

What about isolate_lsb()?

The only issue is that LSB may also refer to least-significant BYTE. :-(

> This is also a replacement of BIT(ffs()), GENMASK(ffs(), 0) constructions.
> Can you check the kernel, and convert those patterns too? I found at least
> one in drivers/clk/nxp/clk-lpc32xx.c:lpc32xx_clk_div_quirk().

Yes, there's a lot of places under drivers/ that can benefit from this
macro. I didn't want to spam everyone with this RFC, as we iron out the
details. I'm even unsure about the correct process to get such a change
into the kernel.

Thanks for the review and suggestions!

Petr T