Probes update for v6.13:

Kprobes cleanups. Functionality does not change.
 - kprobes: Cleanup the config comment
   Adjust #endif comments.
 - kprobes: Cleanup collect_one_slot() and __disable_kprobe()
   Make fail fast to reduce code nested level.
 - kprobes: Use struct_size() in __get_insn_slot()
   Use struct_size() to avoid special macro.
 - x86/kprobes: Cleanup kprobes on ftrace code
   Use macro instead of direct field access/magic number, and avoid
   redundant instruction pointer setting.
 -----BEGIN PGP SIGNATURE-----
 
 iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmc6vhwbHG1hc2FtaS5o
 aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8bxowIALFYrdLV2ofWRy7/lNkP
 6Bv1DkBQ/Xy/ABZ4lAqdgTZrf7Cz8TdPZUL1UOowxW3Cl09PYcpqlUlw/XldvI5j
 fukkwL9rXNgJfYbau+QG9E5c7mNakexDLBKCZGvnDDuKj0f1aauhwZmpJbNgz1Y6
 dUgfFgDJXSArnVKxfZvOhL1tbxYPJUhzNc339p8PVD8r/OUKEZo2EReds3DM40Zq
 wtwyKqWmawTjRud0ZtgkaWiK1d+QKa07h+GnXi1wUy98A2yGp3fcLuxvjBUMqsCD
 uzWkY3MikXIZJ/ijxUsMGBRisD4ozqozlQ4wIxCuahRntl9b/d9jXqKY7RTvy6Vw
 r+Y=
 =n4ST
 -----END PGP SIGNATURE-----

Merge tag 'probes-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull probes updates from Masami Hiramatsu:
 "Kprobes cleanups. Functionality does not change.

   - kprobes: Cleanup the config comment

     Adjust #endif comments.

   - kprobes: Cleanup collect_one_slot() and __disable_kprobe()

     Make fail fast to reduce code nested level.

   - kprobes: Use struct_size() in __get_insn_slot()

     Use struct_size() to avoid special macro.

   - x86/kprobes: Cleanup kprobes on ftrace code

     Use macro instead of direct field access/magic number, and avoid
     redundant instruction pointer setting"

* tag 'probes-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  x86/kprobes: Cleanup kprobes on ftrace code
  kprobes: Use struct_size() in __get_insn_slot()
  kprobes: Cleanup collect_one_slot() and __disable_kprobe()
  kprobes: Cleanup the config comment
This commit is contained in:
Linus Torvalds 2024-11-20 09:36:05 -08:00
commit a0e752bda2
2 changed files with 55 additions and 55 deletions

View File

