diff options
| -rw-r--r-- | arch/x86/include/asm/alternative.h | 7 | ||||
| -rw-r--r-- | arch/x86/kernel/alternative.c | 49 |
2 files changed, 47 insertions, 9 deletions
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h index 76561d20ea2f..4a2adaa9aefc 100644 --- a/arch/x86/include/asm/alternative.h +++ b/arch/x86/include/asm/alternative.h | |||
| @@ -180,8 +180,15 @@ extern void *text_poke_early(void *addr, const void *opcode, size_t len); | |||
| 180 | * On the local CPU you need to be protected again NMI or MCE handlers seeing an | 180 | * On the local CPU you need to be protected again NMI or MCE handlers seeing an |
| 181 | * inconsistent instruction while you patch. | 181 | * inconsistent instruction while you patch. |
| 182 | */ | 182 | */ |
| 183 | struct text_poke_param { | ||
| 184 | void *addr; | ||
| 185 | const void *opcode; | ||
| 186 | size_t len; | ||
| 187 | }; | ||
| 188 | |||
| 183 | extern void *text_poke(void *addr, const void *opcode, size_t len); | 189 | extern void *text_poke(void *addr, const void *opcode, size_t len); |
| 184 | extern void *text_poke_smp(void *addr, const void *opcode, size_t len); | 190 | extern void *text_poke_smp(void *addr, const void *opcode, size_t len); |
| 191 | extern void text_poke_smp_batch(struct text_poke_param *params, int n); | ||
| 185 | 192 | ||
| 186 | #if defined(CONFIG_DYNAMIC_FTRACE) || defined(HAVE_JUMP_LABEL) | 193 | #if defined(CONFIG_DYNAMIC_FTRACE) || defined(HAVE_JUMP_LABEL) |
| 187 | #define IDEAL_NOP_SIZE_5 5 | 194 | #define IDEAL_NOP_SIZE_5 5 |
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 5079f24c955a..553d0b0d639b 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c | |||
| @@ -591,17 +591,21 @@ static atomic_t stop_machine_first; | |||
| 591 | static int wrote_text; | 591 | static int wrote_text; |
| 592 | 592 | ||
| 593 | struct text_poke_params { | 593 | struct text_poke_params { |
| 594 | void *addr; | 594 | struct text_poke_param *params; |
| 595 | const void *opcode; | 595 | int nparams; |
| 596 | size_t len; | ||
| 597 | }; | 596 | }; |
| 598 | 597 | ||
| 599 | static int __kprobes stop_machine_text_poke(void *data) | 598 | static int __kprobes stop_machine_text_poke(void *data) |
| 600 | { | 599 | { |
| 601 | struct text_poke_params *tpp = data; | 600 | struct text_poke_params *tpp = data; |
| 601 | struct text_poke_param *p; | ||
| 602 | int i; | ||
| 602 | 603 | ||
| 603 | if (atomic_dec_and_test(&stop_machine_first)) { | 604 | if (atomic_dec_and_test(&stop_machine_first)) { |
| 604 | text_poke(tpp->addr, tpp->opcode, tpp->len); | 605 | for (i = 0; i < tpp->nparams; i++) { |
| 606 | p = &tpp->params[i]; | ||
| 607 | text_poke(p->addr, p->opcode, p->len); | ||
| 608 | } | ||
| 605 | smp_wmb(); /* Make sure other cpus see that this has run */ | 609 | smp_wmb(); /* Make sure other cpus see that this has run */ |
| 606 | wrote_text = 1; | 610 | wrote_text = 1; |
| 607 | } else { | 611 | } else { |
| @@ -610,8 +614,12 @@ static int __kprobes stop_machine_text_poke(void *data) | |||
| 610 | smp_mb(); /* Load wrote_text before following execution */ | 614 | smp_mb(); /* Load wrote_text before following execution */ |
| 611 | } | 615 | } |
| 612 | 616 | ||
| 613 | flush_icache_range((unsigned long)tpp->addr, | 617 | for (i = 0; i < tpp->nparams; i++) { |
| 614 | (unsigned long)tpp->addr + tpp->len); | 618 | p = &tpp->params[i]; |
| 619 | flush_icache_range((unsigned long)p->addr, | ||
| 620 | (unsigned long)p->addr + p->len); | ||
| 621 | } | ||
| 622 | |||
| 615 | return 0; | 623 | return 0; |
| 616 | } | 624 | } |
| 617 | 625 | ||
| @@ -631,10 +639,13 @@ static int __kprobes stop_machine_text_poke(void *data) | |||
| 631 | void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len) | 639 | void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len) |
| 632 | { | 640 | { |
| 633 | struct text_poke_params tpp; | 641 | struct text_poke_params tpp; |
| 642 | struct text_poke_param p; | ||
| 634 | 643 | ||
| 635 | tpp.addr = addr; | 644 | p.addr = addr; |
| 636 | tpp.opcode = opcode; | 645 | p.opcode = opcode; |
| 637 | tpp.len = len; | 646 | p.len = len; |
| 647 | tpp.params = &p; | ||
| 648 | tpp.nparams = 1; | ||
| 638 | atomic_set(&stop_machine_first, 1); | 649 | atomic_set(&stop_machine_first, 1); |
| 639 | wrote_text = 0; | 650 | wrote_text = 0; |
| 640 | /* Use __stop_machine() because the caller already got online_cpus. */ | 651 | /* Use __stop_machine() because the caller already got online_cpus. */ |
| @@ -642,6 +653,26 @@ void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len) | |||
| 642 | return addr; | 653 | return addr; |
| 643 | } | 654 | } |
| 644 | 655 | ||
| 656 | /** | ||
| 657 | * text_poke_smp_batch - Update instructions on a live kernel on SMP | ||
| 658 | * @params: an array of text_poke parameters | ||
| 659 | * @n: the number of elements in params. | ||
| 660 | * | ||
| 661 | * Modify multi-byte instruction by using stop_machine() on SMP. Since the | ||
| 662 | * stop_machine() is heavy task, it is better to aggregate text_poke requests | ||
| 663 | * and do it once if possible. | ||
| 664 | * | ||
| 665 | * Note: Must be called under get_online_cpus() and text_mutex. | ||
| 666 | */ | ||
| 667 | void __kprobes text_poke_smp_batch(struct text_poke_param *params, int n) | ||
| 668 | { | ||
| 669 | struct text_poke_params tpp = {.params = params, .nparams = n}; | ||
| 670 | |||
| 671 | atomic_set(&stop_machine_first, 1); | ||
| 672 | wrote_text = 0; | ||
| 673 | stop_machine(stop_machine_text_poke, (void *)&tpp, NULL); | ||
| 674 | } | ||
| 675 | |||
| 645 | #if defined(CONFIG_DYNAMIC_FTRACE) || defined(HAVE_JUMP_LABEL) | 676 | #if defined(CONFIG_DYNAMIC_FTRACE) || defined(HAVE_JUMP_LABEL) |
| 646 | 677 | ||
| 647 | #ifdef CONFIG_X86_64 | 678 | #ifdef CONFIG_X86_64 |
