bitmap updates for v7.1

- new API: bitmap_weight_from() and bitmap_weighted_xor() (Yury);
  - drop unused __find_nth_andnot_bit() (Yury);
  - new tests and test improvements (Andy, Akinobu, Yury);
  - fixes for count_zeroes API (Yury);
  - cleanup bitmap_print_to_pagebuf() mess (Yury);
  - documentation updates (Andy, Kai, Kit).
 -----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEEi8GdvG6xMhdgpu/4sUSA/TofvsgFAmnb8vkACgkQsUSA/Tof
 vsjzKgv/RI6HDkwRgjT/jPVAZzaNFrdoL0nIQ1ZriyE70b/0HtjMzbQBO0P3Vmsa
 5k13Nus0eBi9CeEAK0NvjQXy8NRj4E7favqF3faV7l4+J6STHpOKeHZglUAj00CG
 +23WGInz+TS5RBjXnvT00wuTAVQjT6dvYng9606psVDF/nlh8ZtXmYDjLauseoUH
 a1EEKwLGXbk3/MhDgVq/R5RvZoNscL4Hky7QWMZiqLutwF8EDrZotF142tfbxkmW
 mu+2Bn1W66F+8A42HJBDRevcuvsRzMggP2kXxDk50XNL1zTN9f/4iE0r+/5x8UVF
 s3WiGnuLSkRIK4osey12Z9BAtGJTn3gTPvIPYOWvRiJHskOa1yvGSgcvmzc53x0Q
 FZgDq1JkBDsF3OZceSjGIp9QOqg+YJArlzun+mNxLbfnahEbhx21Z/ls65vLJCae
 ENIPAzet5Fxa8mZeJIyiV0zR05DcV+g64FOhcGJ7al4fRWtYVP8qa9FAyGFMV4L2
 JL4xHuRO
 =pEBo
 -----END PGP SIGNATURE-----

Merge tag 'bitmap-for-v7.1' of https://github.com/norov/linux

Pull bitmap updates from Yury Norov:

 - new API: bitmap_weight_from() and bitmap_weighted_xor() (Yury)

 - drop unused __find_nth_andnot_bit() (Yury)

 - new tests and test improvements (Andy, Akinobu, Yury)

 - fixes for count_zeroes API (Yury)

 - cleanup bitmap_print_to_pagebuf() mess (Yury)

 - documentation updates (Andy, Kai, Kit).

* tag 'bitmap-for-v7.1' of https://github.com/norov/linux: (24 commits)
  bitops: Update kernel-doc for sign_extendXX()
  powerpc/xive: simplify xive_spapr_debug_show()
  thermal: intel: switch cpumask_get() to using cpumask_print_to_pagebuf()
  coresight: don't use bitmap_print_to_pagebuf()
  lib/prime_numbers: drop temporary buffer in dump_primes()
  drm/xe: switch xe_pagefault_queue_init() to using bitmap_weighted_or()
  ice: use bitmap_empty() in ice_vf_has_no_qs_ena
  ice: use bitmap_weighted_xor() in ice_find_free_recp_res_idx()
  bitmap: introduce bitmap_weighted_xor()
  bitmap: add test_zero_nbits()
  bitmap: exclude nbits == 0 cases from bitmap test
  bitmap: test bitmap_weight() for more
  asm-generic/bitops: Fix a comment typo in instrumented-atomic.h
  bitops: fix kernel-doc parameter name for parity8()
  lib: count_zeros: unify count_{leading,trailing}_zeros()
  lib: count_zeros: fix 32/64-bit inconsistency in count_trailing_zeros()
  lib: crypto: fix comments for count_leading_zeros()
  x86/topology: use bitmap_weight_from()
  bitmap: add bitmap_weight_from()
  lib/find_bit_benchmark: avoid clearing randomly filled bitmap in test_find_first_bit()
  ...
master
Linus Torvalds 2026-04-14 08:55:18 -07:00
commit a970ed1881
19 changed files with 230 additions and 97 deletions

View File

@ -16,7 +16,7 @@ static void eiointc_set_sw_coreisr(struct loongarch_eiointc *s)
ipnum = (s->ipmap >> (irq / 32 * 8)) & 0xff; ipnum = (s->ipmap >> (irq / 32 * 8)) & 0xff;
if (!(s->status & BIT(EIOINTC_ENABLE_INT_ENCODE))) { if (!(s->status & BIT(EIOINTC_ENABLE_INT_ENCODE))) {
ipnum = count_trailing_zeros(ipnum); ipnum = count_trailing_zeros(ipnum);
ipnum = (ipnum >= 0 && ipnum < 4) ? ipnum : 0; ipnum = ipnum < 4 ? ipnum : 0;
} }
cpuid = ((u8 *)s->coremap)[irq]; cpuid = ((u8 *)s->coremap)[irq];
@ -41,7 +41,7 @@ static void eiointc_update_irq(struct loongarch_eiointc *s, int irq, int level)
ipnum = (s->ipmap >> (irq / 32 * 8)) & 0xff; ipnum = (s->ipmap >> (irq / 32 * 8)) & 0xff;
if (!(s->status & BIT(EIOINTC_ENABLE_INT_ENCODE))) { if (!(s->status & BIT(EIOINTC_ENABLE_INT_ENCODE))) {
ipnum = count_trailing_zeros(ipnum); ipnum = count_trailing_zeros(ipnum);
ipnum = (ipnum >= 0 && ipnum < 4) ? ipnum : 0; ipnum = ipnum < 4 ? ipnum : 0;
} }
cpu = s->sw_coremap[irq]; cpu = s->sw_coremap[irq];

