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 |