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: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Anna-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.de Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
24f73b9971
commit
e722d8daaf
|
@ -16,6 +16,7 @@ enum cpuhp_state {
|
|||
CPUHP_X86_APB_DEAD,
|
||||
CPUHP_WORKQUEUE_PREP,
|
||||
CPUHP_HRTIMERS_PREPARE,
|
||||
CPUHP_PROFILE_PREPARE,
|
||||
CPUHP_TIMERS_DEAD,
|
||||
CPUHP_NOTIFY_PREPARE,
|
||||
CPUHP_BRINGUP_CPU,
|
||||
|
|
185
kernel/profile.c
185
kernel/profile.c
|
@ -328,68 +328,57 @@ out:
|
|||
put_cpu();
|
||||
}
|
||||
|
||||
static int profile_cpu_callback(struct notifier_block *info,
|
||||
unsigned long action, void *__cpu)
|
||||
static int profile_dead_cpu(unsigned int cpu)
|
||||
{
|
||||
int node, cpu = (unsigned long)__cpu;
|
||||
struct page *page;
|
||||
int i;
|
||||
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_clear_cpu(cpu, prof_cpu_mask);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int profile_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
int i, node = cpu_to_mem(cpu);
|
||||
struct page *page;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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)
|
||||
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]);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
|
||||
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
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 */
|
||||
#define profile_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)
|
||||
{
|
||||
|
@ -531,83 +520,43 @@ static const struct file_operations proc_profile_operations = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void profile_nop(void *unused)
|
||||
{
|
||||
}
|
||||
|
||||
static int create_hash_tables(void)
|
||||
{
|
||||
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
|
||||
|
||||
int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */
|
||||
int __ref create_proc_profile(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
#ifdef CONFIG_SMP
|
||||
enum cpuhp_state online_state;
|
||||
#endif
|
||||
|
||||
int err = 0;
|
||||
|
||||
if (!prof_on)
|
||||
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();
|
||||
|
||||
if (create_hash_tables()) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PROFILE_ONLINE",
|
||||
profile_online_cpu, NULL);
|
||||
if (err < 0)
|
||||
goto err_state_prep;
|
||||
online_state = err;
|
||||
err = 0;
|
||||
#endif
|
||||
entry = proc_create("profile", S_IWUSR | S_IRUGO,
|
||||
NULL, &proc_profile_operations);
|
||||
if (!entry)
|
||||
goto out;
|
||||
goto err_state_onl;
|
||||
proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t));
|
||||
__hotcpu_notifier(profile_cpu_callback, 0);
|
||||
|
||||
out:
|
||||
cpu_notifier_register_done();
|
||||
return err;
|
||||
err_state_onl:
|
||||
#ifdef CONFIG_SMP
|
||||
cpuhp_remove_state(online_state);
|
||||
err_state_prep:
|
||||
cpuhp_remove_state(CPUHP_PROFILE_PREPARE);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
subsys_initcall(create_proc_profile);
|
||||
|
|
Loading…
Reference in New Issue