diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2016-07-13 13:16:59 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-07-15 04:41:42 -0400 |
commit | e722d8daafb974b9ad1bbaf42f384a5ea5929f5f (patch) | |
tree | 4606da52182007488af7086c1ff4867ef8f0093f | |
parent | 24f73b99716a9cd8cbb345c41ced6b3b5ed94006 (diff) |
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>
-rw-r--r-- | include/linux/cpuhotplug.h | 1 | ||||
-rw-r--r-- | kernel/profile.c | 181 |
2 files changed, 66 insertions, 116 deletions
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 15d46d2a1d16..ace5ad0fc3ec 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h | |||
@@ -16,6 +16,7 @@ enum cpuhp_state { | |||
16 | CPUHP_X86_APB_DEAD, | 16 | CPUHP_X86_APB_DEAD, |
17 | CPUHP_WORKQUEUE_PREP, | 17 | CPUHP_WORKQUEUE_PREP, |
18 | CPUHP_HRTIMERS_PREPARE, | 18 | CPUHP_HRTIMERS_PREPARE, |
19 | CPUHP_PROFILE_PREPARE, | ||
19 | CPUHP_TIMERS_DEAD, | 20 | CPUHP_TIMERS_DEAD, |
20 | CPUHP_NOTIFY_PREPARE, | 21 | CPUHP_NOTIFY_PREPARE, |
21 | CPUHP_BRINGUP_CPU, | 22 | CPUHP_BRINGUP_CPU, |
diff --git a/kernel/profile.c b/kernel/profile.c index c2199e9901c9..2dbccf2d806c 100644 --- a/kernel/profile.c +++ b/kernel/profile.c | |||
@@ -328,68 +328,57 @@ out: | |||
328 | put_cpu(); | 328 | put_cpu(); |
329 | } | 329 | } |
330 | 330 | ||
331 | static int profile_cpu_callback(struct notifier_block *info, | 331 | static int profile_dead_cpu(unsigned int cpu) |
332 | unsigned long action, void *__cpu) | ||
333 | { | 332 | { |
334 | int node, cpu = (unsigned long)__cpu; | ||
335 | struct page *page; | 333 | struct page *page; |
334 | int i; | ||
336 | 335 | ||
337 | switch (action) { | 336 | if (prof_cpu_mask != NULL) |
338 | case CPU_UP_PREPARE: | 337 | cpumask_clear_cpu(cpu, prof_cpu_mask); |
339 | case CPU_UP_PREPARE_FROZEN: | 338 | |
340 | node = cpu_to_mem(cpu); | 339 | for (i = 0; i < 2; i++) { |
341 | per_cpu(cpu_profile_flip, cpu) = 0; | 340 | if (per_cpu(cpu_profile_hits, cpu)[i]) { |
342 | if (!per_cpu(cpu_profile_hits, cpu)[1]) { | 341 | page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[i]); |
343 | page = __alloc_pages_node(node, | 342 | per_cpu(cpu_profile_hits, cpu)[i] = NULL; |
344 | GFP_KERNEL | __GFP_ZERO, | ||
345 | 0); | ||
346 | if (!page) | ||
347 | return notifier_from_errno(-ENOMEM); | ||
348 | per_cpu(cpu_profile_hits, cpu)[1] = page_address(page); | ||
349 | } | ||
350 | if (!per_cpu(cpu_profile_hits, cpu)[0]) { | ||
351 | page = __alloc_pages_node(node, | ||
352 | GFP_KERNEL | __GFP_ZERO, | ||
353 | 0); | ||
354 | if (!page) | ||
355 | goto out_free; | ||
356 | per_cpu(cpu_profile_hits, cpu)[0] = page_address(page); | ||
357 | } | ||
358 | break; | ||
359 | out_free: | ||
360 | page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]); | ||
361 | per_cpu(cpu_profile_hits, cpu)[1] = NULL; | ||
362 | __free_page(page); | ||
363 | return notifier_from_errno(-ENOMEM); | ||
364 | case CPU_ONLINE: | ||
365 | case CPU_ONLINE_FROZEN: | ||
366 | if (prof_cpu_mask != NULL) | ||
367 | cpumask_set_cpu(cpu, prof_cpu_mask); | ||
368 | break; | ||
369 | case CPU_UP_CANCELED: | ||
370 | case CPU_UP_CANCELED_FROZEN: | ||
371 | case CPU_DEAD: | ||
372 | case CPU_DEAD_FROZEN: | ||
373 | if (prof_cpu_mask != NULL) | ||
374 | cpumask_clear_cpu(cpu, prof_cpu_mask); | ||
375 | if (per_cpu(cpu_profile_hits, cpu)[0]) { | ||
376 | page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]); | ||
377 | per_cpu(cpu_profile_hits, cpu)[0] = NULL; | ||
378 | __free_page(page); | 343 | __free_page(page); |
379 | } | 344 | } |
380 | if (per_cpu(cpu_profile_hits, cpu)[1]) { | 345 | } |
381 | page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]); | 346 | return 0; |
382 | per_cpu(cpu_profile_hits, cpu)[1] = NULL; | 347 | } |
383 | __free_page(page); | 348 | |
349 | static int profile_prepare_cpu(unsigned int cpu) | ||
350 | { | ||
351 | int i, node = cpu_to_mem(cpu); | ||
352 | struct page *page; | ||
353 | |||
354 | per_cpu(cpu_profile_flip, cpu) = 0; | ||
355 | |||
356 | for (i = 0; i < 2; i++) { | ||
357 | if (per_cpu(cpu_profile_hits, cpu)[i]) | ||
358 | continue; | ||
359 | |||
360 | page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); | ||
361 | if (!page) { | ||
362 | profile_dead_cpu(cpu); | ||
363 | return -ENOMEM; | ||
384 | } | 364 | } |
385 | break; | 365 | per_cpu(cpu_profile_hits, cpu)[i] = page_address(page); |
366 | |||
386 | } | 367 | } |
387 | return NOTIFY_OK; | 368 | return 0; |
369 | } | ||
370 | |||
371 | static int profile_online_cpu(unsigned int cpu) | ||
372 | { | ||
373 | if (prof_cpu_mask != NULL) | ||
374 | cpumask_set_cpu(cpu, prof_cpu_mask); | ||
375 | |||
376 | return 0; | ||
388 | } | 377 | } |
378 | |||
389 | #else /* !CONFIG_SMP */ | 379 | #else /* !CONFIG_SMP */ |
390 | #define profile_flip_buffers() do { } while (0) | 380 | #define profile_flip_buffers() do { } while (0) |
391 | #define profile_discard_flip_buffers() do { } while (0) | 381 | #define profile_discard_flip_buffers() do { } while (0) |
392 | #define profile_cpu_callback NULL | ||
393 | 382 | ||
394 | static void do_profile_hits(int type, void *__pc, unsigned int nr_hits) | 383 | static void do_profile_hits(int type, void *__pc, unsigned int nr_hits) |
395 | { | 384 | { |
@@ -531,83 +520,43 @@ static const struct file_operations proc_profile_operations = { | |||
531 | .llseek = default_llseek, | 520 | .llseek = default_llseek, |
532 | }; | 521 | }; |
533 | 522 | ||
534 | #ifdef CONFIG_SMP | 523 | int __ref create_proc_profile(void) |
535 | static void profile_nop(void *unused) | ||
536 | { | ||
537 | } | ||
538 | |||
539 | static int create_hash_tables(void) | ||
540 | { | 524 | { |
541 | int cpu; | 525 | struct proc_dir_entry *entry; |
542 | 526 | #ifdef CONFIG_SMP | |
543 | for_each_online_cpu(cpu) { | 527 | enum cpuhp_state online_state; |
544 | int node = cpu_to_mem(cpu); | ||
545 | struct page *page; | ||
546 | |||
547 | page = __alloc_pages_node(node, | ||
548 | GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, | ||
549 | 0); | ||
550 | if (!page) | ||
551 | goto out_cleanup; | ||
552 | per_cpu(cpu_profile_hits, cpu)[1] | ||
553 | = (struct profile_hit *)page_address(page); | ||
554 | page = __alloc_pages_node(node, | ||
555 | GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, | ||
556 | 0); | ||
557 | if (!page) | ||
558 | goto out_cleanup; | ||
559 | per_cpu(cpu_profile_hits, cpu)[0] | ||
560 | = (struct profile_hit *)page_address(page); | ||
561 | } | ||
562 | return 0; | ||
563 | out_cleanup: | ||
564 | prof_on = 0; | ||
565 | smp_mb(); | ||
566 | on_each_cpu(profile_nop, NULL, 1); | ||
567 | for_each_online_cpu(cpu) { | ||
568 | struct page *page; | ||
569 | |||
570 | if (per_cpu(cpu_profile_hits, cpu)[0]) { | ||
571 | page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]); | ||
572 | per_cpu(cpu_profile_hits, cpu)[0] = NULL; | ||
573 | __free_page(page); | ||
574 | } | ||
575 | if (per_cpu(cpu_profile_hits, cpu)[1]) { | ||
576 | page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]); | ||
577 | per_cpu(cpu_profile_hits, cpu)[1] = NULL; | ||
578 | __free_page(page); | ||
579 | } | ||
580 | } | ||
581 | return -1; | ||
582 | } | ||
583 | #else | ||
584 | #define create_hash_tables() ({ 0; }) | ||
585 | #endif | 528 | #endif |
586 | 529 | ||
587 | int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */ | ||
588 | { | ||
589 | struct proc_dir_entry *entry; | ||
590 | int err = 0; | 530 | int err = 0; |
591 | 531 | ||
592 | if (!prof_on) | 532 | if (!prof_on) |
593 | return 0; | 533 | return 0; |
594 | 534 | #ifdef CONFIG_SMP | |
595 | cpu_notifier_register_begin(); | 535 | err = cpuhp_setup_state(CPUHP_PROFILE_PREPARE, "PROFILE_PREPARE", |
596 | 536 | profile_prepare_cpu, profile_dead_cpu); | |
597 | if (create_hash_tables()) { | 537 | if (err) |
598 | err = -ENOMEM; | 538 | return err; |
599 | goto out; | 539 | |
600 | } | 540 | err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PROFILE_ONLINE", |
601 | 541 | profile_online_cpu, NULL); | |
542 | if (err < 0) | ||
543 | goto err_state_prep; | ||
544 | online_state = err; | ||
545 | err = 0; | ||
546 | #endif | ||
602 | entry = proc_create("profile", S_IWUSR | S_IRUGO, | 547 | entry = proc_create("profile", S_IWUSR | S_IRUGO, |
603 | NULL, &proc_profile_operations); | 548 | NULL, &proc_profile_operations); |
604 | if (!entry) | 549 | if (!entry) |
605 | goto out; | 550 | goto err_state_onl; |
606 | proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t)); | 551 | proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t)); |
607 | __hotcpu_notifier(profile_cpu_callback, 0); | ||
608 | 552 | ||
609 | out: | 553 | return err; |
610 | cpu_notifier_register_done(); | 554 | err_state_onl: |
555 | #ifdef CONFIG_SMP | ||
556 | cpuhp_remove_state(online_state); | ||
557 | err_state_prep: | ||
558 | cpuhp_remove_state(CPUHP_PROFILE_PREPARE); | ||
559 | #endif | ||
611 | return err; | 560 | return err; |
612 | } | 561 | } |
613 | subsys_initcall(create_proc_profile); | 562 | subsys_initcall(create_proc_profile); |