Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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.de


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 24f73b99
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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,
+65 −116
Original line number Diff line number Diff line
@@ -328,68 +328,57 @@ static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
	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;

	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);
	}
		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 */
#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;
#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);