ffs-const update for v6.18-rc1
- PCI: Fix theoretical underflow in use of ffs().
- Universally apply __attribute_const__ to all architecture's ffs()-family
of functions.
- Add KUnit tests for ffs() behavior and const-ness.
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQRSPkdeREjth1dHnSE2KwveOeQkuwUCaNrWngAKCRA2KwveOeQk
u3ZGAPwJTscARU4MspnqpbuAV601dG1TNoJG+8JYH84r+R2jjQEAlmBZB0jaHbC2
qFWjHivD/0ofvihKfAPFgxlakyV1XAg=
=diXF
-----END PGP SIGNATURE-----
Merge tag 'ffs-const-v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull ffs const-attribute cleanups from Kees Cook:
"While working on various hardening refactoring a while back we
encountered inconsistencies in the application of __attribute_const__
on the ffs() family of functions.
This series fixes this across all archs and adds KUnit tests.
Notably, this found a theoretical underflow in PCI (also fixed here)
and uncovered an inefficiency in ARC (fixed in the ARC arch PR). I
kept the series separate from the general hardening PR since it is a
stand-alone "topic".
- PCI: Fix theoretical underflow in use of ffs().
- Universally apply __attribute_const__ to all architecture's
ffs()-family of functions.
- Add KUnit tests for ffs() behavior and const-ness"
* tag 'ffs-const-v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
KUnit: ffs: Validate all the __attribute_const__ annotations
sparc: Add __attribute_const__ to ffs()-family implementations
xtensa: Add __attribute_const__ to ffs()-family implementations
s390: Add __attribute_const__ to ffs()-family implementations
parisc: Add __attribute_const__ to ffs()-family implementations
mips: Add __attribute_const__ to ffs()-family implementations
m68k: Add __attribute_const__ to ffs()-family implementations
openrisc: Add __attribute_const__ to ffs()-family implementations
riscv: Add __attribute_const__ to ffs()-family implementations
hexagon: Add __attribute_const__ to ffs()-family implementations
alpha: Add __attribute_const__ to ffs()-family implementations
sh: Add __attribute_const__ to ffs()-family implementations
powerpc: Add __attribute_const__ to ffs()-family implementations
x86: Add __attribute_const__ to ffs()-family implementations
csky: Add __attribute_const__ to ffs()-family implementations
bitops: Add __attribute_const__ to generic ffs()-family implementations
KUnit: Introduce ffs()-family tests
PCI: Test for bit underflow in pcie_set_readrq()
pull/1354/merge
commit
8c1ed30218
|
|
@ -328,7 +328,7 @@ static inline unsigned long ffz_b(unsigned long x)
|
|||
return sum;
|
||||
}
|
||||
|
||||
static inline unsigned long ffz(unsigned long word)
|
||||
static inline unsigned long __attribute_const__ ffz(unsigned long word)
|
||||
{
|
||||
#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67)
|
||||
/* Whee. EV67 can calculate it directly. */
|
||||
|
|
@ -348,7 +348,7 @@ static inline unsigned long ffz(unsigned long word)
|
|||
/*
|
||||
* __ffs = Find First set bit in word. Undefined if no set bit exists.
|
||||
*/
|
||||
static inline unsigned long __ffs(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long word)
|
||||
{
|
||||
#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67)
|
||||
/* Whee. EV67 can calculate it directly. */
|
||||
|
|
@ -373,7 +373,7 @@ static inline unsigned long __ffs(unsigned long word)
|
|||
* differs in spirit from the above __ffs.
|
||||
*/
|
||||
|
||||
static inline int ffs(int word)
|
||||
static inline __attribute_const__ int ffs(int word)
|
||||
{
|
||||
int result = __ffs(word) + 1;
|
||||
return word ? result : 0;
|
||||
|
|
@ -383,14 +383,14 @@ static inline int ffs(int word)
|
|||
* fls: find last bit set.
|
||||
*/
|
||||
#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67)
|
||||
static inline int fls64(unsigned long word)
|
||||
static inline __attribute_const__ int fls64(unsigned long word)
|
||||
{
|
||||
return 64 - __kernel_ctlz(word);
|
||||
}
|
||||
#else
|
||||
extern const unsigned char __flsm1_tab[256];
|
||||
|
||||
static inline int fls64(unsigned long x)
|
||||
static inline __attribute_const__ int fls64(unsigned long x)
|
||||
{
|
||||
unsigned long t, a, r;
|
||||
|
||||
|
|
@ -403,12 +403,12 @@ static inline int fls64(unsigned long x)
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline unsigned long __fls(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __fls(unsigned long x)
|
||||
{
|
||||
return fls64(x) - 1;
|
||||
}
|
||||
|
||||
static inline int fls(unsigned int x)
|
||||
static inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
return fls64(x);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
/*
|
||||
* asm-generic/bitops/ffs.h
|
||||
*/
|
||||
static inline int ffs(int x)
|
||||
static inline __attribute_const__ int ffs(int x)
|
||||
{
|
||||
if (!x)
|
||||
return 0;
|
||||
|
|
@ -26,7 +26,7 @@ static inline int ffs(int x)
|
|||
/*
|
||||
* asm-generic/bitops/__ffs.h
|
||||
*/
|
||||
static __always_inline unsigned long __ffs(unsigned long x)
|
||||
static __always_inline __attribute_const__ unsigned long __ffs(unsigned long x)
|
||||
{
|
||||
asm volatile (
|
||||
"brev %0\n"
|
||||
|
|
@ -39,7 +39,7 @@ static __always_inline unsigned long __ffs(unsigned long x)
|
|||
/*
|
||||
* asm-generic/bitops/fls.h
|
||||
*/
|
||||
static __always_inline int fls(unsigned int x)
|
||||
static __always_inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
asm volatile(
|
||||
"ff1 %0\n"
|
||||
|
|
@ -52,7 +52,7 @@ static __always_inline int fls(unsigned int x)
|
|||
/*
|
||||
* asm-generic/bitops/__fls.h
|
||||
*/
|
||||
static __always_inline unsigned long __fls(unsigned long x)
|
||||
static __always_inline __attribute_const__ unsigned long __fls(unsigned long x)
|
||||
{
|
||||
return fls(x) - 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ arch_test_bit_acquire(unsigned long nr, const volatile unsigned long *addr)
|
|||
*
|
||||
* Undefined if no zero exists, so code should check against ~0UL first.
|
||||
*/
|
||||
static inline long ffz(int x)
|
||||
static inline long __attribute_const__ ffz(int x)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ static inline long ffz(int x)
|
|||
* This is defined the same way as ffs.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
static inline int fls(unsigned int x)
|
||||
static inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -238,7 +238,7 @@ static inline int fls(unsigned int x)
|
|||
* the libc and compiler builtin ffs routines, therefore
|
||||
* differs in spirit from the above ffz (man ffs).
|
||||
*/
|
||||
static inline int ffs(int x)
|
||||
static inline __attribute_const__ int ffs(int x)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -260,7 +260,7 @@ static inline int ffs(int x)
|
|||
* bits_per_long assumed to be 32
|
||||
* numbering starts at 0 I think (instead of 1 like ffs)
|
||||
*/
|
||||
static inline unsigned long __ffs(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long word)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
|
@ -278,7 +278,7 @@ static inline unsigned long __ffs(unsigned long word)
|
|||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
* bits_per_long assumed to be 32
|
||||
*/
|
||||
static inline unsigned long __fls(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __fls(unsigned long word)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ static inline int find_next_bit(const unsigned long *vaddr, int size,
|
|||
* ffz = Find First Zero in word. Undefined if no zero exists,
|
||||
* so code should check against ~0UL first..
|
||||
*/
|
||||
static inline unsigned long ffz(unsigned long word)
|
||||
static inline unsigned long __attribute_const__ ffz(unsigned long word)
|
||||
{
|
||||
int res;
|
||||
|
||||
|
|
@ -488,7 +488,7 @@ static inline unsigned long ffz(unsigned long word)
|
|||
*/
|
||||
#if (defined(__mcfisaaplus__) || defined(__mcfisac__)) && \
|
||||
!defined(CONFIG_M68000)
|
||||
static inline unsigned long __ffs(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long x)
|
||||
{
|
||||
__asm__ __volatile__ ("bitrev %0; ff1 %0"
|
||||
: "=d" (x)
|
||||
|
|
@ -496,7 +496,7 @@ static inline unsigned long __ffs(unsigned long x)
|
|||
return x;
|
||||
}
|
||||
|
||||
static inline int ffs(int x)
|
||||
static inline __attribute_const__ int ffs(int x)
|
||||
{
|
||||
if (!x)
|
||||
return 0;
|
||||
|
|
@ -518,7 +518,7 @@ static inline int ffs(int x)
|
|||
* the libc and compiler builtin ffs routines, therefore
|
||||
* differs in spirit from the above ffz (man ffs).
|
||||
*/
|
||||
static inline int ffs(int x)
|
||||
static inline __attribute_const__ int ffs(int x)
|
||||
{
|
||||
int cnt;
|
||||
|
||||
|
|
@ -528,7 +528,7 @@ static inline int ffs(int x)
|
|||
return 32 - cnt;
|
||||
}
|
||||
|
||||
static inline unsigned long __ffs(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long x)
|
||||
{
|
||||
return ffs(x) - 1;
|
||||
}
|
||||
|
|
@ -536,7 +536,7 @@ static inline unsigned long __ffs(unsigned long x)
|
|||
/*
|
||||
* fls: find last bit set.
|
||||
*/
|
||||
static inline int fls(unsigned int x)
|
||||
static inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int cnt;
|
||||
|
||||
|
|
@ -546,7 +546,7 @@ static inline int fls(unsigned int x)
|
|||
return 32 - cnt;
|
||||
}
|
||||
|
||||
static inline unsigned long __fls(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __fls(unsigned long x)
|
||||
{
|
||||
return fls(x) - 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ static inline void __clear_bit_unlock(unsigned long nr, volatile unsigned long *
|
|||
* Return the bit position (0..63) of the most significant 1 bit in a word
|
||||
* Returns -1 if no 1 bit exists
|
||||
*/
|
||||
static __always_inline unsigned long __fls(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long __fls(unsigned long word)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
|
@ -393,7 +393,7 @@ static __always_inline unsigned long __fls(unsigned long word)
|
|||
* Returns 0..SZLONG-1
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static __always_inline unsigned long __ffs(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long __ffs(unsigned long word)
|
||||
{
|
||||
return __fls(word & -word);
|
||||
}
|
||||
|
|
@ -405,7 +405,7 @@ static __always_inline unsigned long __ffs(unsigned long word)
|
|||
* This is defined the same way as ffs.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
static inline int fls(unsigned int x)
|
||||
static inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -458,7 +458,7 @@ static inline int fls(unsigned int x)
|
|||
* the libc and compiler builtin ffs routines, therefore
|
||||
* differs in spirit from the below ffz (man ffs).
|
||||
*/
|
||||
static inline int ffs(int word)
|
||||
static inline __attribute_const__ int ffs(int word)
|
||||
{
|
||||
if (!word)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#ifdef CONFIG_OPENRISC_HAVE_INST_FF1
|
||||
|
||||
static inline unsigned long __ffs(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long x)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#ifdef CONFIG_OPENRISC_HAVE_INST_FL1
|
||||
|
||||
static inline unsigned long __fls(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __fls(unsigned long x)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#ifdef CONFIG_OPENRISC_HAVE_INST_FF1
|
||||
|
||||
static inline int ffs(int x)
|
||||
static inline __attribute_const__ int ffs(int x)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#ifdef CONFIG_OPENRISC_HAVE_INST_FL1
|
||||
|
||||
static inline int fls(unsigned int x)
|
||||
static inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ static __inline__ int test_and_change_bit(int nr, volatile unsigned long * addr)
|
|||
* cycles for each mispredicted branch.
|
||||
*/
|
||||
|
||||
static __inline__ unsigned long __ffs(unsigned long x)
|
||||
static __inline__ __attribute_const__ unsigned long __ffs(unsigned long x)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ static __inline__ unsigned long __ffs(unsigned long x)
|
|||
* This is defined the same way as the libc and compiler builtin
|
||||
* ffs routines, therefore differs in spirit from the above ffz (man ffs).
|
||||
*/
|
||||
static __inline__ int ffs(int x)
|
||||
static __inline__ __attribute_const__ int ffs(int x)
|
||||
{
|
||||
return x ? (__ffs((unsigned long)x) + 1) : 0;
|
||||
}
|
||||
|
|
@ -171,7 +171,7 @@ static __inline__ int ffs(int x)
|
|||
* fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
|
||||
static __inline__ int fls(unsigned int x)
|
||||
static __inline__ __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int ret;
|
||||
if (!x)
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ static inline void arch___clear_bit_unlock(int nr, volatile unsigned long *addr)
|
|||
* fls: find last (most-significant) bit set.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
static __always_inline int fls(unsigned int x)
|
||||
static __always_inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int lz;
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ static __always_inline int fls(unsigned int x)
|
|||
* 32-bit fls calls.
|
||||
*/
|
||||
#ifdef CONFIG_PPC64
|
||||
static __always_inline int fls64(__u64 x)
|
||||
static __always_inline __attribute_const__ int fls64(__u64 x)
|
||||
{
|
||||
int lz;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
#error "Unexpected BITS_PER_LONG"
|
||||
#endif
|
||||
|
||||
static __always_inline unsigned long variable__ffs(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long variable__ffs(unsigned long word)
|
||||
{
|
||||
asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0,
|
||||
RISCV_ISA_EXT_ZBB, 1)
|
||||
|
|
@ -74,7 +74,7 @@ legacy:
|
|||
(unsigned long)__builtin_ctzl(word) : \
|
||||
variable__ffs(word))
|
||||
|
||||
static __always_inline unsigned long variable__fls(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long variable__fls(unsigned long word)
|
||||
{
|
||||
asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0,
|
||||
RISCV_ISA_EXT_ZBB, 1)
|
||||
|
|
@ -103,7 +103,7 @@ legacy:
|
|||
(unsigned long)(BITS_PER_LONG - 1 - __builtin_clzl(word)) : \
|
||||
variable__fls(word))
|
||||
|
||||
static __always_inline int variable_ffs(int x)
|
||||
static __always_inline __attribute_const__ int variable_ffs(int x)
|
||||
{
|
||||
asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0,
|
||||
RISCV_ISA_EXT_ZBB, 1)
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ static inline unsigned char __flogr(unsigned long word)
|
|||
*
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline unsigned long __ffs(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long word)
|
||||
{
|
||||
return __flogr(-word & word) ^ (BITS_PER_LONG - 1);
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ static inline unsigned long __ffs(unsigned long word)
|
|||
* This is defined the same way as the libc and
|
||||
* compiler builtin ffs routines (man ffs).
|
||||
*/
|
||||
static inline int ffs(int word)
|
||||
static inline __attribute_const__ int ffs(int word)
|
||||
{
|
||||
unsigned long mask = 2 * BITS_PER_LONG - 1;
|
||||
unsigned int val = (unsigned int)word;
|
||||
|
|
@ -205,7 +205,7 @@ static inline int ffs(int word)
|
|||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline unsigned long __fls(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __fls(unsigned long word)
|
||||
{
|
||||
return __flogr(word) ^ (BITS_PER_LONG - 1);
|
||||
}
|
||||
|
|
@ -221,7 +221,7 @@ static inline unsigned long __fls(unsigned long word)
|
|||
* set bit if value is nonzero. The last (most significant) bit is
|
||||
* at position 64.
|
||||
*/
|
||||
static inline int fls64(unsigned long word)
|
||||
static inline __attribute_const__ int fls64(unsigned long word)
|
||||
{
|
||||
unsigned long mask = 2 * BITS_PER_LONG - 1;
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ static inline int fls64(unsigned long word)
|
|||
* This is defined the same way as ffs.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
static inline int fls(unsigned int word)
|
||||
static inline __attribute_const__ int fls(unsigned int word)
|
||||
{
|
||||
return fls64(word);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <asm-generic/bitops/non-atomic.h>
|
||||
#endif
|
||||
|
||||
static inline unsigned long ffz(unsigned long word)
|
||||
static inline unsigned long __attribute_const__ ffz(unsigned long word)
|
||||
{
|
||||
unsigned long result;
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ static inline unsigned long ffz(unsigned long word)
|
|||
*
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline unsigned long __ffs(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long word)
|
||||
{
|
||||
unsigned long result;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ void set_bit(unsigned long nr, volatile unsigned long *addr);
|
|||
void clear_bit(unsigned long nr, volatile unsigned long *addr);
|
||||
void change_bit(unsigned long nr, volatile unsigned long *addr);
|
||||
|
||||
int fls(unsigned int word);
|
||||
int __fls(unsigned long word);
|
||||
int __attribute_const__ fls(unsigned int word);
|
||||
int __attribute_const__ __fls(unsigned long word);
|
||||
|
||||
#include <asm-generic/bitops/non-atomic.h>
|
||||
|
||||
|
|
@ -32,8 +32,8 @@ int __fls(unsigned long word);
|
|||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
int ffs(int x);
|
||||
unsigned long __ffs(unsigned long);
|
||||
int __attribute_const__ ffs(int x);
|
||||
unsigned long __attribute_const__ __ffs(unsigned long);
|
||||
|
||||
#include <asm-generic/bitops/ffz.h>
|
||||
#include <asm-generic/bitops/sched.h>
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ arch_test_bit_acquire(unsigned long nr, const volatile unsigned long *addr)
|
|||
variable_test_bit(nr, addr);
|
||||
}
|
||||
|
||||
static __always_inline unsigned long variable__ffs(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long variable__ffs(unsigned long word)
|
||||
{
|
||||
asm("tzcnt %1,%0"
|
||||
: "=r" (word)
|
||||
|
|
@ -265,7 +265,7 @@ static __always_inline unsigned long variable__ffs(unsigned long word)
|
|||
(unsigned long)__builtin_ctzl(word) : \
|
||||
variable__ffs(word))
|
||||
|
||||
static __always_inline unsigned long variable_ffz(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long variable_ffz(unsigned long word)
|
||||
{
|
||||
return variable__ffs(~word);
|
||||
}
|
||||
|
|
@ -287,7 +287,7 @@ static __always_inline unsigned long variable_ffz(unsigned long word)
|
|||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static __always_inline unsigned long __fls(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned long __fls(unsigned long word)
|
||||
{
|
||||
if (__builtin_constant_p(word))
|
||||
return BITS_PER_LONG - 1 - __builtin_clzl(word);
|
||||
|
|
@ -301,7 +301,7 @@ static __always_inline unsigned long __fls(unsigned long word)
|
|||
#undef ADDR
|
||||
|
||||
#ifdef __KERNEL__
|
||||
static __always_inline int variable_ffs(int x)
|
||||
static __always_inline __attribute_const__ int variable_ffs(int x)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -355,7 +355,7 @@ static __always_inline int variable_ffs(int x)
|
|||
* set bit if value is nonzero. The last (most significant) bit is
|
||||
* at position 32.
|
||||
*/
|
||||
static __always_inline int fls(unsigned int x)
|
||||
static __always_inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -400,7 +400,7 @@ static __always_inline int fls(unsigned int x)
|
|||
* at position 64.
|
||||
*/
|
||||
#ifdef CONFIG_X86_64
|
||||
static __always_inline int fls64(__u64 x)
|
||||
static __always_inline __attribute_const__ int fls64(__u64 x)
|
||||
{
|
||||
int bitpos = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ static inline unsigned long __cntlz (unsigned long x)
|
|||
* bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
|
||||
*/
|
||||
|
||||
static inline int ffz(unsigned long x)
|
||||
static inline int __attribute_const__ ffz(unsigned long x)
|
||||
{
|
||||
return 31 - __cntlz(~x & -~x);
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ static inline int ffz(unsigned long x)
|
|||
* __ffs: Find first bit set in word. Return 0 for bit 0
|
||||
*/
|
||||
|
||||
static inline unsigned long __ffs(unsigned long x)
|
||||
static inline __attribute_const__ unsigned long __ffs(unsigned long x)
|
||||
{
|
||||
return 31 - __cntlz(x & -x);
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ static inline unsigned long __ffs(unsigned long x)
|
|||
* differs in spirit from the above ffz (man ffs).
|
||||
*/
|
||||
|
||||
static inline int ffs(unsigned long x)
|
||||
static inline __attribute_const__ int ffs(unsigned long x)
|
||||
{
|
||||
return 32 - __cntlz(x & -x);
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ static inline int ffs(unsigned long x)
|
|||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
|
||||
static inline int fls (unsigned int x)
|
||||
static inline __attribute_const__ int fls (unsigned int x)
|
||||
{
|
||||
return 32 - __cntlz(x);
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ static inline int fls (unsigned int x)
|
|||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline unsigned long __fls(unsigned long word)
|
||||
static inline __attribute_const__ unsigned long __fls(unsigned long word)
|
||||
{
|
||||
return 31 - __cntlz(word);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5932,6 +5932,7 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
|
|||
{
|
||||
u16 v;
|
||||
int ret;
|
||||
unsigned int firstbit;
|
||||
struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
|
||||
|
||||
if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
|
||||
|
|
@ -5949,7 +5950,10 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
|
|||
rq = mps;
|
||||
}
|
||||
|
||||
v = FIELD_PREP(PCI_EXP_DEVCTL_READRQ, ffs(rq) - 8);
|
||||
firstbit = ffs(rq);
|
||||
if (firstbit < 8)
|
||||
return -EINVAL;
|
||||
v = FIELD_PREP(PCI_EXP_DEVCTL_READRQ, firstbit - 8);
|
||||
|
||||
if (bridge->no_inc_mrrs) {
|
||||
int max_mrrs = pcie_get_readrq(dev);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static __always_inline unsigned int generic___ffs(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned int generic___ffs(unsigned long word)
|
||||
{
|
||||
unsigned int num = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static __always_inline unsigned int generic___fls(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned int generic___fls(unsigned long word)
|
||||
{
|
||||
unsigned int num = BITS_PER_LONG - 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static __always_inline unsigned int __ffs(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned int __ffs(unsigned long word)
|
||||
{
|
||||
return __builtin_ctzl(word);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static __always_inline unsigned int __fls(unsigned long word)
|
||||
static __always_inline __attribute_const__ unsigned int __fls(unsigned long word)
|
||||
{
|
||||
return (sizeof(word) * 8) - 1 - __builtin_clzl(word);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
* This is defined the same way as ffs.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
static __always_inline int fls(unsigned int x)
|
||||
static __always_inline __attribute_const__ int fls(unsigned int x)
|
||||
{
|
||||
return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* the libc and compiler builtin ffs routines, therefore
|
||||
* differs in spirit from ffz (man ffs).
|
||||
*/
|
||||
static inline int generic_ffs(int x)
|
||||
static inline __attribute_const__ int generic_ffs(int x)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
|
||||
static __always_inline int generic_fls(unsigned int x)
|
||||
static __always_inline __attribute_const__ int generic_fls(unsigned int x)
|
||||
{
|
||||
int r = 32;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* at position 64.
|
||||
*/
|
||||
#if BITS_PER_LONG == 32
|
||||
static __always_inline int fls64(__u64 x)
|
||||
static __always_inline __attribute_const__ int fls64(__u64 x)
|
||||
{
|
||||
__u32 h = x >> 32;
|
||||
if (h)
|
||||
|
|
@ -24,7 +24,7 @@ static __always_inline int fls64(__u64 x)
|
|||
return fls(x);
|
||||
}
|
||||
#elif BITS_PER_LONG == 64
|
||||
static __always_inline int fls64(__u64 x)
|
||||
static __always_inline __attribute_const__ int fls64(__u64 x)
|
||||
{
|
||||
if (x == 0)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ static inline int parity8(u8 val)
|
|||
* The result is not defined if no bits are set, so check that @word
|
||||
* is non-zero before calling this.
|
||||
*/
|
||||
static inline unsigned int __ffs64(u64 word)
|
||||
static inline __attribute_const__ unsigned int __ffs64(u64 word)
|
||||
{
|
||||
#if BITS_PER_LONG == 32
|
||||
if (((u32)word) == 0UL)
|
||||
|
|
|
|||
|
|
@ -2479,6 +2479,20 @@ config STRING_HELPERS_KUNIT_TEST
|
|||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
|
||||
config FFS_KUNIT_TEST
|
||||
tristate "KUnit test ffs-family functions at runtime" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds KUnit tests for ffs-family bit manipulation functions
|
||||
including ffs(), __ffs(), fls(), __fls(), fls64(), and __ffs64().
|
||||
|
||||
These tests validate mathematical correctness, edge case handling,
|
||||
and cross-architecture consistency of bit scanning functions.
|
||||
|
||||
For more information on KUnit and unit tests in general,
|
||||
please refer to Documentation/dev-tools/kunit/.
|
||||
|
||||
config TEST_KSTRTOX
|
||||
tristate "Test kstrto*() family of functions at runtime"
|
||||
|
||||
|
|
|
|||
|
|
@ -15,28 +15,28 @@
|
|||
#include <linux/kernel.h>
|
||||
|
||||
int __weak __ctzsi2(int val);
|
||||
int __weak __ctzsi2(int val)
|
||||
int __weak __attribute_const__ __ctzsi2(int val)
|
||||
{
|
||||
return __ffs(val);
|
||||
}
|
||||
EXPORT_SYMBOL(__ctzsi2);
|
||||
|
||||
int __weak __clzsi2(int val);
|
||||
int __weak __clzsi2(int val)
|
||||
int __weak __attribute_const__ __clzsi2(int val)
|
||||
{
|
||||
return 32 - fls(val);
|
||||
}
|
||||
EXPORT_SYMBOL(__clzsi2);
|
||||
|
||||
int __weak __clzdi2(u64 val);
|
||||
int __weak __clzdi2(u64 val)
|
||||
int __weak __attribute_const__ __clzdi2(u64 val)
|
||||
{
|
||||
return 64 - fls64(val);
|
||||
}
|
||||
EXPORT_SYMBOL(__clzdi2);
|
||||
|
||||
int __weak __ctzdi2(u64 val);
|
||||
int __weak __ctzdi2(u64 val)
|
||||
int __weak __attribute_const__ __ctzdi2(u64 val)
|
||||
{
|
||||
return __ffs64(val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o
|
|||
obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o
|
||||
obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o
|
||||
obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o
|
||||
obj-$(CONFIG_FFS_KUNIT_TEST) += ffs_kunit.o
|
||||
CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced)
|
||||
CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-overread)
|
||||
CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-truncation)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,566 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* KUnit tests for ffs()-family functions
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*
|
||||
* Test data structures
|
||||
*/
|
||||
struct ffs_test_case {
|
||||
unsigned long input;
|
||||
int expected_ffs; /* ffs() result (1-based) */
|
||||
int expected_fls; /* fls() result (1-based) */
|
||||
const char *description;
|
||||
};
|
||||
|
||||
struct ffs64_test_case {
|
||||
u64 input;
|
||||
int expected_fls64; /* fls64() result (1-based) */
|
||||
unsigned int expected_ffs64_0based; /* __ffs64() result (0-based) */
|
||||
const char *description;
|
||||
};
|
||||
|
||||
/*
|
||||
* Basic edge cases - core functionality validation
|
||||
*/
|
||||
static const struct ffs_test_case basic_test_cases[] = {
|
||||
/* Zero case - special handling */
|
||||
{0x00000000, 0, 0, "zero value"},
|
||||
|
||||
/* Single bit patterns - powers of 2 */
|
||||
{0x00000001, 1, 1, "bit 0 set"},
|
||||
{0x00000002, 2, 2, "bit 1 set"},
|
||||
{0x00000004, 3, 3, "bit 2 set"},
|
||||
{0x00000008, 4, 4, "bit 3 set"},
|
||||
{0x00000010, 5, 5, "bit 4 set"},
|
||||
{0x00000020, 6, 6, "bit 5 set"},
|
||||
{0x00000040, 7, 7, "bit 6 set"},
|
||||
{0x00000080, 8, 8, "bit 7 set"},
|
||||
{0x00000100, 9, 9, "bit 8 set"},
|
||||
{0x00008000, 16, 16, "bit 15 set"},
|
||||
{0x00010000, 17, 17, "bit 16 set"},
|
||||
{0x40000000, 31, 31, "bit 30 set"},
|
||||
{0x80000000, 32, 32, "bit 31 set (sign bit)"},
|
||||
|
||||
/* Maximum values */
|
||||
{0xFFFFFFFF, 1, 32, "all bits set"},
|
||||
|
||||
/* Multiple bit patterns */
|
||||
{0x00000003, 1, 2, "bits 0-1 set"},
|
||||
{0x00000007, 1, 3, "bits 0-2 set"},
|
||||
{0x0000000F, 1, 4, "bits 0-3 set"},
|
||||
{0x000000FF, 1, 8, "bits 0-7 set"},
|
||||
{0x0000FFFF, 1, 16, "bits 0-15 set"},
|
||||
{0x7FFFFFFF, 1, 31, "bits 0-30 set"},
|
||||
|
||||
/* Sparse patterns */
|
||||
{0x00000101, 1, 9, "bits 0,8 set"},
|
||||
{0x00001001, 1, 13, "bits 0,12 set"},
|
||||
{0x80000001, 1, 32, "bits 0,31 set"},
|
||||
{0x40000002, 2, 31, "bits 1,30 set"},
|
||||
};
|
||||
|
||||
/*
|
||||
* 64-bit test cases
|
||||
*/
|
||||
static const struct ffs64_test_case ffs64_test_cases[] = {
|
||||
/* Zero case */
|
||||
{0x0000000000000000ULL, 0, 0, "zero value"},
|
||||
|
||||
/* Single bit patterns */
|
||||
{0x0000000000000001ULL, 1, 0, "bit 0 set"},
|
||||
{0x0000000000000002ULL, 2, 1, "bit 1 set"},
|
||||
{0x0000000000000004ULL, 3, 2, "bit 2 set"},
|
||||
{0x0000000000000008ULL, 4, 3, "bit 3 set"},
|
||||
{0x0000000000008000ULL, 16, 15, "bit 15 set"},
|
||||
{0x0000000000010000ULL, 17, 16, "bit 16 set"},
|
||||
{0x0000000080000000ULL, 32, 31, "bit 31 set"},
|
||||
{0x0000000100000000ULL, 33, 32, "bit 32 set"},
|
||||
{0x0000000200000000ULL, 34, 33, "bit 33 set"},
|
||||
{0x4000000000000000ULL, 63, 62, "bit 62 set"},
|
||||
{0x8000000000000000ULL, 64, 63, "bit 63 set (sign bit)"},
|
||||
|
||||
/* Maximum values */
|
||||
{0xFFFFFFFFFFFFFFFFULL, 64, 0, "all bits set"},
|
||||
|
||||
/* Cross 32-bit boundary patterns */
|
||||
{0x00000000FFFFFFFFULL, 32, 0, "lower 32 bits set"},
|
||||
{0xFFFFFFFF00000000ULL, 64, 32, "upper 32 bits set"},
|
||||
{0x8000000000000001ULL, 64, 0, "bits 0,63 set"},
|
||||
{0x4000000000000002ULL, 63, 1, "bits 1,62 set"},
|
||||
|
||||
/* Mixed patterns */
|
||||
{0x00000001FFFFFFFFULL, 33, 0, "bit 32 + lower 32 bits"},
|
||||
{0xFFFFFFFF80000000ULL, 64, 31, "upper 32 bits + bit 31"},
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function to validate ffs results with detailed error messages
|
||||
*/
|
||||
static void validate_ffs_result(struct kunit *test, unsigned long input,
|
||||
int actual, int expected, const char *func_name,
|
||||
const char *description)
|
||||
{
|
||||
KUNIT_EXPECT_EQ_MSG(test, actual, expected,
|
||||
"%s(0x%08lx) [%s]: expected %d, got %d",
|
||||
func_name, input, description, expected, actual);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to validate 64-bit ffs results
|
||||
*/
|
||||
static void validate_ffs64_result(struct kunit *test, u64 input,
|
||||
int actual, int expected, const char *func_name,
|
||||
const char *description)
|
||||
{
|
||||
KUNIT_EXPECT_EQ_MSG(test, actual, expected,
|
||||
"%s(0x%016llx) [%s]: expected %d, got %d",
|
||||
func_name, input, description, expected, actual);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to validate mathematical relationships between functions
|
||||
*/
|
||||
static void validate_ffs_relationships(struct kunit *test, unsigned long input)
|
||||
{
|
||||
int ffs_result;
|
||||
int fls_result;
|
||||
unsigned int ffs_0based;
|
||||
unsigned int fls_0based;
|
||||
|
||||
if (input == 0) {
|
||||
/* Special case: zero input */
|
||||
KUNIT_EXPECT_EQ(test, ffs(input), 0);
|
||||
KUNIT_EXPECT_EQ(test, fls(input), 0);
|
||||
/* __ffs and __fls are undefined for 0, but often return specific values */
|
||||
return;
|
||||
}
|
||||
|
||||
ffs_result = ffs(input);
|
||||
fls_result = fls(input);
|
||||
ffs_0based = __ffs(input);
|
||||
fls_0based = __fls(input);
|
||||
|
||||
/* Relationship: ffs(x) == __ffs(x) + 1 for x != 0 */
|
||||
KUNIT_EXPECT_EQ_MSG(test, ffs_result, ffs_0based + 1,
|
||||
"ffs(0x%08lx) != __ffs(0x%08lx) + 1: %d != %u + 1",
|
||||
input, input, ffs_result, ffs_0based);
|
||||
|
||||
/* Relationship: fls(x) == __fls(x) + 1 for x != 0 */
|
||||
KUNIT_EXPECT_EQ_MSG(test, fls_result, fls_0based + 1,
|
||||
"fls(0x%08lx) != __fls(0x%08lx) + 1: %d != %u + 1",
|
||||
input, input, fls_result, fls_0based);
|
||||
|
||||
/* Range validation */
|
||||
KUNIT_EXPECT_GE(test, ffs_result, 1);
|
||||
KUNIT_EXPECT_LE(test, ffs_result, BITS_PER_LONG);
|
||||
KUNIT_EXPECT_GE(test, fls_result, 1);
|
||||
KUNIT_EXPECT_LE(test, fls_result, BITS_PER_LONG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to validate 64-bit relationships
|
||||
*/
|
||||
static void validate_ffs64_relationships(struct kunit *test, u64 input)
|
||||
{
|
||||
int fls64_result;
|
||||
unsigned int ffs64_0based;
|
||||
|
||||
if (input == 0) {
|
||||
KUNIT_EXPECT_EQ(test, fls64(input), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
fls64_result = fls64(input);
|
||||
ffs64_0based = __ffs64(input);
|
||||
|
||||
/* Range validation */
|
||||
KUNIT_EXPECT_GE(test, fls64_result, 1);
|
||||
KUNIT_EXPECT_LE(test, fls64_result, 64);
|
||||
KUNIT_EXPECT_LT(test, ffs64_0based, 64);
|
||||
|
||||
/*
|
||||
* Relationships with 32-bit functions should hold for small values
|
||||
* on all architectures.
|
||||
*/
|
||||
if (input <= 0xFFFFFFFFULL) {
|
||||
unsigned long input_32 = (unsigned long)input;
|
||||
KUNIT_EXPECT_EQ_MSG(test, fls64(input), fls(input_32),
|
||||
"fls64(0x%llx) != fls(0x%lx): %d != %d",
|
||||
input, input_32, fls64(input), fls(input_32));
|
||||
|
||||
if (input != 0) {
|
||||
KUNIT_EXPECT_EQ_MSG(test, __ffs64(input), __ffs(input_32),
|
||||
"__ffs64(0x%llx) != __ffs(0x%lx): %lu != %lu",
|
||||
input, input_32,
|
||||
(unsigned long)__ffs64(input),
|
||||
(unsigned long)__ffs(input_32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test basic correctness of all ffs-family functions
|
||||
*/
|
||||
static void ffs_basic_correctness_test(struct kunit *test)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(basic_test_cases); i++) {
|
||||
const struct ffs_test_case *tc = &basic_test_cases[i];
|
||||
|
||||
/* Test ffs() */
|
||||
validate_ffs_result(test, tc->input, ffs(tc->input),
|
||||
tc->expected_ffs, "ffs", tc->description);
|
||||
|
||||
/* Test fls() */
|
||||
validate_ffs_result(test, tc->input, fls(tc->input),
|
||||
tc->expected_fls, "fls", tc->description);
|
||||
|
||||
/* Test __ffs() - skip zero case as it's undefined */
|
||||
if (tc->input != 0) {
|
||||
/* Calculate expected __ffs() result: __ffs(x) == ffs(x) - 1 */
|
||||
unsigned int expected_ffs_0based = tc->expected_ffs - 1;
|
||||
validate_ffs_result(test, tc->input, __ffs(tc->input),
|
||||
expected_ffs_0based, "__ffs", tc->description);
|
||||
}
|
||||
|
||||
/* Test __fls() - skip zero case as it's undefined */
|
||||
if (tc->input != 0) {
|
||||
/* Calculate expected __fls() result: __fls(x) == fls(x) - 1 */
|
||||
unsigned int expected_fls_0based = tc->expected_fls - 1;
|
||||
validate_ffs_result(test, tc->input, __fls(tc->input),
|
||||
expected_fls_0based, "__fls", tc->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test 64-bit function correctness
|
||||
*/
|
||||
static void ffs64_correctness_test(struct kunit *test)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ffs64_test_cases); i++) {
|
||||
const struct ffs64_test_case *tc = &ffs64_test_cases[i];
|
||||
|
||||
/* Test fls64() */
|
||||
validate_ffs64_result(test, tc->input, fls64(tc->input),
|
||||
tc->expected_fls64, "fls64", tc->description);
|
||||
|
||||
/* Test __ffs64() - skip zero case as it's undefined */
|
||||
if (tc->input != 0) {
|
||||
validate_ffs64_result(test, tc->input, __ffs64(tc->input),
|
||||
tc->expected_ffs64_0based, "__ffs64",
|
||||
tc->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test mathematical relationships between functions
|
||||
*/
|
||||
static void ffs_mathematical_relationships_test(struct kunit *test)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Test basic cases */
|
||||
for (i = 0; i < ARRAY_SIZE(basic_test_cases); i++) {
|
||||
validate_ffs_relationships(test, basic_test_cases[i].input);
|
||||
}
|
||||
|
||||
/* Test 64-bit cases */
|
||||
for (i = 0; i < ARRAY_SIZE(ffs64_test_cases); i++) {
|
||||
validate_ffs64_relationships(test, ffs64_test_cases[i].input);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test edge cases and boundary conditions
|
||||
*/
|
||||
static void ffs_edge_cases_test(struct kunit *test)
|
||||
{
|
||||
unsigned long test_patterns[] = {
|
||||
/* Powers of 2 */
|
||||
1UL, 2UL, 4UL, 8UL, 16UL, 32UL, 64UL, 128UL,
|
||||
256UL, 512UL, 1024UL, 2048UL, 4096UL, 8192UL,
|
||||
|
||||
/* Powers of 2 minus 1 */
|
||||
1UL, 3UL, 7UL, 15UL, 31UL, 63UL, 127UL, 255UL,
|
||||
511UL, 1023UL, 2047UL, 4095UL, 8191UL,
|
||||
|
||||
/* Boundary values */
|
||||
0x7FFFFFFFUL, /* Maximum positive 32-bit */
|
||||
0x80000000UL, /* Minimum negative 32-bit */
|
||||
0xFFFFFFFFUL, /* Maximum 32-bit unsigned */
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_patterns); i++) {
|
||||
validate_ffs_relationships(test, test_patterns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test 64-bit edge cases
|
||||
*/
|
||||
static void ffs64_edge_cases_test(struct kunit *test)
|
||||
{
|
||||
u64 test_patterns_64[] = {
|
||||
/* 64-bit powers of 2 */
|
||||
0x0000000100000000ULL, /* 2^32 */
|
||||
0x0000000200000000ULL, /* 2^33 */
|
||||
0x0000000400000000ULL, /* 2^34 */
|
||||
0x0000001000000000ULL, /* 2^36 */
|
||||
0x0000010000000000ULL, /* 2^40 */
|
||||
0x0001000000000000ULL, /* 2^48 */
|
||||
0x0100000000000000ULL, /* 2^56 */
|
||||
0x4000000000000000ULL, /* 2^62 */
|
||||
0x8000000000000000ULL, /* 2^63 */
|
||||
|
||||
/* Cross-boundary patterns */
|
||||
0x00000000FFFFFFFFULL, /* Lower 32 bits */
|
||||
0xFFFFFFFF00000000ULL, /* Upper 32 bits */
|
||||
0x7FFFFFFFFFFFFFFFULL, /* Maximum positive 64-bit */
|
||||
0xFFFFFFFFFFFFFFFFULL, /* Maximum 64-bit unsigned */
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_patterns_64); i++) {
|
||||
validate_ffs64_relationships(test, test_patterns_64[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ffz() test data - Find First Zero bit test cases
|
||||
*/
|
||||
struct ffz_test_case {
|
||||
unsigned long input;
|
||||
unsigned long expected_ffz;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
static const struct ffz_test_case ffz_test_cases[] = {
|
||||
/* Zero bits in specific positions */
|
||||
{0xFFFFFFFE, 0, "bit 0 is zero"}, /* ...11111110 */
|
||||
{0xFFFFFFFD, 1, "bit 1 is zero"}, /* ...11111101 */
|
||||
{0xFFFFFFFB, 2, "bit 2 is zero"}, /* ...11111011 */
|
||||
{0xFFFFFFF7, 3, "bit 3 is zero"}, /* ...11110111 */
|
||||
{0xFFFFFFEF, 4, "bit 4 is zero"}, /* ...11101111 */
|
||||
{0xFFFFFFDF, 5, "bit 5 is zero"}, /* ...11011111 */
|
||||
{0xFFFFFFBF, 6, "bit 6 is zero"}, /* ...10111111 */
|
||||
{0xFFFFFF7F, 7, "bit 7 is zero"}, /* ...01111111 */
|
||||
{0xFFFFFEFF, 8, "bit 8 is zero"}, /* Gap in bit 8 */
|
||||
{0xFFFF7FFF, 15, "bit 15 is zero"}, /* Gap in bit 15 */
|
||||
{0xFFFEFFFF, 16, "bit 16 is zero"}, /* Gap in bit 16 */
|
||||
{0xBFFFFFFF, 30, "bit 30 is zero"}, /* Gap in bit 30 */
|
||||
{0x7FFFFFFF, 31, "bit 31 is zero"}, /* 01111111... */
|
||||
|
||||
/* Multiple zero patterns */
|
||||
{0xFFFFFFFC, 0, "bits 0-1 are zero"}, /* ...11111100 */
|
||||
{0xFFFFFFF8, 0, "bits 0-2 are zero"}, /* ...11111000 */
|
||||
{0xFFFFFFF0, 0, "bits 0-3 are zero"}, /* ...11110000 */
|
||||
{0xFFFFFF00, 0, "bits 0-7 are zero"}, /* ...00000000 */
|
||||
{0xFFFF0000, 0, "bits 0-15 are zero"}, /* Lower 16 bits zero */
|
||||
|
||||
/* All zeros (special case) */
|
||||
{0x00000000, 0, "all bits zero"},
|
||||
|
||||
/* Complex patterns */
|
||||
{0xFFFDFFFF, 17, "bit 17 is zero"}, /* Gap in bit 17 */
|
||||
{0xFFF7FFFF, 19, "bit 19 is zero"}, /* Gap in bit 19 */
|
||||
{0xF7FFFFFF, 27, "bit 27 is zero"}, /* Gap in bit 27 */
|
||||
{0xDFFFFFFF, 29, "bit 29 is zero"}, /* Gap in bit 29 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Test basic correctness of ffz() function
|
||||
*/
|
||||
static void ffz_basic_correctness_test(struct kunit *test)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ffz_test_cases); i++) {
|
||||
const struct ffz_test_case *tc = &ffz_test_cases[i];
|
||||
unsigned long result = ffz(tc->input);
|
||||
|
||||
KUNIT_EXPECT_EQ_MSG(test, result, tc->expected_ffz,
|
||||
"ffz(0x%08lx) [%s]: expected %lu, got %lu",
|
||||
tc->input, tc->description, tc->expected_ffz, result);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test mathematical relationships between ffz() and other functions
|
||||
*/
|
||||
static void validate_ffz_relationships(struct kunit *test, unsigned long input)
|
||||
{
|
||||
unsigned long ffz_result;
|
||||
|
||||
if (input == 0) {
|
||||
/* ffz(0) should return 0 (first zero bit is at position 0) */
|
||||
KUNIT_EXPECT_EQ(test, ffz(input), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (input == ~0UL) {
|
||||
/* ffz(~0) is undefined (no zero bits) - just verify it doesn't crash */
|
||||
ffz_result = ffz(input);
|
||||
/* Implementation-defined behavior, just ensure it completes */
|
||||
return;
|
||||
}
|
||||
|
||||
ffz_result = ffz(input);
|
||||
|
||||
/* Range validation - result should be within valid bit range */
|
||||
KUNIT_EXPECT_LT(test, ffz_result, BITS_PER_LONG);
|
||||
|
||||
/* Verify the bit at ffz_result position is actually zero */
|
||||
KUNIT_EXPECT_EQ_MSG(test, (input >> ffz_result) & 1, 0,
|
||||
"ffz(0x%08lx) = %lu, but bit %lu is not zero",
|
||||
input, ffz_result, ffz_result);
|
||||
|
||||
/* Core relationship: if we set the ffz bit, ffz should find a different bit */
|
||||
if (ffz_result < BITS_PER_LONG - 1) {
|
||||
unsigned long modified = input | (1UL << ffz_result);
|
||||
if (modified != ~0UL) { /* Skip if all bits would be set */
|
||||
unsigned long new_ffz = ffz(modified);
|
||||
KUNIT_EXPECT_NE_MSG(test, new_ffz, ffz_result,
|
||||
"ffz(0x%08lx) = %lu, but setting that bit doesn't change ffz result",
|
||||
input, ffz_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ffz_mathematical_relationships_test(struct kunit *test)
|
||||
{
|
||||
unsigned long test_patterns[] = {
|
||||
/* Powers of 2 with one bit clear */
|
||||
0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFB, 0xFFFFFFF7,
|
||||
0xFFFFFFEF, 0xFFFFFFDF, 0xFFFFFFBF, 0xFFFFFF7F,
|
||||
|
||||
/* Multiple patterns */
|
||||
0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, 0xFFF00000,
|
||||
0x7FFFFFFF, 0x3FFFFFFF, 0x1FFFFFFF, 0x0FFFFFFF,
|
||||
|
||||
/* Complex bit patterns */
|
||||
0xAAAAAAAA, 0x55555555, 0xCCCCCCCC, 0x33333333,
|
||||
0xF0F0F0F0, 0x0F0F0F0F, 0xFF00FF00, 0x00FF00FF,
|
||||
};
|
||||
int i;
|
||||
|
||||
/* Test basic test cases */
|
||||
for (i = 0; i < ARRAY_SIZE(ffz_test_cases); i++) {
|
||||
validate_ffz_relationships(test, ffz_test_cases[i].input);
|
||||
}
|
||||
|
||||
/* Test additional patterns */
|
||||
for (i = 0; i < ARRAY_SIZE(test_patterns); i++) {
|
||||
validate_ffz_relationships(test, test_patterns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test edge cases and boundary conditions for ffz()
|
||||
*/
|
||||
static void ffz_edge_cases_test(struct kunit *test)
|
||||
{
|
||||
unsigned long edge_patterns[] = {
|
||||
/* Boundary values */
|
||||
0x00000000, /* All zeros */
|
||||
0x80000000, /* Only MSB set */
|
||||
0x00000001, /* Only LSB set */
|
||||
0x7FFFFFFF, /* MSB clear */
|
||||
0xFFFFFFFE, /* LSB clear */
|
||||
|
||||
/* Powers of 2 complement patterns (one zero bit each) */
|
||||
~(1UL << 0), ~(1UL << 1), ~(1UL << 2), ~(1UL << 3),
|
||||
~(1UL << 4), ~(1UL << 8), ~(1UL << 16), ~(1UL << 31),
|
||||
|
||||
/* Walking zero patterns */
|
||||
0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFB, 0xFFFFFFF7,
|
||||
0xFFFFFFEF, 0xFFFFFFDF, 0xFFFFFFBF, 0xFFFFFF7F,
|
||||
0xFFFFFEFF, 0xFFFFFDFF, 0xFFFFFBFF, 0xFFFFF7FF,
|
||||
|
||||
/* Multiple zeros */
|
||||
0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, 0xFFF00000,
|
||||
0xFF000000, 0xF0000000, 0x00000000,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(edge_patterns); i++) {
|
||||
validate_ffz_relationships(test, edge_patterns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* To have useful build error output, split the tests into separate
|
||||
* functions so it's clear which are missing __attribute_const__.
|
||||
*/
|
||||
#define CREATE_WRAPPER(func) \
|
||||
static noinline bool build_test_##func(void) \
|
||||
{ \
|
||||
int init_##func = 32; \
|
||||
int result_##func = func(6); \
|
||||
\
|
||||
/* Does the static initializer vanish after calling func? */ \
|
||||
BUILD_BUG_ON(init_##func < 32); \
|
||||
\
|
||||
/* "Consume" the results so optimizer doesn't drop them. */ \
|
||||
barrier_data(&init_##func); \
|
||||
barrier_data(&result_##func); \
|
||||
\
|
||||
return true; \
|
||||
}
|
||||
CREATE_WRAPPER(ffs)
|
||||
CREATE_WRAPPER(fls)
|
||||
CREATE_WRAPPER(__ffs)
|
||||
CREATE_WRAPPER(__fls)
|
||||
CREATE_WRAPPER(ffz)
|
||||
#undef CREATE_WRAPPER
|
||||
|
||||
/*
|
||||
* Make sure that __attribute_const__ has be applied to all the
|
||||
* functions. This is a regression test for:
|
||||
* https://github.com/KSPP/linux/issues/364
|
||||
*/
|
||||
static void ffs_attribute_const_test(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_TRUE(test, build_test_ffs());
|
||||
KUNIT_EXPECT_TRUE(test, build_test_fls());
|
||||
KUNIT_EXPECT_TRUE(test, build_test___ffs());
|
||||
KUNIT_EXPECT_TRUE(test, build_test___fls());
|
||||
KUNIT_EXPECT_TRUE(test, build_test_ffz());
|
||||
}
|
||||
|
||||
/*
|
||||
* KUnit test case definitions
|
||||
*/
|
||||
static struct kunit_case ffs_test_cases[] = {
|
||||
KUNIT_CASE(ffs_basic_correctness_test),
|
||||
KUNIT_CASE(ffs64_correctness_test),
|
||||
KUNIT_CASE(ffs_mathematical_relationships_test),
|
||||
KUNIT_CASE(ffs_edge_cases_test),
|
||||
KUNIT_CASE(ffs64_edge_cases_test),
|
||||
KUNIT_CASE(ffz_basic_correctness_test),
|
||||
KUNIT_CASE(ffz_mathematical_relationships_test),
|
||||
KUNIT_CASE(ffz_edge_cases_test),
|
||||
KUNIT_CASE(ffs_attribute_const_test),
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* KUnit test suite definition
|
||||
*/
|
||||
static struct kunit_suite ffs_test_suite = {
|
||||
.name = "ffs",
|
||||
.test_cases = ffs_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&ffs_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit tests for ffs()-family functions");
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Reference in New Issue