From 6ec12414164e9d66966909e6da08453be5b6c4cc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Jun 2026 13:34:41 +0200 Subject: [PATCH 01/25] bitmap: Replace __ASSEMBLY__ with __ASSEMBLER__ in header files While the GCC and Clang compilers already define __ASSEMBLER__ automatically when compiling assembly code, __ASSEMBLY__ is a macro that only gets defined by the Makefiles in the kernel. This can be very confusing when switching between userspace and kernelspace coding, or when dealing with uapi headers that rather should use __ASSEMBLER__ instead. So let's standardize now on the __ASSEMBLER__ macro that is provided by the compilers. This is a completely mechanical patch (done with a simple "sed -i" statement). Signed-off-by: Thomas Huth Signed-off-by: Yury Norov --- include/linux/bitmap.h | 4 ++-- include/linux/bits.h | 6 +++--- tools/include/linux/bits.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index b007d54a9036..8854acf77869 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -2,7 +2,7 @@ #ifndef __LINUX_BITMAP_H #define __LINUX_BITMAP_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -894,6 +894,6 @@ void bitmap_write(unsigned long *map, unsigned long value, #define bitmap_set_value8(map, value, start) \ bitmap_write(map, value, start, BITS_PER_BYTE) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLER__ */ #endif /* __LINUX_BITMAP_H */ diff --git a/include/linux/bits.h b/include/linux/bits.h index a40cc861b3a7..b7509f15718e 100644 --- a/include/linux/bits.h +++ b/include/linux/bits.h @@ -17,7 +17,7 @@ * position @h. For example * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ -#if !defined(__ASSEMBLY__) +#if !defined(__ASSEMBLER__) /* * Missing asm support @@ -75,7 +75,7 @@ #define BIT_U32(nr) BIT_TYPE(u32, nr) #define BIT_U64(nr) BIT_TYPE(u64, nr) -#else /* defined(__ASSEMBLY__) */ +#else /* defined(__ASSEMBLER__) */ /* * BUILD_BUG_ON_ZERO is not available in h files included from asm files, @@ -84,6 +84,6 @@ #define GENMASK(h, l) __GENMASK(h, l) #define GENMASK_ULL(h, l) __GENMASK_ULL(h, l) -#endif /* !defined(__ASSEMBLY__) */ +#endif /* !defined(__ASSEMBLER__) */ #endif /* __LINUX_BITS_H */ diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h index a40cc861b3a7..b7509f15718e 100644 --- a/tools/include/linux/bits.h +++ b/tools/include/linux/bits.h @@ -17,7 +17,7 @@ * position @h. For example * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ -#if !defined(__ASSEMBLY__) +#if !defined(__ASSEMBLER__) /* * Missing asm support @@ -75,7 +75,7 @@ #define BIT_U32(nr) BIT_TYPE(u32, nr) #define BIT_U64(nr) BIT_TYPE(u64, nr) -#else /* defined(__ASSEMBLY__) */ +#else /* defined(__ASSEMBLER__) */ /* * BUILD_BUG_ON_ZERO is not available in h files included from asm files, @@ -84,6 +84,6 @@ #define GENMASK(h, l) __GENMASK(h, l) #define GENMASK_ULL(h, l) __GENMASK_ULL(h, l) -#endif /* !defined(__ASSEMBLY__) */ +#endif /* !defined(__ASSEMBLER__) */ #endif /* __LINUX_BITS_H */ From e61575a62fae531e7c656a35e4c19f15f4ca3e1d Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 28 May 2026 14:36:08 -0400 Subject: [PATCH 02/25] psci: simplify hotplug_tests() Switch to pr_info("... %pbl"), and drop the temporary buffer allocation. This prepares for removing cpumap_print_to_pagebuf(). Reviewed-by: Robin Murphy Signed-off-by: Yury Norov --- drivers/firmware/psci/psci_checker.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/psci/psci_checker.c b/drivers/firmware/psci/psci_checker.c index e67ba9891082..e045e92cd898 100644 --- a/drivers/firmware/psci/psci_checker.c +++ b/drivers/firmware/psci/psci_checker.c @@ -186,7 +186,6 @@ static int hotplug_tests(void) { int i, nb_cpu_group, err = -ENOMEM; cpumask_var_t offlined_cpus, *cpu_groups; - char *page_buf; if (!alloc_cpumask_var(&offlined_cpus, GFP_KERNEL)) return err; @@ -194,10 +193,6 @@ static int hotplug_tests(void) nb_cpu_group = alloc_init_cpu_groups(&cpu_groups); if (nb_cpu_group < 0) goto out_free_cpus; - page_buf = (char *)__get_free_page(GFP_KERNEL); - if (!page_buf) - goto out_free_cpu_groups; - /* * Of course the last CPU cannot be powered down and cpu_down() should * refuse doing that. @@ -210,17 +205,11 @@ static int hotplug_tests(void) * off, the cpu group itself should shut down. */ for (i = 0; i < nb_cpu_group; ++i) { - ssize_t len = cpumap_print_to_pagebuf(true, page_buf, - cpu_groups[i]); - /* Remove trailing newline. */ - page_buf[len - 1] = '\0'; - pr_info("Trying to turn off and on again group %d (CPUs %s)\n", - i, page_buf); + pr_info("Trying to turn off and on again group %d (CPUs %*pbl)\n", + i, cpumask_pr_args(cpu_groups[i])); err += down_and_up_cpus(cpu_groups[i], offlined_cpus); } - free_page((unsigned long)page_buf); -out_free_cpu_groups: free_cpu_groups(nb_cpu_group, &cpu_groups); out_free_cpus: free_cpumask_var(offlined_cpus); From 199dc60543109ad2b7b5904297a3ff54d5a7a0b3 Mon Sep 17 00:00:00 2001 From: sunyi <279644543@qq.com> Date: Mon, 6 Jul 2026 17:29:33 +0800 Subject: [PATCH 03/25] lib: bitmap: add tests for bitmap_find_next_zero_area_off() Add functional and performance tests for bitmap_find_next_zero_area_off(). performance tests partial output: Start testing find_bit() with random-filled bitmap [ 0.310073] bitmap_find_next_zero_area_off: 852731 ns, 1154 iterations [ 0.311435] find_next_bit: 1356654 ns, 163975 iterations Start testing find_bit() with sparse bitmap [ 0.316267] bitmap_find_next_zero_area_off:4426808 ns, 322479 iterations [ 0.316292] find_next_bit: 15154 ns, 656 iterations Signed-off-by: sunyi <279644543@qq.com> Signed-off-by: Yury Norov --- lib/find_bit_benchmark.c | 17 +++++++++++++++++ lib/test_bitmap.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/lib/find_bit_benchmark.c b/lib/find_bit_benchmark.c index 00d9dc61cd46..05305e655f99 100644 --- a/lib/find_bit_benchmark.c +++ b/lib/find_bit_benchmark.c @@ -149,6 +149,21 @@ static int __init test_find_next_and_bit(const void *bitmap, return 0; } +static int __init +test_bitmap_find_next_zero_area_off(unsigned long *bitmap, unsigned long len) +{ + unsigned long i, cnt; + ktime_t time; + + time = ktime_get(); + for (cnt = i = 0; i < BITMAP_LEN; cnt++) + i = bitmap_find_next_zero_area_off(bitmap, BITMAP_LEN, i, 8, 0, 0) + 1; + time = ktime_get() - time; + pr_err("bitmap_find_next_zero_area_off:%7llu ns, %6ld iterations\n", time, cnt); + + return 0; +} + static int __init find_bit_test(void) { unsigned long nbits = BITMAP_LEN / SPARSE; @@ -158,6 +173,7 @@ static int __init find_bit_test(void) get_random_bytes(bitmap, sizeof(bitmap)); get_random_bytes(bitmap2, sizeof(bitmap2)); + test_bitmap_find_next_zero_area_off(bitmap, BITMAP_LEN); test_find_next_bit(bitmap, BITMAP_LEN); test_find_next_zero_bit(bitmap, BITMAP_LEN); test_find_last_bit(bitmap, BITMAP_LEN); @@ -181,6 +197,7 @@ static int __init find_bit_test(void) __set_bit(get_random_u32_below(BITMAP_LEN), bitmap2); } + test_bitmap_find_next_zero_area_off(bitmap, BITMAP_LEN); test_find_next_bit(bitmap, BITMAP_LEN); test_find_next_zero_bit(bitmap, BITMAP_LEN); test_find_last_bit(bitmap, BITMAP_LEN); diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index 69813c10e6c0..8665df77c960 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -234,6 +234,43 @@ static void __init test_find_nth_bit(void) } } +static void __init +test_bitmap_find_next_zero_area_off(void) +{ + DECLARE_BITMAP(bmap, 192); + + bitmap_set(bmap, 0, 192); + + bitmap_clear(bmap, 0, 8); + __clear_bit(50, bmap); + bitmap_clear(bmap, 60, 18); + __set_bit(69, bmap); + __clear_bit(80, bmap); + bitmap_clear(bmap, 100, 10); + __clear_bit(120, bmap); + bitmap_clear(bmap, 145, 8); + bitmap_clear(bmap, 160, 32); + + expect_eq_uint(0, + bitmap_find_next_zero_area_off(bmap, 192, 0, 8, 0, 0)); + expect_eq_uint(0, + bitmap_find_next_zero_area_off(bmap, 192, 0, 8, 3, 0)); + expect_eq_uint(163, + bitmap_find_next_zero_area_off(bmap, 192, 0, 8, 3, 1)); + expect_eq_uint(60, + bitmap_find_next_zero_area_off(bmap, 192, 1, 8, 0, 0)); + expect_eq_uint(160, + bitmap_find_next_zero_area_off(bmap, 192, 1, 8, 7, 0)); + expect_eq_uint(60, + bitmap_find_next_zero_area_off(bmap, 192, 1, 8, 7, 4)); + expect_eq_uint(100, + bitmap_find_next_zero_area_off(bmap, 192, 0, 10, 0, 0)); + expect_eq_uint(160, + bitmap_find_next_zero_area_off(bmap, 192, 0, 32, 0, 0)); + expect_eq_uint(1, + !!(bitmap_find_next_zero_area_off(bmap, 192, 0, 33, 0, 0) >= 192)); +} + static void __init test_fill_set(void) { DECLARE_BITMAP(bmap, 1024); @@ -1559,6 +1596,7 @@ static void __init selftest(void) test_for_each_clear_bitrange_from(); test_for_each_set_clump8(); test_for_each_set_bit_wrap(); + test_bitmap_find_next_zero_area_off(); } KSTM_MODULE_LOADERS(test_bitmap); From df81d444dc740778f7c7a2d4c3500136851375aa Mon Sep 17 00:00:00 2001 From: sunyi <279644543@qq.com> Date: Mon, 6 Jul 2026 17:29:34 +0800 Subject: [PATCH 04/25] lib: bitmap: optimize bitmap_find_next_zero_area_off() Finding a contiguous free region in a highly fragmented bitmap is not easy and may require many repeated attempts. Therefore, find_next_bit(map, end, index) is not the optimal choice. This is because there may be multiple scattered free regions within the range [index, end) and none of them will meet the length requirement of @nr. Instead, it's sufficient to directly find the last bit within the range [index, end), thus reducing unnecessary repeated calls. An example of a bitmap: Bits 0-3: cleared(4 bits) Bits 4-5: set (2 bits) Bits 6-8: cleared(4 bits) Bits 9-10: set (2 bits) Bits 11-20: cleared(10 bits) The goal is to find a 10-bit free region. The old code logic is as follows: find_next_zero_bit(start = 0, find bit 0) -> find_next_bit(find bit 4) -> next loop -> find_next_zero_bit(start = 5, find bit 6) -> find_next_bit(find bit 9) -> next loop -> find_next_zero_bit(start = 10, find bit 11) -> success The new code logic is as follows: find_next_zero_bit(start = 0, find bit 0) -> find_last_bit(find bit 9) -> next loop -> find_next_zero_bit(start = 10, find bit 11) -> success Performance test results on my hardware(use lib/find_bit_benchmark.c): before after change p-value dense 1211 688 -43.2% 8.3e-11 sparse 13.3 13.4 0.8% 0.27 Yury: The less micro-benchmark kselftest/dmabuf-heaps/dmabuf-heap gives even better numbers: Metric Before After Change Trace span 194.0 ms 87.1 ms -55.1% Total CMA alloc time 48.46 ms 16.11 ms -66.8% Avg alloc latency 184.94 us 61.49 us -66.8% Median alloc latency 73.72 us 20.59 us -72.1% p90 alloc latency 329.76 us 55.63 us -83.1% p99 alloc latency 1866.76 us 859.83 us -53.9% Max alloc latency 4821.91 us 2324.41 us -51.8% By request size: Request Before Avg After Avg Change 1 page 79.68 us 34.47 us -56.7% 256 pages 285.50 us 87.30 us -69.4% Co-developed-by: Yury Norov Signed-off-by: Yury Norov Signed-off-by: sunyi <279644543@qq.com> Signed-off-by: Yury Norov --- lib/bitmap.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/lib/bitmap.c b/lib/bitmap.c index b9bfa157e095..b464d843f4eb 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -432,22 +432,28 @@ unsigned long bitmap_find_next_zero_area_off(unsigned long *map, unsigned long align_mask, unsigned long align_offset) { - unsigned long index, end, i; -again: - index = find_next_zero_bit(map, size, start); + unsigned long end, i, off; - /* Align allocation */ - index = __ALIGN_MASK(index + align_offset, align_mask) - align_offset; + for_each_clear_bit_from(start, map, size) { + start = __ALIGN_MASK(start + align_offset, align_mask) - align_offset; + end = start + nr; + if (end > size) + break; - end = index + nr; - if (end > size) - return end; - i = find_next_bit(map, end, index); - if (i < end) { - start = i + 1; - goto again; + off = round_down(start, BITS_PER_LONG); + i = find_last_bit(map + start / BITS_PER_LONG, end - off) + off; + if (i >= end || i < start) + return start; + + start = i; } - return index; + + /* + * Here, returning size + 1 is to maintain consistency + * with the old version, where the return value is always + * greater than size when no zero areas are found. + */ + return size + 1; } EXPORT_SYMBOL(bitmap_find_next_zero_area_off); From 1c61b0a6e5c472737619f74f79c390363ee258df Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Mon, 6 Jul 2026 16:23:24 -0400 Subject: [PATCH 05/25] bitops: make the *_bit_le functions use unsigned long The *_bit_le functions use a signed integer for the bit number. However, the *_bit functions can use an unsigned long. This causes problems if there is a large bitmap and a bit number > 0x80000000 is passed in. Since that is a negative int, it will get sign extended to a long when getting passed to the *_bit function, turning it into a huge bit number. This usually ends up with the memory address wrapping around and the function accessing memory before the start of the bitmap. Avoid this by making the *_bit_le functions take an unsigned long. This can be triggered by faking an almost 4TB dm-mirror device, which uses bitmaps to track the mirror regions: $ dmsetup create bigzero --table '0 8589934590 zero' $ dmsetup create mymirror --table '0 8589934590 mirror core 2 2 nosync 2 /dev/mapper/bigzero 0 /dev/mapper/bigzero 0' This will access memory before the start of the sync_bits bitmap, and likely hit the guard page of the previously allocated clean_bits bitmap, causing a kernel panic with the old code. I looked and didn't see any crazy code using the signed int to intentionally try and access bits before some address within the bitmap. Signed-off-by: Benjamin Marzinski Signed-off-by: Yury Norov --- include/asm-generic/bitops/le.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/asm-generic/bitops/le.h b/include/asm-generic/bitops/le.h index d51beff60375..e3b0da9a25f1 100644 --- a/include/asm-generic/bitops/le.h +++ b/include/asm-generic/bitops/le.h @@ -16,47 +16,47 @@ #endif -static inline int test_bit_le(int nr, const void *addr) +static inline int test_bit_le(unsigned long nr, const void *addr) { return test_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void set_bit_le(int nr, void *addr) +static inline void set_bit_le(unsigned long nr, void *addr) { set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void clear_bit_le(int nr, void *addr) +static inline void clear_bit_le(unsigned long nr, void *addr) { clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void __set_bit_le(int nr, void *addr) +static inline void __set_bit_le(unsigned long nr, void *addr) { __set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void __clear_bit_le(int nr, void *addr) +static inline void __clear_bit_le(unsigned long nr, void *addr) { __clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int test_and_set_bit_le(int nr, void *addr) +static inline int test_and_set_bit_le(unsigned long nr, void *addr) { return test_and_set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int test_and_clear_bit_le(int nr, void *addr) +static inline int test_and_clear_bit_le(unsigned long nr, void *addr) { return test_and_clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int __test_and_set_bit_le(int nr, void *addr) +static inline int __test_and_set_bit_le(unsigned long nr, void *addr) { return __test_and_set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int __test_and_clear_bit_le(int nr, void *addr) +static inline int __test_and_clear_bit_le(unsigned long nr, void *addr) { return __test_and_clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } From 1a9adacbbb1b034bfd6de9a552134423f0e41658 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:14 -0400 Subject: [PATCH 06/25] arm: Use sysfs_emit() for cpumask show callbacks These callbacks are sysfs show paths. Use sysfs_emit() and cpumask_pr_args() to emit the masks. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- arch/arm/mach-imx/mmdc.c | 2 +- arch/arm/mm/cache-l2x0-pmu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-imx/mmdc.c b/arch/arm/mach-imx/mmdc.c index b71467c48b87..f6d993b9b1d4 100644 --- a/arch/arm/mach-imx/mmdc.c +++ b/arch/arm/mach-imx/mmdc.c @@ -127,7 +127,7 @@ static ssize_t mmdc_pmu_cpumask_show(struct device *dev, { struct mmdc_pmu *pmu_mmdc = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, &pmu_mmdc->cpu); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&pmu_mmdc->cpu)); } static struct device_attribute mmdc_pmu_cpumask_attr = diff --git a/arch/arm/mm/cache-l2x0-pmu.c b/arch/arm/mm/cache-l2x0-pmu.c index 3d9caf7464bf..478227078837 100644 --- a/arch/arm/mm/cache-l2x0-pmu.c +++ b/arch/arm/mm/cache-l2x0-pmu.c @@ -390,7 +390,7 @@ static struct attribute_group l2x0_pmu_event_attrs_group = { static ssize_t l2x0_pmu_cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return cpumap_print_to_pagebuf(true, buf, &pmu_cpu); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&pmu_cpu)); } static struct device_attribute l2x0_pmu_cpumask_attr = From bf7f33aada102e171d3777efc55be856f5591d42 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:15 -0400 Subject: [PATCH 07/25] powerpc: Use sysfs_emit() for cpumask show callbacks These callbacks are sysfs show paths. Use sysfs_emit() and cpumask_pr_args() to emit the masks. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- arch/powerpc/kernel/cacheinfo.c | 3 ++- arch/powerpc/perf/hv-24x7.c | 2 +- arch/powerpc/perf/hv-gpci.c | 2 +- arch/powerpc/perf/imc-pmu.c | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 04e5ea38bdc0..cb0933061006 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -690,7 +690,8 @@ show_shared_cpumap(struct kobject *k, struct kobj_attribute *attr, char *buf, bo mask = &cache->shared_cpu_map; - return cpumap_print_to_pagebuf(list, buf, mask); + return sysfs_emit(buf, list ? "%*pbl\n" : "%*pb\n", + cpumask_pr_args(mask)); } static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf) diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index abb4cfb11fcc..b0eedf185960 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -429,7 +429,7 @@ static char *memdup_to_str(char *maybe_str, int max_len, gfp_t gfp) static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return cpumap_print_to_pagebuf(true, buf, &hv_24x7_cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&hv_24x7_cpumask)); } static ssize_t sockets_show(struct device *dev, diff --git a/arch/powerpc/perf/hv-gpci.c b/arch/powerpc/perf/hv-gpci.c index 7269273d3aa8..76495744f52a 100644 --- a/arch/powerpc/perf/hv-gpci.c +++ b/arch/powerpc/perf/hv-gpci.c @@ -100,7 +100,7 @@ static ssize_t kernel_version_show(struct device *dev, static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return cpumap_print_to_pagebuf(true, buf, &hv_gpci_cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&hv_gpci_cpumask)); } /* Interface attribute array index to store system information */ diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index e3822f36c419..0be4e98f7ad1 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -117,7 +117,7 @@ static ssize_t imc_pmu_cpumask_get_attr(struct device *dev, return 0; } - return cpumap_print_to_pagebuf(true, buf, active_mask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(active_mask)); } static DEVICE_ATTR(cpumask, S_IRUGO, imc_pmu_cpumask_get_attr, NULL); From 163d9808b9358de119b621df94a19b5cd21831bb Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:16 -0400 Subject: [PATCH 08/25] x86/events: Use sysfs_emit() for cpumask show callbacks These callbacks are sysfs show paths. Use sysfs_emit() and cpumask_pr_args() to emit the masks. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- arch/x86/events/amd/iommu.c | 2 +- arch/x86/events/amd/power.c | 2 +- arch/x86/events/amd/uncore.c | 2 +- arch/x86/events/intel/core.c | 2 +- arch/x86/events/intel/uncore.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/events/amd/iommu.c b/arch/x86/events/amd/iommu.c index 07b110e8418a..f332c7089bd5 100644 --- a/arch/x86/events/amd/iommu.c +++ b/arch/x86/events/amd/iommu.c @@ -137,7 +137,7 @@ static ssize_t _iommu_cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return cpumap_print_to_pagebuf(true, buf, &iommu_cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&iommu_cpumask)); } static DEVICE_ATTR(cpumask, S_IRUGO, _iommu_cpumask_show, NULL); diff --git a/arch/x86/events/amd/power.c b/arch/x86/events/amd/power.c index 744dffa42dee..66197214b010 100644 --- a/arch/x86/events/amd/power.c +++ b/arch/x86/events/amd/power.c @@ -150,7 +150,7 @@ static void pmu_event_read(struct perf_event *event) static ssize_t get_attr_cpumask(struct device *dev, struct device_attribute *attr, char *buf) { - return cpumap_print_to_pagebuf(true, buf, &cpu_mask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&cpu_mask)); } static DEVICE_ATTR(cpumask, S_IRUGO, get_attr_cpumask, NULL); diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c index dbc00b6dd69e..10c7a4b5f1a8 100644 --- a/arch/x86/events/amd/uncore.c +++ b/arch/x86/events/amd/uncore.c @@ -322,7 +322,7 @@ static ssize_t amd_uncore_attr_show_cpumask(struct device *dev, struct pmu *ptr = dev_get_drvdata(dev); struct amd_uncore_pmu *pmu = container_of(ptr, struct amd_uncore_pmu, pmu); - return cpumap_print_to_pagebuf(true, buf, &pmu->active_mask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&pmu->active_mask)); } static DEVICE_ATTR(cpumask, S_IRUGO, amd_uncore_attr_show_cpumask, NULL); diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 2b35483e2b70..00ecfaa9df3a 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -7564,7 +7564,7 @@ static ssize_t intel_hybrid_get_attr_cpus(struct device *dev, struct x86_hybrid_pmu *pmu = container_of(dev_get_drvdata(dev), struct x86_hybrid_pmu, pmu); - return cpumap_print_to_pagebuf(true, buf, &pmu->supported_cpus); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&pmu->supported_cpus)); } static DEVICE_ATTR(cpus, S_IRUGO, intel_hybrid_get_attr_cpus, NULL); diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c index 7857959c6e82..c5f076d20aa2 100644 --- a/arch/x86/events/intel/uncore.c +++ b/arch/x86/events/intel/uncore.c @@ -861,7 +861,7 @@ static ssize_t uncore_get_attr_cpumask(struct device *dev, { struct intel_uncore_pmu *pmu = container_of(dev_get_drvdata(dev), struct intel_uncore_pmu, pmu); - return cpumap_print_to_pagebuf(true, buf, &pmu->cpu_mask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&pmu->cpu_mask)); } static DEVICE_ATTR(cpumask, S_IRUGO, uncore_get_attr_cpumask, NULL); From 32dfaab71f28574490d2ad37674f1784c1f18b6e Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:17 -0400 Subject: [PATCH 09/25] cpu: Use sysfs_emit() for cpumask show callback show_cpus_attr() is a sysfs show callback. Use sysfs_emit() and cpumask_pr_args() to emit the mask. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- drivers/base/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 19d288a3c80c..69e52fed4241 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -218,7 +218,7 @@ static ssize_t show_cpus_attr(struct device *dev, { struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr); - return cpumap_print_to_pagebuf(true, buf, ca->map); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(ca->map)); } #define _CPU_ATTR(name, map) \ From b4b58c39e85f703f18b05a2bf26e51fbca846b7c Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:18 -0400 Subject: [PATCH 10/25] devfreq: Use sysfs_emit() for cpumask show callbacks These callbacks are sysfs show paths. Use sysfs_emit() and cpumask_pr_args() to emit the masks. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- drivers/devfreq/event/rockchip-dfi.c | 2 +- drivers/devfreq/hisi_uncore_freq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c index 5e6e7e900bda..255aee1bdd91 100644 --- a/drivers/devfreq/event/rockchip-dfi.c +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -354,7 +354,7 @@ static ssize_t ddr_perf_cpumask_show(struct device *dev, struct pmu *pmu = dev_get_drvdata(dev); struct rockchip_dfi *dfi = container_of(pmu, struct rockchip_dfi, pmu); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(dfi->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(dfi->cpu))); } static struct device_attribute ddr_perf_cpumask_attr = diff --git a/drivers/devfreq/hisi_uncore_freq.c b/drivers/devfreq/hisi_uncore_freq.c index bef718d6ae35..e1f64b723082 100644 --- a/drivers/devfreq/hisi_uncore_freq.c +++ b/drivers/devfreq/hisi_uncore_freq.c @@ -540,7 +540,7 @@ static ssize_t related_cpus_show(struct device *dev, { struct hisi_uncore_freq *uncore = dev_get_drvdata(dev->parent); - return cpumap_print_to_pagebuf(true, buf, &uncore->related_cpus); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&uncore->related_cpus)); } static DEVICE_ATTR_RO(related_cpus); From 4f984fbb7078d279ee096a944ea583470297d8dd Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:19 -0400 Subject: [PATCH 11/25] fpga: dfl-fme-perf: Use sysfs_emit() for cpumask show cpumask_show() is a sysfs show callback. Use sysfs_emit() and cpumask_pr_args() to emit the mask. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- drivers/fpga/dfl-fme-perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 7422d2bc6f37..7aa4983ab67d 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -183,7 +183,7 @@ static ssize_t cpumask_show(struct device *dev, priv = to_fme_perf_priv(pmu); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(priv->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(priv->cpu))); } static DEVICE_ATTR_RO(cpumask); From 9041d5897d8a22bc9249a958c64ccfe1bc5de4d5 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:20 -0400 Subject: [PATCH 12/25] hwtracing: hisi_ptt: Use sysfs_emit() for cpumask show cpumask_show() is a sysfs show callback. Use sysfs_emit() and cpumask_pr_args() to emit the mask. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- drivers/hwtracing/ptt/hisi_ptt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 94c371c49135..233c4c32513c 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -780,7 +780,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); const cpumask_t *cpumask = cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask)); } static DEVICE_ATTR_RO(cpumask); From bf34237870aa4151480a99595f6745f9858b3cb9 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:21 -0400 Subject: [PATCH 13/25] RDMA/hfi1: Use sysfs_emit() for cpumask show helper sdma_get_cpu_to_sde_map() is used by a sysfs show callback. Use sysfs_emit() and cpumask_pr_args() to emit the mask. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- drivers/infiniband/hw/hfi1/sdma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index cfd9dd0f7e81..f253c8ee182d 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "hfi.h" #include "common.h" @@ -1049,7 +1050,7 @@ ssize_t sdma_get_cpu_to_sde_map(struct sdma_engine *sde, char *buf) if (cpumask_empty(&sde->cpu_mask)) snprintf(buf, PAGE_SIZE, "%s\n", "empty"); else - cpumap_print_to_pagebuf(true, buf, &sde->cpu_mask); + sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&sde->cpu_mask)); mutex_unlock(&process_to_sde_mutex); return strnlen(buf, PAGE_SIZE); } From 9e368c54216433b735456fc13058e49b7810f31f Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:22 -0400 Subject: [PATCH 14/25] PCI/sysfs: Use sysfs_emit() for cpumask show callbacks These callbacks are sysfs show paths. Use sysfs_emit() and cpumask_pr_args() to emit the masks. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov --- drivers/pci/pci-sysfs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5ec0b245a69b..d6083552b287 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -112,7 +112,8 @@ static ssize_t pci_dev_show_local_cpu(struct device *dev, bool list, #else mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); #endif - return cpumap_print_to_pagebuf(list, buf, mask); + return sysfs_emit(buf, list ? "%*pbl\n" : "%*pb\n", + cpumask_pr_args(mask)); } static ssize_t local_cpus_show(struct device *dev, @@ -137,7 +138,7 @@ static ssize_t cpuaffinity_show(struct device *dev, { const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev)); - return cpumap_print_to_pagebuf(false, buf, cpumask); + return sysfs_emit(buf, "%*pb\n", cpumask_pr_args(cpumask)); } static DEVICE_ATTR_RO(cpuaffinity); @@ -146,7 +147,7 @@ static ssize_t cpulistaffinity_show(struct device *dev, { const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask)); } static DEVICE_ATTR_RO(cpulistaffinity); From b3fe8cc7374ecbb6f5194b0c69f84c11c0c68aa8 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:23 -0400 Subject: [PATCH 15/25] perf: Use sysfs_emit() for cpumask show callbacks These callbacks are sysfs show paths. Use sysfs_emit() and cpumask_pr_args() to emit the masks. This prepares for removing cpumap_print_to_pagebuf(). Link: https://lore.kernel.org/all/akANJ-AT7nHpRMq-@yury/ Acked-by: Robin Murphy Signed-off-by: Yury Norov --- drivers/perf/alibaba_uncore_drw_pmu.c | 2 +- drivers/perf/amlogic/meson_ddr_pmu_core.c | 2 +- drivers/perf/arm-cci.c | 2 +- drivers/perf/arm-ccn.c | 2 +- drivers/perf/arm-cmn.c | 2 +- drivers/perf/arm-ni.c | 2 +- drivers/perf/arm_cspmu/arm_cspmu.c | 2 +- drivers/perf/arm_dmc620_pmu.c | 4 ++-- drivers/perf/arm_dsu_pmu.c | 2 +- drivers/perf/arm_pmu.c | 2 +- drivers/perf/arm_smmuv3_pmu.c | 2 +- drivers/perf/arm_spe_pmu.c | 2 +- drivers/perf/cxl_pmu.c | 2 +- drivers/perf/dwc_pcie_pmu.c | 2 +- drivers/perf/fsl_imx8_ddr_perf.c | 2 +- drivers/perf/fsl_imx9_ddr_perf.c | 2 +- drivers/perf/fujitsu_uncore_pmu.c | 2 +- drivers/perf/hisilicon/hisi_pcie_pmu.c | 2 +- drivers/perf/hisilicon/hisi_uncore_pmu.c | 2 +- drivers/perf/marvell_cn10k_ddr_pmu.c | 2 +- drivers/perf/marvell_cn10k_tad_pmu.c | 2 +- drivers/perf/marvell_pem_pmu.c | 2 +- drivers/perf/nvidia_t410_c2c_pmu.c | 2 +- drivers/perf/nvidia_t410_cmem_latency_pmu.c | 2 +- drivers/perf/qcom_l2_pmu.c | 2 +- drivers/perf/qcom_l3_pmu.c | 2 +- drivers/perf/starfive_starlink_pmu.c | 2 +- drivers/perf/thunderx2_pmu.c | 2 +- drivers/perf/xgene_pmu.c | 2 +- kernel/events/core.c | 2 +- 30 files changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/perf/alibaba_uncore_drw_pmu.c b/drivers/perf/alibaba_uncore_drw_pmu.c index ac49d3b2dad6..74786a5dd6a2 100644 --- a/drivers/perf/alibaba_uncore_drw_pmu.c +++ b/drivers/perf/alibaba_uncore_drw_pmu.c @@ -221,7 +221,7 @@ static ssize_t ali_drw_pmu_cpumask_show(struct device *dev, { struct ali_drw_pmu *drw_pmu = to_ali_drw_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(drw_pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(drw_pmu->cpu))); } static struct device_attribute ali_drw_pmu_cpumask_attr = diff --git a/drivers/perf/amlogic/meson_ddr_pmu_core.c b/drivers/perf/amlogic/meson_ddr_pmu_core.c index c1e755c356a3..f614aa3434a5 100644 --- a/drivers/perf/amlogic/meson_ddr_pmu_core.c +++ b/drivers/perf/amlogic/meson_ddr_pmu_core.c @@ -191,7 +191,7 @@ static ssize_t meson_ddr_perf_cpumask_show(struct device *dev, { struct ddr_pmu *pmu = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pmu->cpu))); } static struct device_attribute meson_ddr_perf_cpumask_attr = diff --git a/drivers/perf/arm-cci.c b/drivers/perf/arm-cci.c index 1cc3214d6b6d..f0ef0a679e74 100644 --- a/drivers/perf/arm-cci.c +++ b/drivers/perf/arm-cci.c @@ -1351,7 +1351,7 @@ static ssize_t pmu_cpumask_attr_show(struct device *dev, struct pmu *pmu = dev_get_drvdata(dev); struct cci_pmu *cci_pmu = to_cci_pmu(pmu); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(cci_pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(cci_pmu->cpu))); } static struct device_attribute pmu_cpumask_attr = diff --git a/drivers/perf/arm-ccn.c b/drivers/perf/arm-ccn.c index c18a0e3205ab..c40da3450c8f 100644 --- a/drivers/perf/arm-ccn.c +++ b/drivers/perf/arm-ccn.c @@ -537,7 +537,7 @@ static ssize_t arm_ccn_pmu_cpumask_show(struct device *dev, { struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(ccn->dt.cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(ccn->dt.cpu))); } static struct device_attribute arm_ccn_pmu_cpumask_attr = diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 6e5cc4086a9e..ff8bbf07c94e 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -1327,7 +1327,7 @@ static ssize_t arm_cmn_cpumask_show(struct device *dev, { struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(cmn->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(cmn->cpu))); } static struct device_attribute arm_cmn_cpumask_attr = diff --git a/drivers/perf/arm-ni.c b/drivers/perf/arm-ni.c index 66858c65215d..03a1c6bf9223 100644 --- a/drivers/perf/arm-ni.c +++ b/drivers/perf/arm-ni.c @@ -239,7 +239,7 @@ static ssize_t arm_ni_cpumask_show(struct device *dev, { struct arm_ni *ni = cd_to_ni(pmu_to_cd(dev_get_drvdata(dev))); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(ni->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(ni->cpu))); } static struct device_attribute arm_ni_cpumask_attr = diff --git a/drivers/perf/arm_cspmu/arm_cspmu.c b/drivers/perf/arm_cspmu/arm_cspmu.c index 80fb314d5135..e6292021f653 100644 --- a/drivers/perf/arm_cspmu/arm_cspmu.c +++ b/drivers/perf/arm_cspmu/arm_cspmu.c @@ -305,7 +305,7 @@ static ssize_t arm_cspmu_cpumask_show(struct device *dev, default: return 0; } - return cpumap_print_to_pagebuf(true, buf, cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask)); } static struct attribute *arm_cspmu_cpumask_attrs[] = { diff --git a/drivers/perf/arm_dmc620_pmu.c b/drivers/perf/arm_dmc620_pmu.c index 4f6b196160f8..467147a05eec 100644 --- a/drivers/perf/arm_dmc620_pmu.c +++ b/drivers/perf/arm_dmc620_pmu.c @@ -237,8 +237,8 @@ static ssize_t dmc620_pmu_cpumask_show(struct device *dev, { struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, - cpumask_of(dmc620_pmu->irq->cpu)); + return sysfs_emit(buf, "%*pbl\n", + cpumask_pr_args(cpumask_of(dmc620_pmu->irq->cpu))); } static struct device_attribute dmc620_pmu_cpumask_attr = diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c index 32b0dd7c693b..bcbd19e075a5 100644 --- a/drivers/perf/arm_dsu_pmu.c +++ b/drivers/perf/arm_dsu_pmu.c @@ -157,7 +157,7 @@ static ssize_t dsu_pmu_cpumask_show(struct device *dev, default: return 0; } - return cpumap_print_to_pagebuf(true, buf, cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask)); } static struct attribute *dsu_pmu_format_attrs[] = { diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 939bcbd433aa..51ab6cc52ca0 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -570,7 +570,7 @@ static ssize_t cpus_show(struct device *dev, struct device_attribute *attr, char *buf) { struct arm_pmu *armpmu = to_arm_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, &armpmu->supported_cpus); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&armpmu->supported_cpus)); } static DEVICE_ATTR_RO(cpus); diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index 621f02a7f43b..8ce34e6bb82b 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -537,7 +537,7 @@ static ssize_t smmu_pmu_cpumask_show(struct device *dev, { struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(smmu_pmu->on_cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(smmu_pmu->on_cpu))); } static struct device_attribute smmu_pmu_cpumask_attr = diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c index dbd0da111639..9f786fd48cdd 100644 --- a/drivers/perf/arm_spe_pmu.c +++ b/drivers/perf/arm_spe_pmu.c @@ -343,7 +343,7 @@ static ssize_t cpumask_show(struct device *dev, { struct arm_spe_pmu *spe_pmu = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, &spe_pmu->supported_cpus); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&spe_pmu->supported_cpus)); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c index 68a54d97d2a8..0735eb33f5f3 100644 --- a/drivers/perf/cxl_pmu.c +++ b/drivers/perf/cxl_pmu.c @@ -493,7 +493,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, { struct cxl_pmu_info *info = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(info->on_cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(info->on_cpu))); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index 5385401fa9cf..291e776d6f6a 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -117,7 +117,7 @@ static ssize_t cpumask_show(struct device *dev, { struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_pmu->on_cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pcie_pmu->on_cpu))); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index bcdf5575d71c..3760ebe02674 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -237,7 +237,7 @@ static ssize_t ddr_perf_cpumask_show(struct device *dev, { struct ddr_pmu *pmu = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pmu->cpu))); } static struct device_attribute ddr_perf_cpumask_attr = diff --git a/drivers/perf/fsl_imx9_ddr_perf.c b/drivers/perf/fsl_imx9_ddr_perf.c index 7050b48c0467..6fee5eb5087a 100644 --- a/drivers/perf/fsl_imx9_ddr_perf.c +++ b/drivers/perf/fsl_imx9_ddr_perf.c @@ -159,7 +159,7 @@ static ssize_t ddr_perf_cpumask_show(struct device *dev, { struct ddr_pmu *pmu = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pmu->cpu))); } static struct device_attribute ddr_perf_cpumask_attr = diff --git a/drivers/perf/fujitsu_uncore_pmu.c b/drivers/perf/fujitsu_uncore_pmu.c index aeeb68c66e1e..ede652f757da 100644 --- a/drivers/perf/fujitsu_uncore_pmu.c +++ b/drivers/perf/fujitsu_uncore_pmu.c @@ -373,7 +373,7 @@ static ssize_t cpumask_show(struct device *dev, { struct uncore_pmu *uncorepmu = to_uncore_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(uncorepmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(uncorepmu->cpu))); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/hisilicon/hisi_pcie_pmu.c b/drivers/perf/hisilicon/hisi_pcie_pmu.c index c5394d007b61..0f55d871c67e 100644 --- a/drivers/perf/hisilicon/hisi_pcie_pmu.c +++ b/drivers/perf/hisilicon/hisi_pcie_pmu.c @@ -121,7 +121,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, c { struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_pmu->on_cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pcie_pmu->on_cpu))); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index de71dcf11653..0ff2fdf4b3e2 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -56,7 +56,7 @@ static ssize_t hisi_associated_cpus_sysfs_show(struct device *dev, { struct hisi_pmu *hisi_pmu = to_hisi_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, &hisi_pmu->associated_cpus); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&hisi_pmu->associated_cpus)); } static DEVICE_ATTR(associated_cpus, 0444, hisi_associated_cpus_sysfs_show, NULL); diff --git a/drivers/perf/marvell_cn10k_ddr_pmu.c b/drivers/perf/marvell_cn10k_ddr_pmu.c index 72ac17efd846..8681e8715cb3 100644 --- a/drivers/perf/marvell_cn10k_ddr_pmu.c +++ b/drivers/perf/marvell_cn10k_ddr_pmu.c @@ -364,7 +364,7 @@ static ssize_t cn10k_ddr_perf_cpumask_show(struct device *dev, { struct cn10k_ddr_pmu *pmu = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pmu->cpu))); } static struct device_attribute cn10k_ddr_perf_cpumask_attr = diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c b/drivers/perf/marvell_cn10k_tad_pmu.c index 51ccb0befa05..54909d0031b7 100644 --- a/drivers/perf/marvell_cn10k_tad_pmu.c +++ b/drivers/perf/marvell_cn10k_tad_pmu.c @@ -258,7 +258,7 @@ static ssize_t tad_pmu_cpumask_show(struct device *dev, { struct tad_pmu *tad_pmu = to_tad_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(tad_pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(tad_pmu->cpu))); } static DEVICE_ATTR(cpumask, 0444, tad_pmu_cpumask_show, NULL); diff --git a/drivers/perf/marvell_pem_pmu.c b/drivers/perf/marvell_pem_pmu.c index 29fbcd1848e4..cf1d8cdb1318 100644 --- a/drivers/perf/marvell_pem_pmu.c +++ b/drivers/perf/marvell_pem_pmu.c @@ -164,7 +164,7 @@ static ssize_t pem_perf_cpumask_show(struct device *dev, { struct pem_pmu *pmu = dev_get_drvdata(dev); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(pmu->cpu))); } static struct device_attribute pem_perf_cpumask_attr = diff --git a/drivers/perf/nvidia_t410_c2c_pmu.c b/drivers/perf/nvidia_t410_c2c_pmu.c index 411987153ff3..bff875f4f625 100644 --- a/drivers/perf/nvidia_t410_c2c_pmu.c +++ b/drivers/perf/nvidia_t410_c2c_pmu.c @@ -658,7 +658,7 @@ static ssize_t nv_c2c_pmu_cpumask_show(struct device *dev, default: return 0; } - return cpumap_print_to_pagebuf(true, buf, cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask)); } #define NV_C2C_PMU_CPUMASK_ATTR(_name, _config) \ diff --git a/drivers/perf/nvidia_t410_cmem_latency_pmu.c b/drivers/perf/nvidia_t410_cmem_latency_pmu.c index acb8f5571522..6c8e41598ec1 100644 --- a/drivers/perf/nvidia_t410_cmem_latency_pmu.c +++ b/drivers/perf/nvidia_t410_cmem_latency_pmu.c @@ -501,7 +501,7 @@ static ssize_t cmem_lat_pmu_cpumask_show(struct device *dev, default: return 0; } - return cpumap_print_to_pagebuf(true, buf, cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask)); } #define NV_PMU_CPUMASK_ATTR(_name, _config) \ diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c index ea8c85729937..c0c522b10b72 100644 --- a/drivers/perf/qcom_l2_pmu.c +++ b/drivers/perf/qcom_l2_pmu.c @@ -638,7 +638,7 @@ static ssize_t l2_cache_pmu_cpumask_show(struct device *dev, { struct l2cache_pmu *l2cache_pmu = to_l2cache_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, &l2cache_pmu->cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&l2cache_pmu->cpumask)); } static struct device_attribute l2_cache_pmu_cpumask_attr = diff --git a/drivers/perf/qcom_l3_pmu.c b/drivers/perf/qcom_l3_pmu.c index 66e6cabd6fff..c8d259dd1f80 100644 --- a/drivers/perf/qcom_l3_pmu.c +++ b/drivers/perf/qcom_l3_pmu.c @@ -663,7 +663,7 @@ static ssize_t cpumask_show(struct device *dev, { struct l3cache_pmu *l3pmu = to_l3cache_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, &l3pmu->cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&l3pmu->cpumask)); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/starfive_starlink_pmu.c b/drivers/perf/starfive_starlink_pmu.c index b1c7dc4869bd..dc4e653761fc 100644 --- a/drivers/perf/starfive_starlink_pmu.c +++ b/drivers/perf/starfive_starlink_pmu.c @@ -130,7 +130,7 @@ cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) { struct starlink_pmu *starlink_pmu = to_starlink_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, &starlink_pmu->cpumask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&starlink_pmu->cpumask)); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/thunderx2_pmu.c b/drivers/perf/thunderx2_pmu.c index 6ed4707bd6bb..a69c02d2d874 100644 --- a/drivers/perf/thunderx2_pmu.c +++ b/drivers/perf/thunderx2_pmu.c @@ -254,7 +254,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, struct tx2_uncore_pmu *tx2_pmu; tx2_pmu = pmu_to_tx2_pmu(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(tx2_pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(tx2_pmu->cpu))); } static DEVICE_ATTR_RO(cpumask); diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c index 33b5497bdc06..e9e4871db08d 100644 --- a/drivers/perf/xgene_pmu.c +++ b/drivers/perf/xgene_pmu.c @@ -595,7 +595,7 @@ static ssize_t cpumask_show(struct device *dev, { struct xgene_pmu_dev *pmu_dev = to_pmu_dev(dev_get_drvdata(dev)); - return cpumap_print_to_pagebuf(true, buf, &pmu_dev->parent->cpu); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(&pmu_dev->parent->cpu)); } static DEVICE_ATTR_RO(cpumask); diff --git a/kernel/events/core.c b/kernel/events/core.c index ba5bd6a78fe7..fa8dc366e2cf 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -12696,7 +12696,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, struct cpumask *mask = perf_scope_cpumask(pmu->scope); if (mask) - return cpumap_print_to_pagebuf(true, buf, mask); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(mask)); return 0; } From be9eae3e839e8b1b687d4d0f3399c3db1cf796f0 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 2 Jul 2026 11:47:24 -0400 Subject: [PATCH 16/25] lib/bitmap-str: get rid of cpumap_print_to_pagebuf() Now that all users of the function are switched to the alternatives, drop the function. Signed-off-by: Yury Norov --- include/linux/cpumask.h | 19 ------------------- lib/bitmap-str.c | 9 ++++----- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index d3cda0544954..4c8bb6953107 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1315,24 +1314,6 @@ static __always_inline bool cpu_dying(unsigned int cpu) } #endif /* NR_CPUS > BITS_PER_LONG */ -/** - * cpumap_print_to_pagebuf - copies the cpumask into the buffer either - * as comma-separated list of cpus or hex values of cpumask - * @list: indicates whether the cpumap must be list - * @mask: the cpumask to copy - * @buf: the buffer to copy into - * - * Return: the length of the (null-terminated) @buf string, zero if - * nothing is copied. - */ -static __always_inline ssize_t -cpumap_print_to_pagebuf(bool list, char *buf, const struct cpumask *mask) -{ - /* Opencode offset_in_page(buf) to not include linux/mm.h */ - return scnprintf(buf, PAGE_SIZE - ((unsigned long)buf & ~PAGE_MASK), - list ? "%*pbl\n" : "%*pb\n", cpumask_pr_args(mask)); -} - /** * cpumap_print_bitmask_to_buf - copies the cpumask into the buffer as * hex values of cpumask diff --git a/lib/bitmap-str.c b/lib/bitmap-str.c index 26d36c938c6a..dd9aa0635fa5 100644 --- a/lib/bitmap-str.c +++ b/lib/bitmap-str.c @@ -75,8 +75,7 @@ static int bitmap_print_to_buf(bool list, char *buf, const unsigned long *maskp, * @off: in the string from which we are copying, We copy to @buf * @count: the maximum number of bytes to print * - * The sprintf("%*pb[l]") is used indirectly via its cpumap wrapper - * cpumap_print_to_pagebuf() or directly by drivers to export hexadecimal + * The sprintf("%*pb[l]") format is used by drivers to export hexadecimal * bitmask and decimal list to userspace by sysfs ABI. * Drivers might be using a normal attribute for this kind of ABIs. A * normal attribute typically has show entry as below:: @@ -115,9 +114,9 @@ static int bitmap_print_to_buf(bool list, char *buf, const unsigned long *maskp, * parameters such as off, count from bin_attribute show entry to this API. * * The role of cpumap_print_bitmask_to_buf() and cpumap_print_list_to_buf() - * is similar with cpumap_print_to_pagebuf(), the difference is that - * scnprintf("%*pb[l]") mainly serves sysfs attribute with the assumption - * the destination buffer is exactly one page and won't be more than one page. + * is similar to direct sysfs_emit("%*pb[l]") formatting, but the latter + * assumes the destination buffer is exactly one page and won't be more than + * one page. * cpumap_print_bitmask_to_buf() and cpumap_print_list_to_buf(), on the other * hand, mainly serves bin_attribute which doesn't work with exact one page, * and it can break the size limit of converted decimal list and hexadecimal From 36f78b0dfa7df4892ba067ccf00d95a4513b4935 Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Thu, 9 Jul 2026 13:40:06 +0200 Subject: [PATCH 17/25] bitmap: Properly initialise destination bitmap for scatter & gather test Erhard reports failure of bitmap tests on powerpc: test_bitmap: loaded. test_bitmap: [lib/test_bitmap.c:397] bitmaps contents differ: expected "1,3-4,9", got "1,3-4,9,65-71,73-79,81-87,89-95,97-99" test_bitmap: parselist('0-2047:128/256'): 912 test_bitmap: scnprintf("%*pbl", '0-32767'): 5977 test_bitmap: test_bitmap_read_perf: 1191082 test_bitmap: test_bitmap_write_perf: 1270153 test_bitmap: failed 1 out of 208655 tests It happens mainly when CONFIG_INIT_STACK_ALL_PATTERN is set. Commit 6b5a4b687367 ("bitmap: Add test for out-of-boundary modifications for scatter & gather") extended the test to out-of-boundary bits, but those bits were left uninitialised. Properly initialise the entire result bitmap before the test. [Yury: minor commit message tweaks] Reported-by: Erhard Furtner Closes: https://lore.kernel.org/all/ca3547ae-8b79-43a2-a758-23ec980bfd9a@mailbox.org Fixes: 6b5a4b687367 ("bitmap: Add test for out-of-boundary modifications for scatter & gather") Signed-off-by: Christophe Leroy (CS GROUP) Reviewed-by: Andy Shevchenko Signed-off-by: Yury Norov --- lib/test_bitmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index 8665df77c960..56bd23059b26 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -429,6 +429,7 @@ static void __init test_bitmap_sg(void) /* Scatter/gather relationship */ bitmap_zero(bmap_tmp, 100); + bitmap_zero(bmap_res, 100); bitmap_gather(bmap_tmp, bmap_scatter, sg_mask, nbits); bitmap_scatter(bmap_res, bmap_tmp, sg_mask, nbits); expect_eq_bitmap(bmap_scatter, bmap_res, 100); From c4f392a35c4f39ebb2c83979361b01521bc9a82e Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 13 Jul 2026 15:51:04 +0800 Subject: [PATCH 18/25] nodemask: reduce bitmap width to nr_node_ids in __nodemask_pr_numnodes() __nodemask_pr_numnodes() currently returns MAX_NUMNODES as the field width for '%*pb[l]' nodemask printing. MAX_NUMNODES is a compile-time upper bound and can be much larger than the runtime node id range, resulting in excessive zero padding in bitmap-form output. For example, /proc//status prints Mems_allowed with '%*pb' using the nodemask_pr_args() helper. On systems built with MAX_NUMNODES=1024 but booted with a much smaller possible-node range, this produces: Mems_allowed: 00000000,00000000,...,00000003 Switch to nr_node_ids, matching the behavior of cpumask_pr_args() which uses nr_cpu_ids. This reduces the output width from MAX_NUMNODES bits to the runtime node id range: Mems_allowed: 3 Visible impact on in-tree users: - Bitmap format ('%*pb') users: * /proc//status Mems_allowed (format changes as shown above) - List format ('%*pbl') users, output is unchanged, as list formatter only prints set bit ranges: * /sys/devices/system/node/{possible,online,has_normal_memory, ...} * NVMe multipath sysfs numa_nodes * memory tier sysfs nodelist * cpuset cgroup mems and effective_mems files * /proc//status Mems_allowed_list * mempolicy strings in /proc//numa_maps * SLUB debugfs output * Kernel log messages printing nodemasks Move nr_node_ids and nr_online_nodes declarations earlier in the file to allow __nodemask_pr_numnodes() to use nr_node_ids. Cc: Yury Norov Cc: Rasmus Villemoes Cc: Andrew Morton Cc: linux-mm@kvack.org Signed-off-by: Li RongQing Signed-off-by: Yury Norov --- include/linux/nodemask.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h index b842aa525546..607373bd83ee 100644 --- a/include/linux/nodemask.h +++ b/include/linux/nodemask.h @@ -95,6 +95,14 @@ extern nodemask_t _unused_nodemask_arg_; +#if MAX_NUMNODES > 1 +extern unsigned int nr_node_ids; +extern unsigned int nr_online_nodes; +#else +#define nr_node_ids 1U +#define nr_online_nodes 1U +#endif + /** * nodemask_pr_args - printf args to output a nodemask * @maskp: nodemask to be printed @@ -105,7 +113,7 @@ extern nodemask_t _unused_nodemask_arg_; __nodemask_pr_bits(maskp) static __always_inline unsigned int __nodemask_pr_numnodes(const nodemask_t *m) { - return m ? MAX_NUMNODES : 0; + return m ? nr_node_ids : 0; } static __always_inline const unsigned long *__nodemask_pr_bits(const nodemask_t *m) { @@ -438,9 +446,6 @@ static __always_inline unsigned int next_memory_node(int nid) return next_node(nid, node_states[N_MEMORY]); } -extern unsigned int nr_node_ids; -extern unsigned int nr_online_nodes; - static __always_inline void node_set_online(int nid) { node_set_state(nid, N_ONLINE); @@ -480,8 +485,6 @@ static __always_inline int num_node_state(enum node_states state) #define first_memory_node 0 #define next_online_node(nid) (MAX_NUMNODES) #define next_memory_node(nid) (MAX_NUMNODES) -#define nr_node_ids 1U -#define nr_online_nodes 1U #define node_set_online(node) node_set_state((node), N_ONLINE) #define node_set_offline(node) node_clear_state((node), N_ONLINE) From ae44a037ac03d7f8bc8a17da02163c7f5c63bf50 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Sun, 19 Jul 2026 08:27:27 -0400 Subject: [PATCH 19/25] bitmap: drop bitmap_next_set_region() The function is a dead code. Drop it. Signed-off-by: Yury Norov --- include/linux/bitmap.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 8854acf77869..a967e1f7542b 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -697,14 +697,6 @@ void bitmap_gather(unsigned long *dst, const unsigned long *src, __assign_bit(n++, dst, test_bit(bit, src)); } -static __always_inline -void bitmap_next_set_region(unsigned long *bitmap, unsigned int *rs, - unsigned int *re, unsigned int end) -{ - *rs = find_next_bit(bitmap, end, *rs); - *re = find_next_zero_bit(bitmap, end, *rs + 1); -} - /** * bitmap_release_region - release allocated bitmap region * @bitmap: array of unsigned longs corresponding to the bitmap From d00e89988e0192b3a1e413ec095e1699e5d23f2b Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Wed, 8 Jul 2026 22:03:07 -0400 Subject: [PATCH 20/25] ARM: dma-mapping: Treat bitmap size as allocation failure bitmap_find_next_zero_area() uses an out-of-range return value to indicate failure. Check for values greater than or equal to the bitmap size so the caller does not depend on the exact failure sentinel. Acked-by: Marek Szyprowski Signed-off-by: Yury Norov --- arch/arm/mm/dma-mapping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f9bc53b60f99..7761099dde9e 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -773,7 +773,7 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, start = bitmap_find_next_zero_area(mapping->bitmaps[i], mapping->bits, 0, count, align); - if (start > mapping->bits) + if (start >= mapping->bits) continue; bitmap_set(mapping->bitmaps[i], start, count); @@ -794,7 +794,7 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, start = bitmap_find_next_zero_area(mapping->bitmaps[i], mapping->bits, 0, count, align); - if (start > mapping->bits) { + if (start >= mapping->bits) { spin_unlock_irqrestore(&mapping->lock, flags); return DMA_MAPPING_ERROR; } From 6abf50fde2da0f8146fde313ae567277e562f1f9 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Wed, 8 Jul 2026 22:03:08 -0400 Subject: [PATCH 21/25] powerpc/msi: Treat bitmap size as allocation failure bitmap_find_next_zero_area() uses an out-of-range return value to indicate failure. Check for values greater than or equal to the bitmap size so the caller does not depend on the exact failure sentinel. Acked-by: Madhavan Srinivasan Signed-off-by: Yury Norov --- arch/powerpc/sysdev/msi_bitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/msi_bitmap.c b/arch/powerpc/sysdev/msi_bitmap.c index 456a4f64ae0a..2f38d4b41ad3 100644 --- a/arch/powerpc/sysdev/msi_bitmap.c +++ b/arch/powerpc/sysdev/msi_bitmap.c @@ -21,7 +21,7 @@ int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num) offset = bitmap_find_next_zero_area(bmp->bitmap, bmp->irq_count, 0, num, (1 << order) - 1); - if (offset > bmp->irq_count) + if (offset >= bmp->irq_count) goto err; bitmap_set(bmp->bitmap, offset, num); From c69269f941b8c0c527ea2319556df0e21b8f3f52 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Wed, 8 Jul 2026 22:03:09 -0400 Subject: [PATCH 22/25] crypto: ccp: Treat bitmap size as allocation failure bitmap_find_next_zero_area() uses an out-of-range return value to indicate failure. Accept only offsets strictly below the bitmap size so the callers do not depend on the exact failure sentinel. Signed-off-by: Yury Norov --- drivers/crypto/ccp/ccp-dev-v3.c | 2 +- drivers/crypto/ccp/ccp-dev-v5.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/ccp/ccp-dev-v3.c b/drivers/crypto/ccp/ccp-dev-v3.c index fe69053b2394..60a133e23e5f 100644 --- a/drivers/crypto/ccp/ccp-dev-v3.c +++ b/drivers/crypto/ccp/ccp-dev-v3.c @@ -28,7 +28,7 @@ static u32 ccp_alloc_ksb(struct ccp_cmd_queue *cmd_q, unsigned int count) ccp->sb_count, ccp->sb_start, count, 0); - if (start <= ccp->sb_count) { + if (start < ccp->sb_count) { bitmap_set(ccp->sb, start, count); mutex_unlock(&ccp->sb_mutex); diff --git a/drivers/crypto/ccp/ccp-dev-v5.c b/drivers/crypto/ccp/ccp-dev-v5.c index 7b73332d6aa1..dcd6aab51e47 100644 --- a/drivers/crypto/ccp/ccp-dev-v5.c +++ b/drivers/crypto/ccp/ccp-dev-v5.c @@ -47,7 +47,7 @@ static u32 ccp_lsb_alloc(struct ccp_cmd_queue *cmd_q, unsigned int count) MAX_LSB_CNT * LSB_SIZE, 0, count, 0); - if (start <= MAX_LSB_CNT * LSB_SIZE) { + if (start < MAX_LSB_CNT * LSB_SIZE) { bitmap_set(ccp->lsbmap, start, count); mutex_unlock(&ccp->sb_mutex); From e46185c13953500ed868751b944a96974eaca923 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Wed, 8 Jul 2026 22:03:10 -0400 Subject: [PATCH 23/25] media: s5p-mfc: Treat bitmap size as allocation failure bitmap_find_next_zero_area() uses an out-of-range return value to indicate failure. Check for values greater than or equal to the bitmap size so the caller does not depend on the exact failure sentinel. Acked-by: Marek Szyprowski Signed-off-by: Yury Norov --- drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c index 5ba791fa3676..b25ad2a37196 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c @@ -43,7 +43,7 @@ int s5p_mfc_alloc_priv_buf(struct s5p_mfc_dev *dev, unsigned int mem_ctx, if (dev->mem_virt) { start = bitmap_find_next_zero_area(dev->mem_bitmap, bits, 0, count, align); - if (start > bits) + if (start >= bits) goto no_mem; bitmap_set(dev->mem_bitmap, start, count); From bf7e3686b708683efe6b7ee79ae4c83a875dbc6f Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Wed, 8 Jul 2026 22:03:11 -0400 Subject: [PATCH 24/25] bitmap: Return size when no zero area is found Return the bitmap size, rather than size + 1, when bitmap_find_next_zero_area_off() cannot find a suitable area. This matches the conventional find_bit() failure sentinel and still lets callers detect failure with an out-of-range check. Document the public failure contract as a value greater than or equal to the bitmap size, without requiring callers to depend on the exact sentinel. Signed-off-by: Yury Norov --- include/linux/bitmap.h | 3 +++ lib/bitmap.c | 10 ++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index a967e1f7542b..7df1573a409c 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -209,6 +209,9 @@ unsigned long bitmap_find_next_zero_area_off(unsigned long *map, * The @align_mask should be one less than a power of 2; the effect is that * the bit offset of all zero areas this function finds is multiples of that * power of 2. A @align_mask of 0 means no alignment is required. + * + * Return: The bit offset of the found area or a value >= @size + * if no area is found. */ static __always_inline unsigned long bitmap_find_next_zero_area(unsigned long *map, diff --git a/lib/bitmap.c b/lib/bitmap.c index b464d843f4eb..ed685127a107 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -424,6 +424,9 @@ EXPORT_SYMBOL(__bitmap_clear); * The @align_mask should be one less than a power of 2; the effect is that * the bit offset of all zero areas this function finds plus @align_offset * is multiple of that power of 2. + * + * Return: The bit offset of the found area or a value greater than or equal + * to @size if no area is found. */ unsigned long bitmap_find_next_zero_area_off(unsigned long *map, unsigned long size, @@ -448,12 +451,7 @@ unsigned long bitmap_find_next_zero_area_off(unsigned long *map, start = i; } - /* - * Here, returning size + 1 is to maintain consistency - * with the old version, where the return value is always - * greater than size when no zero areas are found. - */ - return size + 1; + return size; } EXPORT_SYMBOL(bitmap_find_next_zero_area_off); From f4806cc63cc65bd752fd72d84937614dca6504ec Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 17 Jul 2026 01:32:40 -0400 Subject: [PATCH 25/25] lib: test bitmap vs IDA vs Maple Tree performance for region allocations Compare the cost of allocating and freeing variable-sized regions using a bitmap, IDA and a Maple Tree. All implementations process the same randomly generated sequence of regions containing up to 32 entries, until the configured capacity is exhausted. The benchmark exercises monotonic allocation into an initially empty pool, followed by reverse-order freeing. It does not model fragmentation or interleaved allocation and freeing, nor does it isolate locking or RCU overhead. Allocation time includes the terminal failed request that detects exhaustion. Run the benchmark at several capacities to show how the approaches scale. Report allocation and free times separately because bitmap, IDA and Maple Tree removal have substantially different costs. On x86/kvm, the output example is: Start testing bitmap vs IDA vs Maple Tree region allocation memory: bitmap is exact; IDA and Maple Tree are lower bounds Type alloc (ns) free (ns) regions capacity memory (B) Bitmap 93457345 176151 60644 1000000 125000 Maple 11758660 12870146 60644 1000000 1552656 IDA 31066416 20870824 60644 1000000 134864 Bitmap 919119 17679 6032 100000 12504 Maple 1158193 1187140 6032 100000 154640 IDA 2759670 2116004 6032 100000 14288 Bitmap 17120 2043 613 10000 1256 Maple 116350 117537 613 10000 15888 IDA 243396 202654 613 10000 1872 Bitmap 1220 262 55 1000 128 Maple 12076 10106 55 1000 1552 IDA 25730 20875 55 1000 144 Bitmap 593 124 18 100 16 Maple 3599 4782 18 100 528 IDA 3266 1960 18 100 144 Bitmap 414 129 10 10 8 Maple 2143 1385 10 10 272 IDA 892 648 10 10 16 Region allocation benchmark complete Reported IDA and Maple Tree memory figures exclude slab overhead and transient allocations. The Maple Tree figure is additionally a lower-bound estimate that assumes fully occupied leaf nodes and excludes internal nodes. IDA has no region-allocation API, so each region is implemented as a sequence of single-ID allocations. The IDs remain contiguous because this benchmark fills an initially empty IDA monotonically. The benchmark is motivated by the discussion linked below about choosing the best data structure for the channel ID pool with the capacity of 2048 IDs for the nova GPU driver. Specifically for 2048 IDs the result is: Bitmap 5112 615 121 2048 256 Maple 78526 59592 121 2048 3344 IDA 165274 117761 121 2048 848 The benchmark accepts a list of up to 64 nonzero capacities to test. For example: insmod region_alloc_benchmark.ko capacities=1024,2048,4096,65536 The list may contain duplicate capacities. Each occurrence generates a new region-size sequence, which is useful for collecting statistical characteristics of the benchmark results. Link: https://lore.kernel.org/all/20260710-chid-maple-v1-1-4ee869055268@nvidia.com/ Tested-by: Eliot Courtney Reviewed-by: Eliot Courtney Signed-off-by: Yury Norov --- MAINTAINERS | 3 + lib/Kconfig.debug | 13 +++ lib/Makefile | 1 + lib/region_alloc_benchmark.c | 217 +++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 lib/region_alloc_benchmark.c diff --git a/MAINTAINERS b/MAINTAINERS index 1ab8736850ea..7f8662a0e5a0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4615,6 +4615,7 @@ F: lib/bitmap.c F: lib/cpumask.c F: lib/find_bit.c F: lib/find_bit_benchmark.c +F: lib/region_alloc_benchmark.c F: lib/test_bitmap.c F: lib/tests/cpumask_kunit.c F: tools/include/linux/bitfield.h @@ -15583,6 +15584,7 @@ F: Documentation/core-api/maple_tree.rst F: include/linux/maple_tree.h F: include/trace/events/maple_tree.h F: lib/maple_tree.c +F: lib/region_alloc_benchmark.c F: lib/test_maple_tree.c F: rust/helpers/maple_tree.c F: rust/kernel/maple_tree.rs @@ -29329,6 +29331,7 @@ F: Documentation/core-api/xarray.rst F: include/linux/idr.h F: include/linux/xarray.h F: lib/idr.c +F: lib/region_alloc_benchmark.c F: lib/test_xarray.c F: lib/xarray.c F: tools/testing/radix-tree diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1244dcac2294..0451dfca7098 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2683,6 +2683,19 @@ config FIND_BIT_BENCHMARK If unsure, say N. +config REGION_ALLOC_BENCHMARK + tristate "Benchmark bitmap, IDA and Maple Tree region allocation" + help + This builds a microbenchmark comparing variable-sized region + allocation using bitmaps, IDA and Maple Tree. The benchmark + runs at initialization time. + + Usage: + insmod region_alloc_benchmark.ko + insmod region_alloc_benchmark.ko capacities=1024,2048,4096,65536 + + If unsure, say N. + config FIND_BIT_BENCHMARK_RUST tristate "Test find_bit functions in Rust" depends on RUST diff --git a/lib/Makefile b/lib/Makefile index 7f75cc6edf94..adb18810e3f7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -64,6 +64,7 @@ obj-y += hexdump.o obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o obj-y += kstrtox.o obj-$(CONFIG_FIND_BIT_BENCHMARK) += find_bit_benchmark.o +obj-$(CONFIG_REGION_ALLOC_BENCHMARK) += region_alloc_benchmark.o obj-$(CONFIG_FIND_BIT_BENCHMARK_RUST) += find_bit_benchmark_rust.o obj-$(CONFIG_TEST_BPF) += test_bpf.o test_dhry-objs := dhry_1.o dhry_2.o dhry_run.o diff --git a/lib/region_alloc_benchmark.c b/lib/region_alloc_benchmark.c new file mode 100644 index 000000000000..e88b4cf55c62 --- /dev/null +++ b/lib/region_alloc_benchmark.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Benchmark bitmap, IDA and Maple Tree allocation of variable-sized regions. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REGION_MAX_SIZE 32 + +static unsigned long *bitmap __initdata; +/* One more request guarantees that even an all-ones trace reaches ENOSPC. */ +static u8 *reg_sz __initdata; +static unsigned long *reg_idx __initdata; +static unsigned long capacities[64] = { 1000000, 100000, 10000, 1000, 100, 10 }; +static unsigned int cap_cnt = 6; + +module_param_array(capacities, ulong, &cap_cnt, 0400); +MODULE_PARM_DESC(capacities, "Region capacities to benchmark"); + +static unsigned long __init benchmark_bitmap(unsigned long cap) +{ + unsigned long cnt, idx; + ktime_t alloc_time, free_time; + size_t sz; + + bitmap_zero(bitmap, cap); + alloc_time = ktime_get(); + for (cnt = 0; cnt <= cap; cnt++) { + idx = bitmap_find_next_zero_area(bitmap, cap, 0, reg_sz[cnt], 0); + if (idx >= cap) + break; + + reg_idx[cnt] = idx; + bitmap_set(bitmap, idx, reg_sz[cnt]); + } + alloc_time = ktime_get() - alloc_time; + + idx = cnt; + + free_time = ktime_get(); + while (idx--) + bitmap_clear(bitmap, reg_idx[idx], reg_sz[idx]); + free_time = ktime_get() - free_time; + + WARN_ON(!bitmap_empty(bitmap, cap)); + + sz = BITS_TO_LONGS(cap) * sizeof(unsigned long); + pr_err("Bitmap %12llu %12llu %8lu %8lu %10zu\n", + alloc_time, free_time, cnt, cap, sz); + + return cnt; +} + +static size_t __init ida_size(unsigned long nr_ids) +{ + unsigned long entries = DIV_ROUND_UP(nr_ids, IDA_BITMAP_BITS); + unsigned long bitmaps = nr_ids / IDA_BITMAP_BITS; + unsigned long nodes = 0; + + if (nr_ids % IDA_BITMAP_BITS > BITS_PER_XA_VALUE) + bitmaps++; + + while (entries > 1) { + entries = DIV_ROUND_UP(entries, XA_CHUNK_SIZE); + nodes += entries; + } + + return sizeof(struct ida) + + bitmaps * sizeof(struct ida_bitmap) + + nodes * sizeof(struct xa_node); +} + +static unsigned long __init benchmark_ida(unsigned long cap) +{ + struct ida ida = IDA_INIT(ida); + unsigned long cnt, idx, off, nr_ids = 0; + ktime_t alloc_time, free_time; + int id = -ENOSPC; + + alloc_time = ktime_get(); + for (cnt = 0; cnt <= cap; cnt++) { + for (off = 0; off < reg_sz[cnt]; off++) { + id = ida_alloc_max(&ida, cap - 1, GFP_KERNEL); + if (id < 0) + break; + + if (!off) + reg_idx[cnt] = id; + } + if (id < 0) { + while (off--) + ida_free(&ida, reg_idx[cnt] + off); + break; + } + WARN_ON(id != reg_idx[cnt] + reg_sz[cnt] - 1); + nr_ids += reg_sz[cnt]; + } + alloc_time = ktime_get() - alloc_time; + + WARN_ON(id != -ENOSPC); + + idx = cnt; + + free_time = ktime_get(); + while (idx--) { + for (off = 0; off < reg_sz[idx]; off++) + ida_free(&ida, reg_idx[idx] + off); + } + free_time = ktime_get() - free_time; + + WARN_ON(!ida_is_empty(&ida)); + + pr_err("IDA %12llu %12llu %8lu %8lu %10zu\n", + alloc_time, free_time, cnt, cap, ida_size(nr_ids)); + + ida_destroy(&ida); + return cnt; +} + +static unsigned long __init benchmark_maple_tree(unsigned long cap) +{ + struct maple_tree mt = MTREE_INIT(mt, MT_FLAGS_ALLOC_RANGE); + unsigned long cnt, idx; + ktime_t alloc_time, free_time; + size_t sz; + int ret; + + alloc_time = ktime_get(); + for (cnt = 0; cnt <= cap; cnt++) { + ret = mtree_alloc_range(&mt, &idx, xa_mk_value(cnt + 1), + reg_sz[cnt], 0, cap - 1, GFP_KERNEL); + if (ret) + break; + + reg_idx[cnt] = idx; + } + alloc_time = ktime_get() - alloc_time; + + WARN_ON(ret != -EBUSY); + + idx = cnt; + + free_time = ktime_get(); + while (idx--) + mtree_erase(&mt, reg_idx[idx]); + free_time = ktime_get() - free_time; + + WARN_ON(!mtree_empty(&mt)); + + /* Minimum storage assuming fully occupied allocation-range leaf nodes. */ + sz = sizeof(mt) + DIV_ROUND_UP(cnt, MAPLE_ARANGE64_SLOTS) * sizeof(struct maple_node); + pr_err("Maple %12llu %12llu %8lu %8lu %10zu\n", + alloc_time, free_time, cnt, cap, sz); + + mtree_destroy(&mt); + return cnt; +} + +static int __init region_alloc_benchmark(void) +{ + unsigned long bitmap_count, ida_count, maple_count; + unsigned long i, max_cap = 0; + int ret = -ENOMEM; + + for (i = 0; i < cap_cnt; i++) { + if (capacities[i] == 0) { + pr_err("capacity must be nonzero\n"); + return -EINVAL; + } + max_cap = max(max_cap, capacities[i]); + } + + bitmap = kvmalloc_array(BITS_TO_LONGS(max_cap), sizeof(*bitmap), GFP_KERNEL); + reg_sz = kvmalloc_array(max_cap + 1, sizeof(*reg_sz), GFP_KERNEL); + reg_idx = kvmalloc_array(max_cap, sizeof(*reg_idx), GFP_KERNEL); + if (!bitmap || !reg_sz || !reg_idx) + goto out; + + pr_err("\nStart testing bitmap vs IDA vs Maple Tree region allocation\n"); + pr_err("memory: bitmap is exact; IDA and Maple Tree are lower bounds\n"); + pr_err("Type alloc (ns) free (ns) regions capacity memory (B)\n"); + + for (i = 0; i < cap_cnt; i++) { + unsigned long idx, max_size; + + max_size = min(REGION_MAX_SIZE, capacities[i] / 10) ? : 1; + for (idx = 0; idx <= capacities[i]; idx++) + reg_sz[idx] = get_random_u32_below(max_size) + 1; + + bitmap_count = benchmark_bitmap(capacities[i]); + maple_count = benchmark_maple_tree(capacities[i]); + ida_count = benchmark_ida(capacities[i]); + + WARN_ON(bitmap_count != ida_count); + WARN_ON(bitmap_count != maple_count); + } + + /* Return an error so the benchmark can run repeatedly without rmmod. */ + pr_info("Region allocation benchmark complete\n"); + ret = -EAGAIN; +out: + kvfree(reg_idx); + kvfree(reg_sz); + kvfree(bitmap); + return ret; +} +module_init(region_alloc_benchmark); + +MODULE_AUTHOR("Yury Norov "); +MODULE_DESCRIPTION("Benchmark bitmap, IDA and Maple Tree region allocation"); +MODULE_LICENSE("GPL");