Skip to content

Commit 0c09ab9

Browse files
committed
cpu/hotplug: Cache number of online CPUs
Re-evaluating the bitmap wheight of the online cpus bitmap in every invocation of num_online_cpus() over and over is a pretty useless exercise. Especially when num_online_cpus() is used in code paths like the IPI delivery of x86 or the membarrier code. Cache the number of online CPUs in the core and just return the cached variable. The accessor function provides only a snapshot when used without protection against concurrent CPU hotplug. The storage needs to use an atomic_t because the kexec and reboot code (ab)use set_cpu_online() in their 'shutdown' handlers without any form of serialization as pointed out by Mathieu. Regular CPU hotplug usage is properly serialized. Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Mathieu Desnoyers <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent b9fa644 commit 0c09ab9

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

include/linux/cpumask.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/kernel.h>
1111
#include <linux/threads.h>
1212
#include <linux/bitmap.h>
13+
#include <linux/atomic.h>
1314
#include <linux/bug.h>
1415

1516
/* Don't assign or return these: may not be this big! */
@@ -95,8 +96,21 @@ extern struct cpumask __cpu_active_mask;
9596
#define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask)
9697
#define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask)
9798

99+
extern atomic_t __num_online_cpus;
100+
98101
#if NR_CPUS > 1
99-
#define num_online_cpus() cpumask_weight(cpu_online_mask)
102+
/**
103+
* num_online_cpus() - Read the number of online CPUs
104+
*
105+
* Despite the fact that __num_online_cpus is of type atomic_t, this
106+
* interface gives only a momentary snapshot and is not protected against
107+
* concurrent CPU hotplug operations unless invoked from a cpuhp_lock held
108+
* region.
109+
*/
110+
static inline unsigned int num_online_cpus(void)
111+
{
112+
return atomic_read(&__num_online_cpus);
113+
}
100114
#define num_possible_cpus() cpumask_weight(cpu_possible_mask)
101115
#define num_present_cpus() cpumask_weight(cpu_present_mask)
102116
#define num_active_cpus() cpumask_weight(cpu_active_mask)
@@ -821,14 +835,7 @@ set_cpu_present(unsigned int cpu, bool present)
821835
cpumask_clear_cpu(cpu, &__cpu_present_mask);
822836
}
823837

824-
static inline void
825-
set_cpu_online(unsigned int cpu, bool online)
826-
{
827-
if (online)
828-
cpumask_set_cpu(cpu, &__cpu_online_mask);
829-
else
830-
cpumask_clear_cpu(cpu, &__cpu_online_mask);
831-
}
838+
void set_cpu_online(unsigned int cpu, bool online);
832839

833840
static inline void
834841
set_cpu_active(unsigned int cpu, bool active)

kernel/cpu.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,6 +2298,9 @@ EXPORT_SYMBOL(__cpu_present_mask);
22982298
struct cpumask __cpu_active_mask __read_mostly;
22992299
EXPORT_SYMBOL(__cpu_active_mask);
23002300

2301+
atomic_t __num_online_cpus __read_mostly;
2302+
EXPORT_SYMBOL(__num_online_cpus);
2303+
23012304
void init_cpu_present(const struct cpumask *src)
23022305
{
23032306
cpumask_copy(&__cpu_present_mask, src);
@@ -2313,6 +2316,27 @@ void init_cpu_online(const struct cpumask *src)
23132316
cpumask_copy(&__cpu_online_mask, src);
23142317
}
23152318

2319+
void set_cpu_online(unsigned int cpu, bool online)
2320+
{
2321+
/*
2322+
* atomic_inc/dec() is required to handle the horrid abuse of this
2323+
* function by the reboot and kexec code which invoke it from
2324+
* IPI/NMI broadcasts when shutting down CPUs. Invocation from
2325+
* regular CPU hotplug is properly serialized.
2326+
*
2327+
* Note, that the fact that __num_online_cpus is of type atomic_t
2328+
* does not protect readers which are not serialized against
2329+
* concurrent hotplug operations.
2330+
*/
2331+
if (online) {
2332+
if (!cpumask_test_and_set_cpu(cpu, &__cpu_online_mask))
2333+
atomic_inc(&__num_online_cpus);
2334+
} else {
2335+
if (cpumask_test_and_clear_cpu(cpu, &__cpu_online_mask))
2336+
atomic_dec(&__num_online_cpus);
2337+
}
2338+
}
2339+
23162340
/*
23172341
* Activate the first processor.
23182342
*/

0 commit comments

Comments
 (0)