diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2012-08-06 03:59:49 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-08-23 04:45:13 -0400 |
commit | 816afe4ff98ee10b1d30fd66361be132a0a5cee6 (patch) | |
tree | edb054530d87da95a9a9054d50467d92b62d8214 /arch/x86/kernel/alternative.c | |
parent | 23dcfa61bac244e1200ff9ad19c6e9144dcb6bb5 (diff) |
x86/smp: Don't ever patch back to UP if we unplug cpus
We still patch SMP instructions to UP variants if we boot with a
single CPU, but not at any other time. In particular, not if we
unplug CPUs to return to a single cpu.
Paul McKenney points out:
mean offline overhead is 6251/48=130.2 milliseconds.
If I remove the alternatives_smp_switch() from the offline
path [...] the mean offline overhead is 550/42=13.1 milliseconds
Basically, we're never going to get those 120ms back, and the
code is pretty messy.
We get rid of:
1) The "smp-alt-once" boot option. It's actually "smp-alt-boot", the
documentation is wrong. It's now the default.
2) The skip_smp_alternatives flag used by suspend.
3) arch_disable_nonboot_cpus_begin() and arch_disable_nonboot_cpus_end()
which were only used to set this one flag.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Paul McKenney <paul.mckenney@us.ibm.com>
Cc: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/87vcgwwive.fsf@rustcorp.com.au
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/alternative.c')
-rw-r--r-- | arch/x86/kernel/alternative.c | 107 |
1 files changed, 26 insertions, 81 deletions
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index afb7ff79a29f..af1f326a31c4 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c | |||
@@ -23,19 +23,6 @@ | |||
23 | 23 | ||
24 | #define MAX_PATCH_LEN (255-1) | 24 | #define MAX_PATCH_LEN (255-1) |
25 | 25 | ||
26 | #ifdef CONFIG_HOTPLUG_CPU | ||
27 | static int smp_alt_once; | ||
28 | |||
29 | static int __init bootonly(char *str) | ||
30 | { | ||
31 | smp_alt_once = 1; | ||
32 | return 1; | ||
33 | } | ||
34 | __setup("smp-alt-boot", bootonly); | ||
35 | #else | ||
36 | #define smp_alt_once 1 | ||
37 | #endif | ||
38 | |||
39 | static int __initdata_or_module debug_alternative; | 26 | static int __initdata_or_module debug_alternative; |
40 | 27 | ||
41 | static int __init debug_alt(char *str) | 28 | static int __init debug_alt(char *str) |
@@ -326,9 +313,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end, | |||
326 | { | 313 | { |
327 | const s32 *poff; | 314 | const s32 *poff; |
328 | 315 | ||
329 | if (noreplace_smp) | ||
330 | return; | ||
331 | |||
332 | mutex_lock(&text_mutex); | 316 | mutex_lock(&text_mutex); |
333 | for (poff = start; poff < end; poff++) { | 317 | for (poff = start; poff < end; poff++) { |
334 | u8 *ptr = (u8 *)poff + *poff; | 318 | u8 *ptr = (u8 *)poff + *poff; |
@@ -359,7 +343,7 @@ struct smp_alt_module { | |||
359 | }; | 343 | }; |
360 | static LIST_HEAD(smp_alt_modules); | 344 | static LIST_HEAD(smp_alt_modules); |
361 | static DEFINE_MUTEX(smp_alt); | 345 | static DEFINE_MUTEX(smp_alt); |
362 | static int smp_mode = 1; /* protected by smp_alt */ | 346 | static bool uniproc_patched = false; /* protected by smp_alt */ |
363 | 347 | ||
364 | void __init_or_module alternatives_smp_module_add(struct module *mod, | 348 | void __init_or_module alternatives_smp_module_add(struct module *mod, |
365 | char *name, | 349 | char *name, |
@@ -368,19 +352,18 @@ void __init_or_module alternatives_smp_module_add(struct module *mod, | |||
368 | { | 352 | { |
369 | struct smp_alt_module *smp; | 353 | struct smp_alt_module *smp; |
370 | 354 | ||
371 | if (noreplace_smp) | 355 | mutex_lock(&smp_alt); |
372 | return; | 356 | if (!uniproc_patched) |
357 | goto unlock; | ||
373 | 358 | ||
374 | if (smp_alt_once) { | 359 | if (num_possible_cpus() == 1) |
375 | if (boot_cpu_has(X86_FEATURE_UP)) | 360 | /* Don't bother remembering, we'll never have to undo it. */ |
376 | alternatives_smp_unlock(locks, locks_end, | 361 | goto smp_unlock; |
377 | text, text_end); | ||
378 | return; | ||
379 | } | ||
380 | 362 | ||
381 | smp = kzalloc(sizeof(*smp), GFP_KERNEL); | 363 | smp = kzalloc(sizeof(*smp), GFP_KERNEL); |
382 | if (NULL == smp) | 364 | if (NULL == smp) |
383 | return; /* we'll run the (safe but slow) SMP code then ... */ | 365 | /* we'll run the (safe but slow) SMP code then ... */ |
366 | goto unlock; | ||
384 | 367 | ||
385 | smp->mod = mod; | 368 | smp->mod = mod; |
386 | smp->name = name; | 369 | smp->name = name; |
@@ -392,11 +375,10 @@ void __init_or_module alternatives_smp_module_add(struct module *mod, | |||
392 | __func__, smp->locks, smp->locks_end, | 375 | __func__, smp->locks, smp->locks_end, |
393 | smp->text, smp->text_end, smp->name); | 376 | smp->text, smp->text_end, smp->name); |
394 | 377 | ||
395 | mutex_lock(&smp_alt); | ||
396 | list_add_tail(&smp->next, &smp_alt_modules); | 378 | list_add_tail(&smp->next, &smp_alt_modules); |
397 | if (boot_cpu_has(X86_FEATURE_UP)) | 379 | smp_unlock: |
398 | alternatives_smp_unlock(smp->locks, smp->locks_end, | 380 | alternatives_smp_unlock(locks, locks_end, text, text_end); |
399 | smp->text, smp->text_end); | 381 | unlock: |
400 | mutex_unlock(&smp_alt); | 382 | mutex_unlock(&smp_alt); |
401 | } | 383 | } |
402 | 384 | ||
@@ -404,24 +386,18 @@ void __init_or_module alternatives_smp_module_del(struct module *mod) | |||
404 | { | 386 | { |
405 | struct smp_alt_module *item; | 387 | struct smp_alt_module *item; |
406 | 388 | ||
407 | if (smp_alt_once || noreplace_smp) | ||
408 | return; | ||
409 | |||
410 | mutex_lock(&smp_alt); | 389 | mutex_lock(&smp_alt); |
411 | list_for_each_entry(item, &smp_alt_modules, next) { | 390 | list_for_each_entry(item, &smp_alt_modules, next) { |
412 | if (mod != item->mod) | 391 | if (mod != item->mod) |
413 | continue; | 392 | continue; |
414 | list_del(&item->next); | 393 | list_del(&item->next); |
415 | mutex_unlock(&smp_alt); | ||
416 | DPRINTK("%s: %s\n", __func__, item->name); | ||
417 | kfree(item); | 394 | kfree(item); |
418 | return; | 395 | break; |
419 | } | 396 | } |
420 | mutex_unlock(&smp_alt); | 397 | mutex_unlock(&smp_alt); |
421 | } | 398 | } |
422 | 399 | ||
423 | bool skip_smp_alternatives; | 400 | void alternatives_enable_smp(void) |
424 | void alternatives_smp_switch(int smp) | ||
425 | { | 401 | { |
426 | struct smp_alt_module *mod; | 402 | struct smp_alt_module *mod; |
427 | 403 | ||
@@ -436,34 +412,21 @@ void alternatives_smp_switch(int smp) | |||
436 | pr_info("lockdep: fixing up alternatives\n"); | 412 | pr_info("lockdep: fixing up alternatives\n"); |
437 | #endif | 413 | #endif |
438 | 414 | ||
439 | if (noreplace_smp || smp_alt_once || skip_smp_alternatives) | 415 | /* Why bother if there are no other CPUs? */ |
440 | return; | 416 | BUG_ON(num_possible_cpus() == 1); |
441 | BUG_ON(!smp && (num_online_cpus() > 1)); | ||
442 | 417 | ||
443 | mutex_lock(&smp_alt); | 418 | mutex_lock(&smp_alt); |
444 | 419 | ||
445 | /* | 420 | if (uniproc_patched) { |
446 | * Avoid unnecessary switches because it forces JIT based VMs to | ||
447 | * throw away all cached translations, which can be quite costly. | ||
448 | */ | ||
449 | if (smp == smp_mode) { | ||
450 | /* nothing */ | ||
451 | } else if (smp) { | ||
452 | pr_info("switching to SMP code\n"); | 421 | pr_info("switching to SMP code\n"); |
422 | BUG_ON(num_online_cpus() != 1); | ||
453 | clear_cpu_cap(&boot_cpu_data, X86_FEATURE_UP); | 423 | clear_cpu_cap(&boot_cpu_data, X86_FEATURE_UP); |
454 | clear_cpu_cap(&cpu_data(0), X86_FEATURE_UP); | 424 | clear_cpu_cap(&cpu_data(0), X86_FEATURE_UP); |
455 | list_for_each_entry(mod, &smp_alt_modules, next) | 425 | list_for_each_entry(mod, &smp_alt_modules, next) |
456 | alternatives_smp_lock(mod->locks, mod->locks_end, | 426 | alternatives_smp_lock(mod->locks, mod->locks_end, |
457 | mod->text, mod->text_end); | 427 | mod->text, mod->text_end); |
458 | } else { | 428 | uniproc_patched = false; |
459 | pr_info("switching to UP code\n"); | ||
460 | set_cpu_cap(&boot_cpu_data, X86_FEATURE_UP); | ||
461 | set_cpu_cap(&cpu_data(0), X86_FEATURE_UP); | ||
462 | list_for_each_entry(mod, &smp_alt_modules, next) | ||
463 | alternatives_smp_unlock(mod->locks, mod->locks_end, | ||
464 | mod->text, mod->text_end); | ||
465 | } | 429 | } |
466 | smp_mode = smp; | ||
467 | mutex_unlock(&smp_alt); | 430 | mutex_unlock(&smp_alt); |
468 | } | 431 | } |
469 | 432 | ||
@@ -540,40 +503,22 @@ void __init alternative_instructions(void) | |||
540 | 503 | ||
541 | apply_alternatives(__alt_instructions, __alt_instructions_end); | 504 | apply_alternatives(__alt_instructions, __alt_instructions_end); |
542 | 505 | ||
543 | /* switch to patch-once-at-boottime-only mode and free the | ||
544 | * tables in case we know the number of CPUs will never ever | ||
545 | * change */ | ||
546 | #ifdef CONFIG_HOTPLUG_CPU | ||
547 | if (num_possible_cpus() < 2) | ||
548 | smp_alt_once = 1; | ||
549 | #endif | ||
550 | |||
551 | #ifdef CONFIG_SMP | 506 | #ifdef CONFIG_SMP |
552 | if (smp_alt_once) { | 507 | /* Patch to UP if other cpus not imminent. */ |
553 | if (1 == num_possible_cpus()) { | 508 | if (!noreplace_smp && (num_present_cpus() == 1 || setup_max_cpus <= 1)) { |
554 | pr_info("switching to UP code\n"); | 509 | uniproc_patched = true; |
555 | set_cpu_cap(&boot_cpu_data, X86_FEATURE_UP); | ||
556 | set_cpu_cap(&cpu_data(0), X86_FEATURE_UP); | ||
557 | |||
558 | alternatives_smp_unlock(__smp_locks, __smp_locks_end, | ||
559 | _text, _etext); | ||
560 | } | ||
561 | } else { | ||
562 | alternatives_smp_module_add(NULL, "core kernel", | 510 | alternatives_smp_module_add(NULL, "core kernel", |
563 | __smp_locks, __smp_locks_end, | 511 | __smp_locks, __smp_locks_end, |
564 | _text, _etext); | 512 | _text, _etext); |
565 | |||
566 | /* Only switch to UP mode if we don't immediately boot others */ | ||
567 | if (num_present_cpus() == 1 || setup_max_cpus <= 1) | ||
568 | alternatives_smp_switch(0); | ||
569 | } | 513 | } |
570 | #endif | ||
571 | apply_paravirt(__parainstructions, __parainstructions_end); | ||
572 | 514 | ||
573 | if (smp_alt_once) | 515 | if (!uniproc_patched || num_possible_cpus() == 1) |
574 | free_init_pages("SMP alternatives", | 516 | free_init_pages("SMP alternatives", |
575 | (unsigned long)__smp_locks, | 517 | (unsigned long)__smp_locks, |
576 | (unsigned long)__smp_locks_end); | 518 | (unsigned long)__smp_locks_end); |
519 | #endif | ||
520 | |||
521 | apply_paravirt(__parainstructions, __parainstructions_end); | ||
577 | 522 | ||
578 | restart_nmi(); | 523 | restart_nmi(); |
579 | } | 524 | } |