RE: [PATCH] x86/msr: Use num_possible_cpus() for chrdev minor count
From: Michael Kelley
Date: Wed Apr 01 2026 - 15:29:08 EST
From: Roman 'Hedin' Storozhenko <romeusmeister@xxxxxxxxx> Sent: Wednesday, April 1, 2026 11:56 AM
>
> The MSR driver currently register their character device
> regions using NR_CPUS as the minor count:
>
> __register_chrdev(..., 0, NR_CPUS, ...)
>
> This dates back to commit 0b962d473af3 ("x86, msr/cpuid: Register
> enough minors for the MSR and CPUID drivers"), which intentionally
> used NR_CPUS as a static upper bound on the maximum CPU index.
>
> However, NR_CPUS is a compile-time upper bound and may significantly
> exceed the actual number of CPUs supported by the running system
> (i.e. cpu_possible_mask). As a result, the drivers may reserve a much
> larger minor range than can ever be used at runtime.
>
> Replace NR_CPUS with num_possible_cpus() so that the registered minor
> range reflects the actual system topology instead of a static maximum.
Using num_possible_cpus() introduces the potential for a different
problem. There's no guarantee that the cpu_possible_mask is dense,
so num_possible_cpus() might be smaller than the largest possible
Linux cpu number. Since msr_device_create() uses the Linux cpu number
as the minor device number, that minor number could exceed the size
of the registered minor range.
There's no problem seen today because I believe the possible_cpus
bitmap is currently always dense, but there's the potential in the future
that it is not. See email thread [1].
Instead of num_possible_cpus(), use nr_cpu_ids.
Michael
[1] https://lore.kernel.org/lkml/SN6PR02MB4157210CC36B2593F8572E5ED4692@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
>
> The driver already creates and destroy devices dynamically based on
> CPU hotplug state, so this change only affects the size of the
> internally reserved minor range and does not alter runtime behavior
> or userspace-visible interfaces.
>
> Signed-off-by: Roman 'Hedin' Storozhenko <romeusmeister@xxxxxxxxx>
> ---
> This series replaces NR_CPUS with num_possible_cpus() when registering
> character device regions for the MSR and CPUID drivers.
>
> Historically, commit 0b962d473af3 ("x86, msr/cpuid: Register enough minors
> for the MSR and CPUID drivers") switched to using NR_CPUS as a static upper
> bound on the maximum CPU index. While correct, this may result in reserving
> a significantly larger minor range than is actually usable on a given system.
>
> This series updates the drivers to use num_possible_cpus(), aligning the
> registered minor range with the system's CPU topology.
>
> The change does not affect runtime behavior: devices are still created and
> destroyed dynamically based on CPU hotplug state.
>
> Validation
> ----------
>
> The change was validated in a virtme-ng environment with:
>
> CONFIG_NR_CPUS=64
> possible CPUs: 0-7 (8 CPUs)
>
> 1) Internal state (eBPF)
>
> Using bpftrace on __register_chrdev_region():
>
> Before:
> count = 64
>
> After:
> count = 8
>
> This confirms that the registered minor range now reflects
> num_possible_cpus() instead of NR_CPUS.
>
> 2) Userspace-visible behavior
>
> No change observed:
>
> /dev/cpu/*/msr count: unchanged (8)
> /sys/class/msr entries: unchanged (8)
>
> 3) Functional correctness
>
> rdmsr access continues to work as expected.
>
> 4) CPU hotplug
>
> Tracing msr_device_create shows identical behavior before and after,
> confirming that device lifecycle remains driven by CPU hotplug events.
>
> ----------
> Overall, this change reduces over-allocation of minor numbers while
> preserving existing behavior and interfaces.
> ---
> arch/x86/kernel/msr.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c
> index 4469c784eaa0..7f93f59d3cd9 100644
> --- a/arch/x86/kernel/msr.c
> +++ b/arch/x86/kernel/msr.c
> @@ -263,7 +263,7 @@ static int __init msr_init(void)
> {
> int err;
>
> - if (__register_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr", &msr_fops)) {
> + if (__register_chrdev(MSR_MAJOR, 0, num_possible_cpus(), "cpu/msr",
> &msr_fops)) {
> pr_err("unable to get major %d for msr\n", MSR_MAJOR);
> return -EBUSY;
> }
> @@ -281,7 +281,7 @@ static int __init msr_init(void)
> out_class:
> class_unregister(&msr_class);
> out_chrdev:
> - __unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
> + __unregister_chrdev(MSR_MAJOR, 0, num_possible_cpus(), "cpu/msr");
> return err;
> }
> module_init(msr_init);
> @@ -290,7 +290,7 @@ static void __exit msr_exit(void)
> {
> cpuhp_remove_state(cpuhp_msr_state);
> class_unregister(&msr_class);
> - __unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
> + __unregister_chrdev(MSR_MAJOR, 0, num_possible_cpus(), "cpu/msr");
> }
> module_exit(msr_exit)
>
>
> ---
> base-commit: 25f9333c7244d9c5a0243bcf045903ba19635d35
> change-id: 20260401-msr_cpu-d88ade162ca0
>
> Best regards,
> --
> Roman 'Hedin' Storozhenko <romeusmeister@xxxxxxxxx>
>