[PATCH RFC] sysfs/cpu: add attributes for count of cpus and last cpu index

From: Konstantin Khlebnikov
Date: Sun May 25 2014 - 10:31:31 EST


Surprisingly there is no straight way to get number of processors.
Sysfs attributes /sys/devices/system/cpu/online, "present" and "possible"
shows cpus bitmap as comma-separated list of numbers and ranges. This format
is human-freindly but it's hard to parse and unusable for scripts.

This patchs adds new attributes into /sys/devices/system/cpu/
nr_online, nr_present, nr_possible, last_online, last_present, last_possible.

nr_* shows count of cpus as plain decimal number.
last_* shows highest index, it might not equal to NR-1 if bitmap is sparse.
This might be useful for preallocating arrays for ranges of cpu indexes.

Signed-off-by: Konstantin Khlebnikov <koct9i@xxxxxxxxx>

---

Currently GNU C Library does weird things in the implementation of get_nprocs()
and get_nprocs_conf() (sysconf(_SC_NPROCESSORS_ONLN / _SC_NPROCESSORS_CONF)).

get_nprocs() parses "/sys/devices/system/cpu/online", counts lines started with
"cpu" in "/proc/stat" and even parses "/proc/cpuinfo".
And because all this is expensive it caches result for one second.

get_nprocs_conf() counts subdirs in /sys/devices/system/cpu named "cpu*"
It also parses "/proc/cpuinfo" on alpha and sparc. This function returns
count of present CPUs, probably it should return number of possible CPUs
otherwise userspace isn't able to handle cpu-hotplug.
This should be common situation for virtual-machines like XEN or KVM.

If nothing works well both functions return 1. They cannot report error.

For example hotspot JVM uses sysconf(_SC_NPROCESSORS_CONF) to detect
UP machine where it can drop 'lock' prefix in atomic operations.
If sysfs and proc aren't available it crashes or hangs inside GC =)
[ http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/linux/vm/os_linux.cpp#l291 ]

Other user which I've found is userspace-RCU. It uses per-cpu arrays indexed by
getcpu() and uses sysconf(_SC_NPROCESSORS_CONF) - 1 as last possible cpu.
So this doesn't work for sparse bitmap, also I'm not sure about cpu-hotplug.
---
Documentation/ABI/testing/sysfs-devices-system-cpu | 17 +++++++++
drivers/base/cpu.c | 37 +++++++++++++++++++-
2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index d5a0d33..e05edb7 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -37,6 +37,23 @@ Description: CPU topology files that describe kernel limits related to
See Documentation/cputopology.txt for more information.


+What: /sys/devices/system/cpu/nr_online
+ /sys/devices/system/cpu/nr_present
+ /sys/devices/system/cpu/nr_possible
+ /sys/devices/system/cpu/last_online
+ /sys/devices/system/cpu/last_present
+ /sys/devices/system/cpu/last_possible
+Date: May 2014
+Contact: Linux kernel mailing list <linux-kernel@xxxxxxxxxxxxxxx>
+Description: Number and last index of processors in the system
+
+ nr_{online,possible,present}: count of cpus
+
+ last_{online,possible,present}: highest cpu index
+
+ See also /sys/devices/system/cpu/{online,possible,present} above
+
+
What: /sys/devices/system/cpu/probe
/sys/devices/system/cpu/release
Date: November 2009
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 006b1bc..1ca73c7 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -214,14 +214,43 @@ static ssize_t show_cpus_attr(struct device *dev,
return n;
}

+static ssize_t show_nr_cpus_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", cpumask_weight(*ca->map));
+}
+
+static ssize_t show_last_cpus_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
+ unsigned last_cpu = find_last_bit(cpumask_bits(*ca->map), NR_CPUS);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", last_cpu);
+}
+
#define _CPU_ATTR(name, map) \
{ __ATTR(name, 0444, show_cpus_attr, NULL), map }

-/* Keep in sync with cpu_subsys_attrs */
+#define _NR_CPU_ATTR(name, map) \
+ { __ATTR(name, 0444, show_nr_cpus_attr, NULL), map }
+
+#define _LAST_CPU_ATTR(name, map) \
+ { __ATTR(name, 0444, show_last_cpus_attr, NULL), map }
+
+/* Keep in sync with cpu_root_attrs */
static struct cpu_attr cpu_attrs[] = {
_CPU_ATTR(online, &cpu_online_mask),
_CPU_ATTR(possible, &cpu_possible_mask),
_CPU_ATTR(present, &cpu_present_mask),
+ _NR_CPU_ATTR(nr_online, &cpu_online_mask),
+ _NR_CPU_ATTR(nr_possible, &cpu_possible_mask),
+ _NR_CPU_ATTR(nr_present, &cpu_present_mask),
+ _LAST_CPU_ATTR(last_online, &cpu_online_mask),
+ _LAST_CPU_ATTR(last_possible, &cpu_possible_mask),
+ _LAST_CPU_ATTR(last_present, &cpu_present_mask),
};

/*
@@ -378,6 +407,12 @@ static struct attribute *cpu_root_attrs[] = {
&cpu_attrs[0].attr.attr,
&cpu_attrs[1].attr.attr,
&cpu_attrs[2].attr.attr,
+ &cpu_attrs[3].attr.attr,
+ &cpu_attrs[4].attr.attr,
+ &cpu_attrs[5].attr.attr,
+ &cpu_attrs[6].attr.attr,
+ &cpu_attrs[7].attr.attr,
+ &cpu_attrs[8].attr.attr,
&dev_attr_kernel_max.attr,
&dev_attr_offline.attr,
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/