@ -9,6 +9,7 @@
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/preempt.h> #include <linux/preempt.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <asm/text-patching.h>
#include "common.h" #include "common.h"
@ -36,23 +37,25 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
if (kprobe_running()) { if (kprobe_running()) {
kprobes_inc_nmissed_count(p); kprobes_inc_nmissed_count(p);
} else { } else {
unsigned long orig_ip = regs->ip; unsigned long orig_ip = instruction_pointer(regs);
/* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */ /* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */
regs->ip = ip + sizeof(kprobe_opcode_t); instruction_pointer_set(regs, ip + INT3_INSN_SIZE);
__this_cpu_write(current_kprobe, p); __this_cpu_write(current_kprobe, p);
kcb->kprobe_status = KPROBE_HIT_ACTIVE; kcb->kprobe_status = KPROBE_HIT_ACTIVE;
if (!p->pre_handler || !p->pre_handler(p, regs)) { if (!p->pre_handler || !p->pre_handler(p, regs)) {
/*
* Emulate singlestep (and also recover regs->ip)
* as if there is a 5byte nop
*/
regs->ip = (unsigned long)p->addr + MCOUNT_INSN_SIZE;
if (unlikely(p->post_handler)) { if (unlikely(p->post_handler)) {
/*
* Emulate singlestep (and also recover regs->ip)
* as if there is a 5byte nop
*/
instruction_pointer_set(regs, ip + MCOUNT_INSN_SIZE);
kcb->kprobe_status = KPROBE_HIT_SSDONE; kcb->kprobe_status = KPROBE_HIT_SSDONE;
p->post_handler(p, regs, 0); p->post_handler(p, regs, 0);
} }
regs->ip = orig_ip; /* Recover IP address */
instruction_pointer_set(regs, orig_ip);
} }
/* /*
* If pre_handler returns !0, it changes regs->ip. We have to * If pre_handler returns !0, it changes regs->ip. We have to

View File

@ -95,10 +95,6 @@ struct kprobe_insn_page {
char slot_used[]; char slot_used[];
}; };
#define KPROBE_INSN_PAGE_SIZE(slots) \
(offsetof(struct kprobe_insn_page, slot_used) + \
(sizeof(char) * (slots)))
static int slots_per_page(struct kprobe_insn_cache *c) static int slots_per_page(struct kprobe_insn_cache *c)
{ {
return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t)); return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t));
@ -175,7 +171,7 @@ kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
goto retry; goto retry;
/* All out of space. Need to allocate a new page. */ /* All out of space. Need to allocate a new page. */
kip = kmalloc(KPROBE_INSN_PAGE_SIZE(slots_per_page(c)), GFP_KERNEL); kip = kmalloc(struct_size(kip, slot_used, slots_per_page(c)), GFP_KERNEL);
if (!kip) if (!kip)
goto out; goto out;
@ -206,29 +202,29 @@ static bool collect_one_slot(struct kprobe_insn_page *kip, int idx)
{ {
kip->slot_used[idx] = SLOT_CLEAN; kip->slot_used[idx] = SLOT_CLEAN;
kip->nused--; kip->nused--;
if (kip->nused == 0) { if (kip->nused != 0)
return false;
/*
* Page is no longer in use. Free it unless
* it's the last one. We keep the last one
* so as not to have to set it up again the
* next time somebody inserts a probe.
*/
if (!list_is_singular(&kip->list)) {
/* /*
* Page is no longer in use. Free it unless * Record perf ksymbol unregister event before removing
* it's the last one. We keep the last one * the page.
* so as not to have to set it up again the
* next time somebody inserts a probe.
*/ */
if (!list_is_singular(&kip->list)) { perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_OOL,
/* (unsigned long)kip->insns, PAGE_SIZE, true,
* Record perf ksymbol unregister event before removing kip->cache->sym);
* the page. list_del_rcu(&kip->list);
*/ synchronize_rcu();
perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_OOL, kip->cache->free(kip->insns);
(unsigned long)kip->insns, PAGE_SIZE, true, kfree(kip);
kip->cache->sym);
list_del_rcu(&kip->list);
synchronize_rcu();
kip->cache->free(kip->insns);
kfree(kip);
}
return true;
} }
return false; return true;
} }
static int collect_garbage_slots(struct kprobe_insn_cache *c) static int collect_garbage_slots(struct kprobe_insn_cache *c)
@ -353,8 +349,8 @@ struct kprobe_insn_cache kprobe_optinsn_slots = {
/* .insn_size is initialized later */ /* .insn_size is initialized later */
.nr_garbage = 0, .nr_garbage = 0,
}; };
#endif #endif /* CONFIG_OPTPROBES */
#endif #endif /* __ARCH_WANT_KPROBES_INSN_SLOT */
/* We have preemption disabled.. so it is safe to use __ versions */ /* We have preemption disabled.. so it is safe to use __ versions */
static inline void set_kprobe_instance(struct kprobe *kp) static inline void set_kprobe_instance(struct kprobe *kp)
@ -1543,7 +1539,7 @@ static int check_ftrace_location(struct kprobe *p)
if (ftrace_location(addr) == addr) { if (ftrace_location(addr) == addr) {
#ifdef CONFIG_KPROBES_ON_FTRACE #ifdef CONFIG_KPROBES_ON_FTRACE
p->flags |= KPROBE_FLAG_FTRACE; p->flags |= KPROBE_FLAG_FTRACE;
#else /* !CONFIG_KPROBES_ON_FTRACE */ #else
return -EINVAL; return -EINVAL;
#endif #endif
} }
@ -1725,28 +1721,29 @@ static struct kprobe *__disable_kprobe(struct kprobe *p)
if (unlikely(orig_p == NULL)) if (unlikely(orig_p == NULL))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (!kprobe_disabled(p)) { if (kprobe_disabled(p))
/* Disable probe if it is a child probe */ return orig_p;
if (p != orig_p)
p->flags |= KPROBE_FLAG_DISABLED;
/* Try to disarm and disable this/parent probe */ /* Disable probe if it is a child probe */
if (p == orig_p || aggr_kprobe_disabled(orig_p)) { if (p != orig_p)
/* p->flags |= KPROBE_FLAG_DISABLED;
* Don't be lazy here. Even if 'kprobes_all_disarmed'
* is false, 'orig_p' might not have been armed yet. /* Try to disarm and disable this/parent probe */
* Note arm_all_kprobes() __tries__ to arm all kprobes if (p == orig_p || aggr_kprobe_disabled(orig_p)) {
* on the best effort basis. /*
*/ * Don't be lazy here. Even if 'kprobes_all_disarmed'
if (!kprobes_all_disarmed && !kprobe_disabled(orig_p)) { * is false, 'orig_p' might not have been armed yet.
ret = disarm_kprobe(orig_p, true); * Note arm_all_kprobes() __tries__ to arm all kprobes
if (ret) { * on the best effort basis.
p->flags &= ~KPROBE_FLAG_DISABLED; */
return ERR_PTR(ret); if (!kprobes_all_disarmed && !kprobe_disabled(orig_p)) {
} ret = disarm_kprobe(orig_p, true);
if (ret) {
p->flags &= ~KPROBE_FLAG_DISABLED;
return ERR_PTR(ret);
} }
orig_p->flags |= KPROBE_FLAG_DISABLED;
} }
orig_p->flags |= KPROBE_FLAG_DISABLED;
} }
return orig_p; return orig_p;