View File

@ -667,17 +667,9 @@ static void xive_spapr_sync_source(u32 hw_irq)
static int xive_spapr_debug_show(struct seq_file *m, void *private) static int xive_spapr_debug_show(struct seq_file *m, void *private)
{ {
struct xive_irq_bitmap *xibm; struct xive_irq_bitmap *xibm;
char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf) list_for_each_entry(xibm, &xive_irq_bitmaps, list)
return -ENOMEM; seq_printf(m, "bitmap #%d: %*pbl\n", xibm->count, xibm->count, xibm->bitmap);
list_for_each_entry(xibm, &xive_irq_bitmaps, list) {
memset(buf, 0, PAGE_SIZE);
bitmap_print_to_pagebuf(true, buf, xibm->bitmap, xibm->count);
seq_printf(m, "bitmap #%d: %s", xibm->count, buf);
}
kfree(buf);
return 0; return 0;
} }

View File

@ -204,15 +204,11 @@ fwbug:
static unsigned int topo_unit_count(u32 lvlid, enum x86_topology_domains at_level, static unsigned int topo_unit_count(u32 lvlid, enum x86_topology_domains at_level,
unsigned long *map) unsigned long *map)
{ {
unsigned int id, end, cnt = 0; unsigned int end;
/* Calculate the exclusive end */ /* Calculate the exclusive end */
end = lvlid + (1U << x86_topo_system.dom_shifts[at_level]); end = lvlid + (1U << x86_topo_system.dom_shifts[at_level]);
return bitmap_weight_from(map, lvlid, end);
/* Unfortunately there is no bitmap_weight_range() */
for (id = find_next_bit(map, end, lvlid); id < end; id = find_next_bit(map, end, ++id))
cnt++;
return cnt;
} }
static __init void topo_register_apic(u32 apic_id, u32 acpi_id, bool present) static __init void topo_register_apic(u32 apic_id, u32 acpi_id, bool present)

View File

