profile: Convert to hotplug state machine
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Wed, 13 Jul 2016 17:16:59 +0000 (17:16 +0000)
committerIngo Molnar <mingo@kernel.org>
Fri, 15 Jul 2016 08:41:42 +0000 (10:41 +0200)
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>
include/linux/cpuhotplug.h
kernel/profile.c

index 15d46d2a1d1601d90b2424ba2ce83127c37708e3..ace5ad0fc3eccad929d6305b6e69707a475e3eff 100644 (file)
@@ -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,
index c2199e9901c9e636da0082fcee65e11ba299fa64..2dbccf2d806c66d76a59a208cb6a000819bf5db4 100644 (file)
@@ -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;
 
-       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)
-                       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;
+       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);
                }
-               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 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;
                }
-               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 __ref create_proc_profile(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; })
+       struct proc_dir_entry *entry;
+#ifdef CONFIG_SMP
+       enum cpuhp_state online_state;
 #endif
 
-int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */
-{
-       struct proc_dir_entry *entry;
        int err = 0;
 
        if (!prof_on)
                return 0;
-
-       cpu_notifier_register_begin();
-
-       if (create_hash_tables()) {
-               err = -ENOMEM;
-               goto out;
-       }
-
+#ifdef CONFIG_SMP
+       err = cpuhp_setup_state(CPUHP_PROFILE_PREPARE, "PROFILE_PREPARE",
+                               profile_prepare_cpu, profile_dead_cpu);
+       if (err)
+               return err;
+
+       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);