|
@@ -328,68 +328,57 @@ out:
|
|
put_cpu();
|
|
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;
|
|
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);
|
|
__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 */
|
|
#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
|
|
|
|
-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
|
|
#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;
|
|
-
|
|
|
|
- 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,
|
|
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:
|
|
|
|
- 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;
|
|
return err;
|
|
}
|
|
}
|
|
subsys_initcall(create_proc_profile);
|
|
subsys_initcall(create_proc_profile);
|