diff options
| author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2010-12-03 04:54:28 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2010-12-06 11:59:31 -0500 |
| commit | cd7ebe2298ff1c3112232878678ce5fe6be8a15b (patch) | |
| tree | 7bac7adf40ce2141e779b7d99b2784279c2dc45c | |
| parent | 7deb18dcf0478940ac979de002db1ed8ba6531dc (diff) | |
kprobes: Use text_poke_smp_batch for optimizing
Use text_poke_smp_batch() in optimization path for reducing
the number of stop_machine() issues. If the number of optimizing
probes is more than MAX_OPTIMIZE_PROBES(=256), kprobes optimizes
first MAX_OPTIMIZE_PROBES probes and kicks optimizer for
remaining probes.
Changes in v5:
- Use kick_kprobe_optimizer() instead of directly calling
schedule_delayed_work().
- Rescheduling optimizer outside of kprobe mutex lock.
Changes in v2:
- Allocate code buffer and parameters in arch_init_kprobes()
instead of using static arraies.
- Merge previous max optimization limit patch into this patch.
So, this patch introduces upper limit of optimization at
once.
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20101203095428.2961.8994.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/x86/kernel/kprobes.c | 69 | ||||
| -rw-r--r-- | include/linux/kprobes.h | 2 | ||||
| -rw-r--r-- | kernel/kprobes.c | 17 |
3 files changed, 69 insertions, 19 deletions
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index da51dc8e77cb..25a8af76feb5 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
| @@ -1405,10 +1405,16 @@ int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op) | |||
| 1405 | return 0; | 1405 | return 0; |
| 1406 | } | 1406 | } |
| 1407 | 1407 | ||
| 1408 | /* Replace a breakpoint (int3) with a relative jump. */ | 1408 | #define MAX_OPTIMIZE_PROBES 256 |
| 1409 | int __kprobes arch_optimize_kprobe(struct optimized_kprobe *op) | 1409 | static struct text_poke_param *jump_poke_params; |
| 1410 | static struct jump_poke_buffer { | ||
| 1411 | u8 buf[RELATIVEJUMP_SIZE]; | ||
| 1412 | } *jump_poke_bufs; | ||
| 1413 | |||
| 1414 | static void __kprobes setup_optimize_kprobe(struct text_poke_param *tprm, | ||
| 1415 | u8 *insn_buf, | ||
| 1416 | struct optimized_kprobe *op) | ||
| 1410 | { | 1417 | { |
| 1411 | unsigned char jmp_code[RELATIVEJUMP_SIZE]; | ||
| 1412 | s32 rel = (s32)((long)op->optinsn.insn - | 1418 | s32 rel = (s32)((long)op->optinsn.insn - |
| 1413 | ((long)op->kp.addr + RELATIVEJUMP_SIZE)); | 1419 | ((long)op->kp.addr + RELATIVEJUMP_SIZE)); |
| 1414 | 1420 | ||
| @@ -1416,16 +1422,39 @@ int __kprobes arch_optimize_kprobe(struct optimized_kprobe *op) | |||
| 1416 | memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE, | 1422 | memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE, |
| 1417 | RELATIVE_ADDR_SIZE); | 1423 | RELATIVE_ADDR_SIZE); |
| 1418 | 1424 | ||
| 1419 | jmp_code[0] = RELATIVEJUMP_OPCODE; | 1425 | insn_buf[0] = RELATIVEJUMP_OPCODE; |
| 1420 | *(s32 *)(&jmp_code[1]) = rel; | 1426 | *(s32 *)(&insn_buf[1]) = rel; |
| 1427 | |||
| 1428 | tprm->addr = op->kp.addr; | ||
| 1429 | tprm->opcode = insn_buf; | ||
| 1430 | tprm->len = RELATIVEJUMP_SIZE; | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | /* | ||
| 1434 | * Replace breakpoints (int3) with relative jumps. | ||
| 1435 | * Caller must call with locking kprobe_mutex and text_mutex. | ||
| 1436 | */ | ||
| 1437 | void __kprobes arch_optimize_kprobes(struct list_head *oplist) | ||
| 1438 | { | ||
| 1439 | struct optimized_kprobe *op, *tmp; | ||
| 1440 | int c = 0; | ||
| 1441 | |||
| 1442 | list_for_each_entry_safe(op, tmp, oplist, list) { | ||
| 1443 | WARN_ON(kprobe_disabled(&op->kp)); | ||
| 1444 | /* Setup param */ | ||
| 1445 | setup_optimize_kprobe(&jump_poke_params[c], | ||
| 1446 | jump_poke_bufs[c].buf, op); | ||
| 1447 | list_del_init(&op->list); | ||
| 1448 | if (++c >= MAX_OPTIMIZE_PROBES) | ||
| 1449 | break; | ||
| 1450 | } | ||
| 1421 | 1451 | ||
| 1422 | /* | 1452 | /* |
| 1423 | * text_poke_smp doesn't support NMI/MCE code modifying. | 1453 | * text_poke_smp doesn't support NMI/MCE code modifying. |
| 1424 | * However, since kprobes itself also doesn't support NMI/MCE | 1454 | * However, since kprobes itself also doesn't support NMI/MCE |
| 1425 | * code probing, it's not a problem. | 1455 | * code probing, it's not a problem. |
| 1426 | */ | 1456 | */ |
| 1427 | text_poke_smp(op->kp.addr, jmp_code, RELATIVEJUMP_SIZE); | 1457 | text_poke_smp_batch(jump_poke_params, c); |
| 1428 | return 0; | ||
| 1429 | } | 1458 | } |
| 1430 | 1459 | ||
| 1431 | /* Replace a relative jump with a breakpoint (int3). */ | 1460 | /* Replace a relative jump with a breakpoint (int3). */ |
| @@ -1457,11 +1486,35 @@ static int __kprobes setup_detour_execution(struct kprobe *p, | |||
| 1457 | } | 1486 | } |
| 1458 | return 0; | 1487 | return 0; |
| 1459 | } | 1488 | } |
| 1489 | |||
| 1490 | static int __kprobes init_poke_params(void) | ||
| 1491 | { | ||
| 1492 | /* Allocate code buffer and parameter array */ | ||
| 1493 | jump_poke_bufs = kmalloc(sizeof(struct jump_poke_buffer) * | ||
| 1494 | MAX_OPTIMIZE_PROBES, GFP_KERNEL); | ||
| 1495 | if (!jump_poke_bufs) | ||
| 1496 | return -ENOMEM; | ||
| 1497 | |||
| 1498 | jump_poke_params = kmalloc(sizeof(struct text_poke_param) * | ||
| 1499 | MAX_OPTIMIZE_PROBES, GFP_KERNEL); | ||
| 1500 | if (!jump_poke_params) { | ||
| 1501 | kfree(jump_poke_bufs); | ||
| 1502 | jump_poke_bufs = NULL; | ||
| 1503 | return -ENOMEM; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | return 0; | ||
| 1507 | } | ||
| 1508 | #else /* !CONFIG_OPTPROBES */ | ||
| 1509 | static int __kprobes init_poke_params(void) | ||
| 1510 | { | ||
| 1511 | return 0; | ||
| 1512 | } | ||
| 1460 | #endif | 1513 | #endif |
| 1461 | 1514 | ||
| 1462 | int __init arch_init_kprobes(void) | 1515 | int __init arch_init_kprobes(void) |
| 1463 | { | 1516 | { |
| 1464 | return 0; | 1517 | return init_poke_params(); |
| 1465 | } | 1518 | } |
| 1466 | 1519 | ||
| 1467 | int __kprobes arch_trampoline_kprobe(struct kprobe *p) | 1520 | int __kprobes arch_trampoline_kprobe(struct kprobe *p) |
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index e7d1b2e0070d..fe157ba6aa0e 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h | |||
| @@ -275,7 +275,7 @@ extern int arch_prepared_optinsn(struct arch_optimized_insn *optinsn); | |||
| 275 | extern int arch_check_optimized_kprobe(struct optimized_kprobe *op); | 275 | extern int arch_check_optimized_kprobe(struct optimized_kprobe *op); |
| 276 | extern int arch_prepare_optimized_kprobe(struct optimized_kprobe *op); | 276 | extern int arch_prepare_optimized_kprobe(struct optimized_kprobe *op); |
| 277 | extern void arch_remove_optimized_kprobe(struct optimized_kprobe *op); | 277 | extern void arch_remove_optimized_kprobe(struct optimized_kprobe *op); |
| 278 | extern int arch_optimize_kprobe(struct optimized_kprobe *op); | 278 | extern void arch_optimize_kprobes(struct list_head *oplist); |
| 279 | extern void arch_unoptimize_kprobe(struct optimized_kprobe *op); | 279 | extern void arch_unoptimize_kprobe(struct optimized_kprobe *op); |
| 280 | extern kprobe_opcode_t *get_optinsn_slot(void); | 280 | extern kprobe_opcode_t *get_optinsn_slot(void); |
| 281 | extern void free_optinsn_slot(kprobe_opcode_t *slot, int dirty); | 281 | extern void free_optinsn_slot(kprobe_opcode_t *slot, int dirty); |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 134754d18bb4..531e10164836 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
| @@ -480,8 +480,6 @@ static DECLARE_COMPLETION(optimizer_comp); | |||
| 480 | */ | 480 | */ |
| 481 | static __kprobes void do_optimize_kprobes(void) | 481 | static __kprobes void do_optimize_kprobes(void) |
| 482 | { | 482 | { |
| 483 | struct optimized_kprobe *op, *tmp; | ||
| 484 | |||
| 485 | /* Optimization never be done when disarmed */ | 483 | /* Optimization never be done when disarmed */ |
| 486 | if (kprobes_all_disarmed || !kprobes_allow_optimization || | 484 | if (kprobes_all_disarmed || !kprobes_allow_optimization || |
| 487 | list_empty(&optimizing_list)) | 485 | list_empty(&optimizing_list)) |
| @@ -499,12 +497,7 @@ static __kprobes void do_optimize_kprobes(void) | |||
| 499 | */ | 497 | */ |
| 500 | get_online_cpus(); | 498 | get_online_cpus(); |
| 501 | mutex_lock(&text_mutex); | 499 | mutex_lock(&text_mutex); |
| 502 | list_for_each_entry_safe(op, tmp, &optimizing_list, list) { | 500 | arch_optimize_kprobes(&optimizing_list); |
| 503 | WARN_ON(kprobe_disabled(&op->kp)); | ||
| 504 | if (arch_optimize_kprobe(op) < 0) | ||
| 505 | op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; | ||
| 506 | list_del_init(&op->list); | ||
| 507 | } | ||
| 508 | mutex_unlock(&text_mutex); | 501 | mutex_unlock(&text_mutex); |
| 509 | put_online_cpus(); | 502 | put_online_cpus(); |
| 510 | } | 503 | } |
| @@ -598,8 +591,12 @@ static __kprobes void kprobe_optimizer(struct work_struct *work) | |||
| 598 | mutex_unlock(&kprobe_mutex); | 591 | mutex_unlock(&kprobe_mutex); |
| 599 | mutex_unlock(&module_mutex); | 592 | mutex_unlock(&module_mutex); |
| 600 | 593 | ||
| 601 | /* Wake up all waiters */ | 594 | /* Step 5: Kick optimizer again if needed */ |
| 602 | complete_all(&optimizer_comp); | 595 | if (!list_empty(&optimizing_list)) |
| 596 | kick_kprobe_optimizer(); | ||
| 597 | else | ||
| 598 | /* Wake up all waiters */ | ||
| 599 | complete_all(&optimizer_comp); | ||
| 603 | } | 600 | } |
| 604 | 601 | ||
| 605 | /* Wait for completing optimization and unoptimization */ | 602 | /* Wait for completing optimization and unoptimization */ |
