mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
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_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,
|
||||||
|
185
kernel/profile.c
185
kernel/profile.c
@ -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;
|
||||||
|
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;
|
struct page *page;
|
||||||
|
|
||||||
switch (action) {
|
per_cpu(cpu_profile_flip, cpu) = 0;
|
||||||
case CPU_UP_PREPARE:
|
|
||||||
case CPU_UP_PREPARE_FROZEN:
|
for (i = 0; i < 2; i++) {
|
||||||
node = cpu_to_mem(cpu);
|
if (per_cpu(cpu_profile_hits, cpu)[i])
|
||||||
per_cpu(cpu_profile_flip, cpu) = 0;
|
continue;
|
||||||
if (!per_cpu(cpu_profile_hits, cpu)[1]) {
|
|
||||||
page = __alloc_pages_node(node,
|
page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
|
||||||
GFP_KERNEL | __GFP_ZERO,
|
if (!page) {
|
||||||
0);
|
profile_dead_cpu(cpu);
|
||||||
if (!page)
|
return -ENOMEM;
|
||||||
return notifier_from_errno(-ENOMEM);
|
|
||||||
per_cpu(cpu_profile_hits, cpu)[1] = page_address(page);
|
|
||||||
}
|
}
|
||||||
if (!per_cpu(cpu_profile_hits, cpu)[0]) {
|
per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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 */
|
#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 = {
|
|||||||
.llseek = default_llseek,
|
.llseek = default_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
int __ref create_proc_profile(void)
|
||||||
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 */
|
|
||||||
{
|
{
|
||||||
struct proc_dir_entry *entry;
|
struct proc_dir_entry *entry;
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
enum cpuhp_state online_state;
|
||||||
|
#endif
|
||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user