mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02: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
					
				
					 2 changed files with 68 additions and 118 deletions
				
			
		|  | @ -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 @@ 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; | ||||
| 
 | ||||
| 	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 a new issue
	
	 Sebastian Andrzej Siewior
						Sebastian Andrzej Siewior