mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +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_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 a new issue
	
	 Sebastian Andrzej Siewior
						Sebastian Andrzej Siewior