@ -291,10 +291,9 @@ static int xe_pagefault_queue_init(struct xe_device *xe,
xe_dss_mask_t all_dss; xe_dss_mask_t all_dss;
int num_dss, num_eus; int num_dss, num_eus;
bitmap_or(all_dss, gt->fuse_topo.g_dss_mask, num_dss = bitmap_weighted_or(all_dss, gt->fuse_topo.g_dss_mask,
gt->fuse_topo.c_dss_mask, XE_MAX_DSS_FUSE_BITS); gt->fuse_topo.c_dss_mask, XE_MAX_DSS_FUSE_BITS);
num_dss = bitmap_weight(all_dss, XE_MAX_DSS_FUSE_BITS);
num_eus = bitmap_weight(gt->fuse_topo.eu_mask_per_dss, num_eus = bitmap_weight(gt->fuse_topo.eu_mask_per_dss,
XE_MAX_EU_FUSE_BITS) * num_dss; XE_MAX_EU_FUSE_BITS) * num_dss;

View File

@ -606,14 +606,11 @@ static ssize_t chan_gate_enable_show(struct device *dev,
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config; struct cti_config *cfg = &drvdata->config;
unsigned long ctigate_bitmask = cfg->ctigate; unsigned long ctigate_bitmask = cfg->ctigate;
int size = 0;
if (cfg->ctigate == 0) if (cfg->ctigate == 0)
size = sprintf(buf, "\n"); return sprintf(buf, "\n");
else
size = bitmap_print_to_pagebuf(true, buf, &ctigate_bitmask, return sysfs_emit(buf, "%*pbl\n", cfg->nr_ctm_channels, &ctigate_bitmask);
cfg->nr_ctm_channels);
return size;
} }
static DEVICE_ATTR_RW(chan_gate_enable); static DEVICE_ATTR_RW(chan_gate_enable);
@ -710,12 +707,13 @@ static ssize_t trigout_filtered_show(struct device *dev,
{ {
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config; struct cti_config *cfg = &drvdata->config;
int size = 0, nr_trig_max = cfg->nr_trig_max; int nr_trig_max = cfg->nr_trig_max;
unsigned long mask = cfg->trig_out_filter; unsigned long mask = cfg->trig_out_filter;
if (mask) if (mask == 0)
size = bitmap_print_to_pagebuf(true, buf, &mask, nr_trig_max); return 0;
return size;
return sysfs_emit(buf, "%*pbl\n", nr_trig_max, &mask);
} }
static DEVICE_ATTR_RO(trigout_filtered); static DEVICE_ATTR_RO(trigout_filtered);
@ -834,7 +832,7 @@ static ssize_t print_chan_list(struct device *dev,
{ {
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config; struct cti_config *config = &drvdata->config;
int size, i; int i;
unsigned long inuse_bits = 0, chan_mask; unsigned long inuse_bits = 0, chan_mask;
/* scan regs to get bitmap of channels in use. */ /* scan regs to get bitmap of channels in use. */
@ -852,11 +850,9 @@ static ssize_t print_chan_list(struct device *dev,
/* list of channels, or 'none' */ /* list of channels, or 'none' */
chan_mask = GENMASK(config->nr_ctm_channels - 1, 0); chan_mask = GENMASK(config->nr_ctm_channels - 1, 0);
if (inuse_bits & chan_mask) if (inuse_bits & chan_mask)
size = bitmap_print_to_pagebuf(true, buf, &inuse_bits, return sysfs_emit(buf, "%*pbl\n", config->nr_ctm_channels, &inuse_bits);
config->nr_ctm_channels);
else return sprintf(buf, "\n");
size = sprintf(buf, "\n");
return size;
} }
static ssize_t chan_inuse_show(struct device *dev, static ssize_t chan_inuse_show(struct device *dev,
@ -928,7 +924,7 @@ static ssize_t trigin_sig_show(struct device *dev,
struct cti_config *cfg = &drvdata->config; struct cti_config *cfg = &drvdata->config;
unsigned long mask = con->con_in->used_mask; unsigned long mask = con->con_in->used_mask;
return bitmap_print_to_pagebuf(true, buf, &mask, cfg->nr_trig_max); return sysfs_emit(buf, "%*pbl\n", cfg->nr_trig_max, &mask);
} }
static ssize_t trigout_sig_show(struct device *dev, static ssize_t trigout_sig_show(struct device *dev,
@ -942,7 +938,7 @@ static ssize_t trigout_sig_show(struct device *dev,
struct cti_config *cfg = &drvdata->config; struct cti_config *cfg = &drvdata->config;
unsigned long mask = con->con_out->used_mask; unsigned long mask = con->con_out->used_mask;
return bitmap_print_to_pagebuf(true, buf, &mask, cfg->nr_trig_max); return sysfs_emit(buf, "%*pbl\n", cfg->nr_trig_max, &mask);
} }
/* convert a sig type id to a name */ /* convert a sig type id to a name */

View File

@ -4984,10 +4984,8 @@ ice_find_free_recp_res_idx(struct ice_hw *hw, const unsigned long *profiles,
hw->switch_info->recp_list[bit].res_idxs, hw->switch_info->recp_list[bit].res_idxs,
ICE_MAX_FV_WORDS); ICE_MAX_FV_WORDS);
bitmap_xor(free_idx, used_idx, possible_idx, ICE_MAX_FV_WORDS);
/* return number of free indexes */ /* return number of free indexes */
return (u16)bitmap_weight(free_idx, ICE_MAX_FV_WORDS); return (u16)bitmap_weighted_xor(free_idx, used_idx, possible_idx, ICE_MAX_FV_WORDS);
} }
/** /**

View File

@ -1210,8 +1210,8 @@ bool ice_is_vf_trusted(struct ice_vf *vf)
*/ */
bool ice_vf_has_no_qs_ena(struct ice_vf *vf) bool ice_vf_has_no_qs_ena(struct ice_vf *vf)
{ {
return (!bitmap_weight(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF) && return bitmap_empty(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF) &&
!bitmap_weight(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF)); bitmap_empty(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF);
} }
/** /**

View File

@ -200,8 +200,7 @@ static int cpumask_get(char *buf, const struct kernel_param *kp)
if (!cpumask_available(idle_injection_cpu_mask)) if (!cpumask_available(idle_injection_cpu_mask))
return -ENODEV; return -ENODEV;
return bitmap_print_to_pagebuf(false, buf, cpumask_bits(idle_injection_cpu_mask), return cpumap_print_to_pagebuf(false, buf, idle_injection_cpu_mask);
nr_cpumask_bits);
} }
static const struct kernel_param_ops cpumask_ops = { static const struct kernel_param_ops cpumask_ops = {

View File

@ -100,4 +100,4 @@ static __always_inline bool test_and_change_bit(long nr, volatile unsigned long
return arch_test_and_change_bit(nr, addr); return arch_test_and_change_bit(nr, addr);
} }
#endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H */ #endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_ATOMIC_H */

View File

@ -46,6 +46,7 @@ struct device;
* bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2 * bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2
* bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2 * bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2
* bitmap_weighted_or(dst, src1, src2, nbits) *dst = *src1 | *src2. Returns Hamming Weight of dst * bitmap_weighted_or(dst, src1, src2, nbits) *dst = *src1 | *src2. Returns Hamming Weight of dst
* bitmap_weighted_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2. Returns Hamming Weight of dst
* bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2 * bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2
* bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2)
* bitmap_complement(dst, src, nbits) *dst = ~(*src) * bitmap_complement(dst, src, nbits) *dst = ~(*src)
@ -57,6 +58,7 @@ struct device;
* bitmap_weight(src, nbits) Hamming Weight: number set bits * bitmap_weight(src, nbits) Hamming Weight: number set bits
* bitmap_weight_and(src1, src2, nbits) Hamming Weight of and'ed bitmap * bitmap_weight_and(src1, src2, nbits) Hamming Weight of and'ed bitmap
* bitmap_weight_andnot(src1, src2, nbits) Hamming Weight of andnot'ed bitmap * bitmap_weight_andnot(src1, src2, nbits) Hamming Weight of andnot'ed bitmap
* bitmap_weight_from(src, start, end) Hamming Weight starting from @start
* bitmap_set(dst, pos, nbits) Set specified bit area * bitmap_set(dst, pos, nbits) Set specified bit area
* bitmap_clear(dst, pos, nbits) Clear specified bit area * bitmap_clear(dst, pos, nbits) Clear specified bit area
* bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area
@ -168,6 +170,8 @@ void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int nbits); const unsigned long *bitmap2, unsigned int nbits);
unsigned int __bitmap_weighted_or(unsigned long *dst, const unsigned long *bitmap1, unsigned int __bitmap_weighted_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int nbits); const unsigned long *bitmap2, unsigned int nbits);
unsigned int __bitmap_weighted_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int nbits);
void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int nbits); const unsigned long *bitmap2, unsigned int nbits);
bool __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, bool __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
@ -352,6 +356,18 @@ unsigned int bitmap_weighted_or(unsigned long *dst, const unsigned long *src1,
} }
} }
static __always_inline
unsigned int bitmap_weighted_xor(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, unsigned int nbits)
{
if (small_const_nbits(nbits)) {
*dst = *src1 ^ *src2;
return hweight_long(*dst & BITMAP_LAST_WORD_MASK(nbits));
} else {
return __bitmap_weighted_xor(dst, src1, src2, nbits);
}
}
static __always_inline static __always_inline
void bitmap_xor(unsigned long *dst, const unsigned long *src1, void bitmap_xor(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, unsigned int nbits) const unsigned long *src2, unsigned int nbits)
@ -479,6 +495,38 @@ unsigned long bitmap_weight_andnot(const unsigned long *src1,
return __bitmap_weight_andnot(src1, src2, nbits); return __bitmap_weight_andnot(src1, src2, nbits);
} }
/**
* bitmap_weight_from - Hamming weight for a memory region
* @bitmap: The base address
* @start: The bitnumber to starts weighting
* @end: the bitmap size in bits
*
* Returns the number of set bits in the region. If @start >= @end,
* return >= end.
*/
static __always_inline
unsigned long bitmap_weight_from(const unsigned long *bitmap,
unsigned int start, unsigned int end)
{
unsigned long w;
if (unlikely(start >= end))
return end;
if (small_const_nbits(end))
return hweight_long(*bitmap & GENMASK(end - 1, start));
bitmap += start / BITS_PER_LONG;
/* Opencode round_down() to not include math.h */
end -= start & ~(BITS_PER_LONG - 1);
start %= BITS_PER_LONG;
w = bitmap_weight(bitmap, end);
if (start)
w -= hweight_long(*bitmap & BITMAP_LAST_WORD_MASK(start));
return w;
}
static __always_inline static __always_inline
void bitmap_set(unsigned long *map, unsigned int start, unsigned int nbits) void bitmap_set(unsigned long *map, unsigned int start, unsigned int nbits)
{ {

View File

@ -179,9 +179,11 @@ static inline __u8 ror8(__u8 word, unsigned int shift)
/** /**
* sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit * sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit
* @value: value to sign extend * @value: value to sign extend
* @index: 0 based bit index (0<=index<32) to sign bit * @index: 0 based bit index (0 <= index < 32) to sign bit
* *
* This is safe to use for 16- and 8-bit types as well. * This is safe to use for 16- and 8-bit types as well.
*
* Return: 32-bit sign extended value
*/ */
static __always_inline __s32 sign_extend32(__u32 value, int index) static __always_inline __s32 sign_extend32(__u32 value, int index)
{ {
@ -192,7 +194,11 @@ static __always_inline __s32 sign_extend32(__u32 value, int index)
/** /**
* sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit
* @value: value to sign extend * @value: value to sign extend
* @index: 0 based bit index (0<=index<64) to sign bit * @index: 0 based bit index (0 <= index < 64) to sign bit
*
* This is safe to use for 32-, 16- and 8-bit types as well.
*
* Return: 64-bit sign extended value
*/ */
static __always_inline __s64 sign_extend64(__u64 value, int index) static __always_inline __s64 sign_extend64(__u64 value, int index)
{ {
@ -230,7 +236,7 @@ static inline int get_count_order_long(unsigned long l)
/** /**
* parity8 - get the parity of an u8 value * parity8 - get the parity of an u8 value
* @value: the value to be examined * @val: the value to be examined
* *
* Determine the parity of the u8 argument. * Determine the parity of the u8 argument.
* *

View File

@ -18,7 +18,7 @@
* *
* If the MSB of @x is set, the result is 0. * If the MSB of @x is set, the result is 0.
* If only the LSB of @x is set, then the result is BITS_PER_LONG-1. * If only the LSB of @x is set, then the result is BITS_PER_LONG-1.
* If @x is 0 then the result is COUNT_LEADING_ZEROS_0. * If @x is 0 then the result is BITS_PER_LONG.
*/ */
static inline int count_leading_zeros(unsigned long x) static inline int count_leading_zeros(unsigned long x)
{ {
@ -28,8 +28,6 @@ static inline int count_leading_zeros(unsigned long x)
return BITS_PER_LONG - fls64(x); return BITS_PER_LONG - fls64(x);
} }
#define COUNT_LEADING_ZEROS_0 BITS_PER_LONG
/** /**
* count_trailing_zeros - Count the number of zeros from the LSB forwards * count_trailing_zeros - Count the number of zeros from the LSB forwards
* @x: The value * @x: The value
@ -38,16 +36,11 @@ static inline int count_leading_zeros(unsigned long x)
* *
* If the LSB of @x is set, the result is 0. * If the LSB of @x is set, the result is 0.
* If only the MSB of @x is set, then the result is BITS_PER_LONG-1. * If only the MSB of @x is set, then the result is BITS_PER_LONG-1.
* If @x is 0 then the result is COUNT_TRAILING_ZEROS_0. * If @x is 0 then the result is BITS_PER_LONG.
*/ */
static inline int count_trailing_zeros(unsigned long x) static inline int count_trailing_zeros(unsigned long x)
{ {
#define COUNT_TRAILING_ZEROS_0 (-1) return x ? __ffs(x) : BITS_PER_LONG;
if (sizeof(x) == 4)
return ffs(x);
else
return (x != 0) ? __ffs(x) : COUNT_TRAILING_ZEROS_0;
} }
#endif /* _LINUX_BITOPS_COUNT_ZEROS_H_ */ #endif /* _LINUX_BITOPS_COUNT_ZEROS_H_ */

View File

@ -22,8 +22,6 @@ extern unsigned long _find_first_bit(const unsigned long *addr, unsigned long si
unsigned long __find_nth_bit(const unsigned long *addr, unsigned long size, unsigned long n); unsigned long __find_nth_bit(const unsigned long *addr, unsigned long size, unsigned long n);
unsigned long __find_nth_and_bit(const unsigned long *addr1, const unsigned long *addr2, unsigned long __find_nth_and_bit(const unsigned long *addr1, const unsigned long *addr2,
unsigned long size, unsigned long n); unsigned long size, unsigned long n);
unsigned long __find_nth_andnot_bit(const unsigned long *addr1, const unsigned long *addr2,
unsigned long size, unsigned long n);
unsigned long __find_nth_and_andnot_bit(const unsigned long *addr1, const unsigned long *addr2, unsigned long __find_nth_and_andnot_bit(const unsigned long *addr1, const unsigned long *addr2,
const unsigned long *addr3, unsigned long size, const unsigned long *addr3, unsigned long size,
unsigned long n); unsigned long n);

View File

@ -69,6 +69,7 @@ bool __bitmap_or_equal(const unsigned long *bitmap1,
tmp = (bitmap1[k] | bitmap2[k]) ^ bitmap3[k]; tmp = (bitmap1[k] | bitmap2[k]) ^ bitmap3[k];
return (tmp & BITMAP_LAST_WORD_MASK(bits)) == 0; return (tmp & BITMAP_LAST_WORD_MASK(bits)) == 0;
} }
EXPORT_SYMBOL(__bitmap_or_equal);
void __bitmap_complement(unsigned long *dst, const unsigned long *src, unsigned int bits) void __bitmap_complement(unsigned long *dst, const unsigned long *src, unsigned int bits)
{ {
@ -360,6 +361,14 @@ unsigned int __bitmap_weighted_or(unsigned long *dst, const unsigned long *bitma
{ {
return BITMAP_WEIGHT(({dst[idx] = bitmap1[idx] | bitmap2[idx]; dst[idx]; }), bits); return BITMAP_WEIGHT(({dst[idx] = bitmap1[idx] | bitmap2[idx]; dst[idx]; }), bits);
} }
EXPORT_SYMBOL(__bitmap_weighted_or);
unsigned int __bitmap_weighted_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int bits)
{
return BITMAP_WEIGHT(({dst[idx] = bitmap1[idx] ^ bitmap2[idx]; dst[idx]; }), bits);
}
EXPORT_SYMBOL(__bitmap_weighted_xor);
void __bitmap_set(unsigned long *map, unsigned int start, int len) void __bitmap_set(unsigned long *map, unsigned int start, int len)
{ {

View File

@ -66,12 +66,12 @@
* denominator). Like udiv_qrnnd but the numbers are signed. The quotient * denominator). Like udiv_qrnnd but the numbers are signed. The quotient
* is rounded towards 0. * is rounded towards 0.
* *
* 5) count_leading_zeros(count, x) counts the number of zero-bits from the * 5) count_leading_zeros(x) counts the number of zero-bits from the
* msb to the first non-zero bit in the UWtype X. This is the number of * msb to the first non-zero bit in the UWtype X. This is the number of
* steps X needs to be shifted left to set the msb. Undefined for X == 0, * steps X needs to be shifted left to set the msb.
* unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value. * count_leading_zeros(0) == BITS_PER_LONG
* *
* 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts * 6) count_trailing_zeros() like count_leading_zeros(), but counts
* from the least significant end. * from the least significant end.
* *
* 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, * 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,

View File

@ -172,13 +172,6 @@ unsigned long __find_nth_and_bit(const unsigned long *addr1, const unsigned long
} }
EXPORT_SYMBOL(__find_nth_and_bit); EXPORT_SYMBOL(__find_nth_and_bit);
unsigned long __find_nth_andnot_bit(const unsigned long *addr1, const unsigned long *addr2,
unsigned long size, unsigned long n)
{
return FIND_NTH_BIT(addr1[idx] & ~addr2[idx], size, n);
}
EXPORT_SYMBOL(__find_nth_andnot_bit);
unsigned long __find_nth_and_andnot_bit(const unsigned long *addr1, unsigned long __find_nth_and_andnot_bit(const unsigned long *addr1,
const unsigned long *addr2, const unsigned long *addr2,
const unsigned long *addr3, const unsigned long *addr3,

View File

@ -30,18 +30,20 @@ static DECLARE_BITMAP(bitmap, BITMAP_LEN) __initdata;
static DECLARE_BITMAP(bitmap2, BITMAP_LEN) __initdata; static DECLARE_BITMAP(bitmap2, BITMAP_LEN) __initdata;
/* /*
* This is Schlemiel the Painter's algorithm. It should be called after * This is Schlemiel the Painter's algorithm.
* all other tests for the same bitmap because it sets all bits of bitmap to 1.
*/ */
static int __init test_find_first_bit(void *bitmap, unsigned long len) static int __init test_find_first_bit(const void *bitmap, unsigned long len)
{ {
static DECLARE_BITMAP(cp, BITMAP_LEN) __initdata;
unsigned long i, cnt; unsigned long i, cnt;
ktime_t time; ktime_t time;
bitmap_copy(cp, bitmap, BITMAP_LEN);
time = ktime_get(); time = ktime_get();
for (cnt = i = 0; i < len; cnt++) { for (cnt = i = 0; i < len; cnt++) {
i = find_first_bit(bitmap, len); i = find_first_bit(cp, len);
__clear_bit(i, bitmap); __clear_bit(i, cp);
} }
time = ktime_get() - time; time = ktime_get() - time;
pr_err("find_first_bit: %18llu ns, %6ld iterations\n", time, cnt); pr_err("find_first_bit: %18llu ns, %6ld iterations\n", time, cnt);
@ -49,7 +51,8 @@ static int __init test_find_first_bit(void *bitmap, unsigned long len)
return 0; return 0;
} }
static int __init test_find_first_and_bit(void *bitmap, const void *bitmap2, unsigned long len) static int __init test_find_first_and_bit(const void *bitmap, const void *bitmap2,
unsigned long len)
{ {
static DECLARE_BITMAP(cp, BITMAP_LEN) __initdata; static DECLARE_BITMAP(cp, BITMAP_LEN) __initdata;
unsigned long i, cnt; unsigned long i, cnt;

View File

@ -8,12 +8,10 @@
static void dump_primes(void *ctx, const struct primes *p) static void dump_primes(void *ctx, const struct primes *p)
{ {
static char buf[PAGE_SIZE];
struct kunit_suite *suite = ctx; struct kunit_suite *suite = ctx;
bitmap_print_to_pagebuf(true, buf, p->primes, p->sz); kunit_info(suite, "primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %*pbl",
kunit_info(suite, "primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %s", p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], (int)p->sz, p->primes);
p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], buf);
} }
static void prime_numbers_test(struct kunit *test) static void prime_numbers_test(struct kunit *test)

View File

@ -354,18 +354,22 @@ static void __init test_replace(void)
static const unsigned long sg_mask[] __initconst = { static const unsigned long sg_mask[] __initconst = {
BITMAP_FROM_U64(0x000000000000035aULL), BITMAP_FROM_U64(0x000000000000035aULL),
BITMAP_FROM_U64(0x0000000000000000ULL),
}; };
static const unsigned long sg_src[] __initconst = { static const unsigned long sg_src[] __initconst = {
BITMAP_FROM_U64(0x0000000000000667ULL), BITMAP_FROM_U64(0x0000000000000667ULL),
BITMAP_FROM_U64(0x0000000000000000ULL),
}; };
static const unsigned long sg_gather_exp[] __initconst = { static const unsigned long sg_gather_exp[] __initconst = {
BITMAP_FROM_U64(0x0000000000000029ULL), BITMAP_FROM_U64(0x0000000000000029ULL),
BITMAP_FROM_U64(0x0000000000000000ULL),
}; };
static const unsigned long sg_scatter_exp[] __initconst = { static const unsigned long sg_scatter_exp[] __initconst = {
BITMAP_FROM_U64(0x000000000000021aULL), BITMAP_FROM_U64(0x000000000000021aULL),
BITMAP_FROM_U64(0x0000000000000000ULL),
}; };
static void __init test_bitmap_sg(void) static void __init test_bitmap_sg(void)
@ -379,18 +383,18 @@ static void __init test_bitmap_sg(void)
/* Simple gather call */ /* Simple gather call */
bitmap_zero(bmap_gather, 100); bitmap_zero(bmap_gather, 100);
bitmap_gather(bmap_gather, sg_src, sg_mask, nbits); bitmap_gather(bmap_gather, sg_src, sg_mask, nbits);
expect_eq_bitmap(sg_gather_exp, bmap_gather, nbits); expect_eq_bitmap(sg_gather_exp, bmap_gather, 100);
/* Simple scatter call */ /* Simple scatter call */
bitmap_zero(bmap_scatter, 100); bitmap_zero(bmap_scatter, 100);
bitmap_scatter(bmap_scatter, sg_src, sg_mask, nbits); bitmap_scatter(bmap_scatter, sg_src, sg_mask, nbits);
expect_eq_bitmap(sg_scatter_exp, bmap_scatter, nbits); expect_eq_bitmap(sg_scatter_exp, bmap_scatter, 100);
/* Scatter/gather relationship */ /* Scatter/gather relationship */
bitmap_zero(bmap_tmp, 100); bitmap_zero(bmap_tmp, 100);
bitmap_gather(bmap_tmp, bmap_scatter, sg_mask, nbits); bitmap_gather(bmap_tmp, bmap_scatter, sg_mask, nbits);
bitmap_scatter(bmap_res, bmap_tmp, sg_mask, nbits); bitmap_scatter(bmap_res, bmap_tmp, sg_mask, nbits);
expect_eq_bitmap(bmap_scatter, bmap_res, nbits); expect_eq_bitmap(bmap_scatter, bmap_res, 100);
} }
#define PARSE_TIME 0x1 #define PARSE_TIME 0x1
@ -520,8 +524,7 @@ static void __init test_bitmap_parselist(void)
} }
if (ptest.flags & PARSE_TIME) if (ptest.flags & PARSE_TIME)
pr_info("parselist: %d: input is '%s' OK, Time: %llu\n", pr_info("parselist('%s'):\t%llu\n", ptest.in, time);
i, ptest.in, time);
#undef ptest #undef ptest
} }
@ -544,22 +547,22 @@ static void __init test_bitmap_printlist(void)
goto out; goto out;
time = ktime_get(); time = ktime_get();
ret = bitmap_print_to_pagebuf(true, buf, bmap, PAGE_SIZE * 8); ret = scnprintf(buf, PAGE_SIZE, "%*pbl", (int)PAGE_SIZE * 8, bmap);
time = ktime_get() - time; time = ktime_get() - time;
if (ret != slen + 1) { if (ret != slen) {
pr_err("bitmap_print_to_pagebuf: result is %d, expected %d\n", ret, slen); pr_err("scnprintf(\"%%*pbl\"): result is %d, expected %d\n", ret, slen);
failed_tests++; failed_tests++;
goto out; goto out;
} }
if (strncmp(buf, expected, slen)) { if (strncmp(buf, expected, slen)) {
pr_err("bitmap_print_to_pagebuf: result is %s, expected %s\n", buf, expected); pr_err("scnprintf(\"%%*pbl\"): result is %s, expected %s\n", buf, expected);
failed_tests++; failed_tests++;
goto out; goto out;
} }
pr_info("bitmap_print_to_pagebuf: input is '%s', Time: %llu\n", buf, time); pr_info("scnprintf(\"%%*pbl\", '%s'):\t%llu\n", buf, time);
out: out:
kfree(buf); kfree(buf);
kfree(bmap); kfree(bmap);
@ -650,7 +653,7 @@ static void __init test_bitmap_arr32(void)
memset(arr, 0xa5, sizeof(arr)); memset(arr, 0xa5, sizeof(arr));
for (nbits = 0; nbits < EXP1_IN_BITS; ++nbits) { for (nbits = 1; nbits < EXP1_IN_BITS; ++nbits) {
bitmap_to_arr32(arr, exp1, nbits); bitmap_to_arr32(arr, exp1, nbits);
bitmap_from_arr32(bmap2, arr, nbits); bitmap_from_arr32(bmap2, arr, nbits);
expect_eq_bitmap(bmap2, exp1, nbits); expect_eq_bitmap(bmap2, exp1, nbits);
@ -678,7 +681,7 @@ static void __init test_bitmap_arr64(void)
memset(arr, 0xa5, sizeof(arr)); memset(arr, 0xa5, sizeof(arr));
for (nbits = 0; nbits < EXP1_IN_BITS; ++nbits) { for (nbits = 1; nbits < EXP1_IN_BITS; ++nbits) {
memset(bmap2, 0xff, sizeof(arr)); memset(bmap2, 0xff, sizeof(arr));
bitmap_to_arr64(arr, exp1, nbits); bitmap_to_arr64(arr, exp1, nbits);
bitmap_from_arr64(bmap2, arr, nbits); bitmap_from_arr64(bmap2, arr, nbits);
@ -711,7 +714,7 @@ static void noinline __init test_mem_optimisations(void)
unsigned int start, nbits; unsigned int start, nbits;
for (start = 0; start < 1024; start += 8) { for (start = 0; start < 1024; start += 8) {
for (nbits = 0; nbits < 1024 - start; nbits += 8) { for (nbits = 1; nbits < 1024 - start; nbits += 8) {
memset(bmap1, 0x5a, sizeof(bmap1)); memset(bmap1, 0x5a, sizeof(bmap1));
memset(bmap2, 0x5a, sizeof(bmap2)); memset(bmap2, 0x5a, sizeof(bmap2));
@ -851,6 +854,50 @@ static void __init test_for_each_set_bit_from(void)
} }
} }
static void __init test_bitmap_weight(void)
{
unsigned int bit, w1, w2, w;
DECLARE_BITMAP(b, 30);
DECLARE_BITMAP(b1, 128);
bitmap_parselist("all:1/2", b, 30);
/* Test inline implementation */
w = bitmap_weight(b, 30);
w1 = bitmap_weight(b, 15);
w2 = bitmap_weight_from(b, 15, 30);
expect_eq_uint(15, w);
expect_eq_uint(8, w1);
expect_eq_uint(7, w2);
/* Test outline implementation */
w = bitmap_weight(exp1, EXP1_IN_BITS);
for (bit = 1; bit < EXP1_IN_BITS; bit++) {
w1 = bitmap_weight(exp1, bit);
w2 = bitmap_weight_from(exp1, bit, EXP1_IN_BITS);
expect_eq_uint(w1 + w2, w);
}
/* Test out-of-range */
w = bitmap_weight_from(b, 31, 30);
expect_eq_uint(0, !!(w < 30));
/*
* Test bitmap_weight() for correctness in case of some bits set between
* nbits and end of the last word.
*/
bitmap_fill(b1, 128);
/* Inline */
expect_eq_uint(30, bitmap_weight(b1, 30));
expect_eq_uint(100, bitmap_weight(b1, 100));
/* Outline */
for (int i = 1; i < 128; i++)
expect_eq_uint(i, bitmap_weight(b1, i));
}
static void __init test_for_each_clear_bit(void) static void __init test_for_each_clear_bit(void)
{ {
DECLARE_BITMAP(orig, 500); DECLARE_BITMAP(orig, 500);
@ -1395,7 +1442,7 @@ static void __init test_bitmap_read_perf(void)
} }
} }
time = ktime_get() - time; time = ktime_get() - time;
pr_info("Time spent in %s:\t%llu\n", __func__, time); pr_info("%s:\t\t%llu\n", __func__, time);
} }
static void __init test_bitmap_write_perf(void) static void __init test_bitmap_write_perf(void)
@ -1417,7 +1464,63 @@ static void __init test_bitmap_write_perf(void)
} }
} }
time = ktime_get() - time; time = ktime_get() - time;
pr_info("Time spent in %s:\t%llu\n", __func__, time); pr_info("%s:\t\t%llu\n", __func__, time);
}
/*
* nbits == 0 is most commonly not a valid case. Bitmap users should revisit
* the caller logic. Bitmap API doesn't provide any guarantees on returned
* value. The pointers are not dereferenced. The return value is intentionally
* ignored.
*/
static void __init test_zero_nbits(void)
{
static volatile __always_used unsigned long ret __initdata;
bitmap_clear(NULL, 0, 0);
bitmap_complement(NULL, NULL, 0);
bitmap_copy(NULL, NULL, 0);
bitmap_copy_clear_tail(NULL, NULL, 0);
bitmap_fill(NULL, 0);
bitmap_from_arr32(NULL, NULL, 0);
bitmap_from_arr64(NULL, NULL, 0);
bitmap_or(NULL, NULL, NULL, 0);
bitmap_set(NULL, 0, 0);
bitmap_shift_left(NULL, NULL, 0, 0);
bitmap_shift_right(NULL, NULL, 0, 0);
bitmap_to_arr32(NULL, NULL, 0);
bitmap_to_arr64(NULL, NULL, 0);
bitmap_write(NULL, 0, 0, 0);
bitmap_xor(NULL, NULL, NULL, 0);
bitmap_zero(NULL, 0);
ret = bitmap_and(NULL, NULL, NULL, 0);
ret = bitmap_empty(NULL, 0);
ret = bitmap_equal(NULL, NULL, 0);
ret = bitmap_full(NULL, 0);
ret = bitmap_or_equal(NULL, NULL, NULL, 0);
ret = bitmap_read(NULL, 0, 0);
ret = bitmap_subset(NULL, NULL, 0);
ret = bitmap_weight(NULL, 0);
ret = bitmap_weight_and(NULL, NULL, 0);
ret = bitmap_weight_andnot(NULL, NULL, 0);
ret = bitmap_weight_from(NULL, 0, 0);
ret = bitmap_weighted_or(NULL, NULL, NULL, 0);
ret = find_first_and_and_bit(NULL, NULL, NULL, 0);
ret = find_first_and_bit(NULL, NULL, 0);
ret = find_first_andnot_bit(NULL, NULL, 0);
ret = find_first_bit(NULL, 0);
ret = find_first_zero_bit(NULL, 0);
ret = find_last_bit(NULL, 0);
ret = find_next_and_bit(NULL, NULL, 0, 0);
ret = find_next_andnot_bit(NULL, NULL, 0, 0);
ret = find_next_bit(NULL, 0, 0);
ret = find_next_clump8(NULL, NULL, 0, 0);
ret = find_next_zero_bit(NULL, 0, 0);
ret = find_nth_and_bit(NULL, NULL, 0, 0);
ret = find_nth_bit(NULL, 0, 0);
ret = find_random_bit(NULL, 0);
} }
#undef TEST_BIT_LEN #undef TEST_BIT_LEN
@ -1441,7 +1544,9 @@ static void __init selftest(void)
test_bitmap_const_eval(); test_bitmap_const_eval();
test_bitmap_read_write(); test_bitmap_read_write();
test_bitmap_read_perf(); test_bitmap_read_perf();
test_bitmap_weight();
test_bitmap_write_perf(); test_bitmap_write_perf();
test_zero_nbits();
test_find_nth_bit(); test_find_nth_bit();
test_for_each_set_bit(); test_for_each_set_bit();