[PATCH] x86/msr: Use num_possible_cpus() for chrdev minor count
From: Roman 'Hedin' Storozhenko
Date: Wed Apr 01 2026 - 14:56:15 EST
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.
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>