Commit e722d8da authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Ingo Molnar

profile: Convert to hotplug state machine

Install the callbacks via the state machine and let the core invoke
the callbacks on the already online CPUs. A lot of code is removed because
the for-loop is used and create_hash_tables() is removed since its purpose
is covered by the startup / teardown hooks.
Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarAnna-Maria Gleixner <anna-maria@linutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: rt@linutronix.de
Link: http://lkml.kernel.org/r/20160713153337.649867675@linutronix.deSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 24f73b99
...@@ -16,6 +16,7 @@ enum cpuhp_state { ...@@ -16,6 +16,7 @@ enum cpuhp_state {
CPUHP_X86_APB_DEAD, CPUHP_X86_APB_DEAD,
CPUHP_WORKQUEUE_PREP, CPUHP_WORKQUEUE_PREP,
CPUHP_HRTIMERS_PREPARE, CPUHP_HRTIMERS_PREPARE,
CPUHP_PROFILE_PREPARE,
CPUHP_TIMERS_DEAD, CPUHP_TIMERS_DEAD,
CPUHP_NOTIFY_PREPARE, CPUHP_NOTIFY_PREPARE,
CPUHP_BRINGUP_CPU, CPUHP_BRINGUP_CPU,
......
...@@ -328,68 +328,57 @@ static void do_profile_hits(int type, void *__pc, unsigned int nr_hits) ...@@ -328,68 +328,57 @@ static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
put_cpu(); put_cpu();
} }
static int profile_cpu_callback(struct notifier_block *info, static int profile_dead_cpu(unsigned int cpu)
unsigned long action, void *__cpu)
{ {
int node, cpu = (unsigned long)__cpu;
struct page *page; struct page *page;
int i;
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
node = cpu_to_mem(cpu);
per_cpu(cpu_profile_flip, cpu) = 0;
if (!per_cpu(cpu_profile_hits, cpu)[1]) {
page = __alloc_pages_node(node,
GFP_KERNEL | __GFP_ZERO,
0);
if (!page)
return notifier_from_errno(-ENOMEM);
per_cpu(cpu_profile_hits, cpu)[1] = page_address(page);
}
if (!per_cpu(cpu_profile_hits, cpu)[0]) {
page = __alloc_pages_node(node,
GFP_KERNEL | __GFP_ZERO,
0);
if (!page)
goto out_free;
per_cpu(cpu_profile_hits, cpu)[0] = page_address(page);
}
break;
out_free:
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
per_cpu(cpu_profile_hits, cpu)[1] = NULL;
__free_page(page);
return notifier_from_errno(-ENOMEM);
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
if (prof_cpu_mask != NULL)
cpumask_set_cpu(cpu, prof_cpu_mask);
break;
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
case CPU_DEAD_FROZEN:
if (prof_cpu_mask != NULL) if (prof_cpu_mask != NULL)
cpumask_clear_cpu(cpu, prof_cpu_mask); cpumask_clear_cpu(cpu, prof_cpu_mask);
if (per_cpu(cpu_profile_hits, cpu)[0]) {
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]); for (i = 0; i < 2; i++) {
per_cpu(cpu_profile_hits, cpu)[0] = NULL; if (per_cpu(cpu_profile_hits, cpu)[i]) {
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[i]);
per_cpu(cpu_profile_hits, cpu)[i] = NULL;
__free_page(page); __free_page(page);
} }
if (per_cpu(cpu_profile_hits, cpu)[1]) {
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
per_cpu(cpu_profile_hits, cpu)[1] = NULL;
__free_page(page);
} }
break; return 0;
}
static int profile_prepare_cpu(unsigned int cpu)
{
int i, node = cpu_to_mem(cpu);
struct page *page;
per_cpu(cpu_profile_flip, cpu) = 0;
for (i = 0; i < 2; i++) {
if (per_cpu(cpu_profile_hits, cpu)[i])
continue;
page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
if (!page) {
profile_dead_cpu(cpu);
return -ENOMEM;
} }
return NOTIFY_OK; per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
}
return 0;
} }
static int profile_online_cpu(unsigned int cpu)
{
if (prof_cpu_mask != NULL)
cpumask_set_cpu(cpu, prof_cpu_mask);
return 0;
}
#else /* !CONFIG_SMP */ #else /* !CONFIG_SMP */
#define profile_flip_buffers() do { } while (0) #define profile_flip_buffers() do { } while (0)
#define profile_discard_flip_buffers() do { } while (0) #define profile_discard_flip_buffers() do { } while (0)
#define profile_cpu_callback NULL
static void do_profile_hits(int type, void *__pc, unsigned int nr_hits) static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
{ {
...@@ -531,83 +520,43 @@ static const struct file_operations proc_profile_operations = { ...@@ -531,83 +520,43 @@ static const struct file_operations proc_profile_operations = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
#ifdef CONFIG_SMP int __ref create_proc_profile(void)
static void profile_nop(void *unused)
{ {
} struct proc_dir_entry *entry;
#ifdef CONFIG_SMP
static int create_hash_tables(void) enum cpuhp_state online_state;
{
int cpu;
for_each_online_cpu(cpu) {
int node = cpu_to_mem(cpu);
struct page *page;
page = __alloc_pages_node(node,
GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
0);
if (!page)
goto out_cleanup;
per_cpu(cpu_profile_hits, cpu)[1]
= (struct profile_hit *)page_address(page);
page = __alloc_pages_node(node,
GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
0);
if (!page)
goto out_cleanup;
per_cpu(cpu_profile_hits, cpu)[0]
= (struct profile_hit *)page_address(page);
}
return 0;
out_cleanup:
prof_on = 0;
smp_mb();
on_each_cpu(profile_nop, NULL, 1);
for_each_online_cpu(cpu) {
struct page *page;
if (per_cpu(cpu_profile_hits, cpu)[0]) {
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
per_cpu(cpu_profile_hits, cpu)[0] = NULL;
__free_page(page);
}
if (per_cpu(cpu_profile_hits, cpu)[1]) {
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
per_cpu(cpu_profile_hits, cpu)[1] = NULL;
__free_page(page);
}
}
return -1;
}
#else
#define create_hash_tables() ({ 0; })
#endif #endif
int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */
{
struct proc_dir_entry *entry;
int err = 0; int err = 0;
if (!prof_on) if (!prof_on)
return 0; return 0;
#ifdef CONFIG_SMP
err = cpuhp_setup_state(CPUHP_PROFILE_PREPARE, "PROFILE_PREPARE",
profile_prepare_cpu, profile_dead_cpu);
if (err)
return err;
cpu_notifier_register_begin(); err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PROFILE_ONLINE",
profile_online_cpu, NULL);
if (create_hash_tables()) { if (err < 0)
err = -ENOMEM; goto err_state_prep;
goto out; online_state = err;
} err = 0;
#endif
entry = proc_create("profile", S_IWUSR | S_IRUGO, entry = proc_create("profile", S_IWUSR | S_IRUGO,
NULL, &proc_profile_operations); NULL, &proc_profile_operations);
if (!entry) if (!entry)
goto out; goto err_state_onl;
proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t)); proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t));
__hotcpu_notifier(profile_cpu_callback, 0);
out: return err;
cpu_notifier_register_done(); err_state_onl:
#ifdef CONFIG_SMP
cpuhp_remove_state(online_state);
err_state_prep:
cpuhp_remove_state(CPUHP_PROFILE_PREPARE);
#endif
return err; return err;
} }
subsys_initcall(create_proc_profile); subsys_initcall(create_proc_profile);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment