Merge tag 'perf-core-2024-09-18' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf events updates from Ingo Molnar:
- Implement per-PMU context rescheduling to significantly improve
single-PMU performance, and related cleanups/fixes (Peter Zijlstra
and Namhyung Kim)
- Fix ancient bug resulting in a lot of events being dropped
erroneously at higher sampling frequencies (Luo Gengkun)
- uprobes enhancements:
- Implement RCU-protected hot path optimizations for better
performance:
"For baseline vs SRCU, peak througput increased from 3.7 M/s
(million uprobe triggerings per second) up to about 8 M/s. For
uretprobes it's a bit more modest with bump from 2.4 M/s to
5 M/s.
For SRCU vs RCU Tasks Trace, peak throughput for uprobes
increases further from 8 M/s to 10.3 M/s (+28%!), and for
uretprobes from 5.3 M/s to 5.8 M/s (+11%), as we have more
work to do on uretprobes side.
Even single-thread (no contention) performance is slightly
better: 3.276 M/s to 3.396 M/s (+3.5%) for uprobes, and 2.055
M/s to 2.174 M/s (+5.8%) for uretprobes."
(Andrii Nakryiko et al)
- Document mmap_lock, don't abuse get_user_pages_remote() (Oleg
Nesterov)
- Cleanups & fixes to prepare for future work:
- Remove uprobe_register_refctr()
- Simplify error handling for alloc_uprobe()
- Make uprobe_register() return struct uprobe *
- Fold __uprobe_unregister() into uprobe_unregister()
- Shift put_uprobe() from delete_uprobe() to uprobe_unregister()
- BPF: Fix use-after-free in bpf_uprobe_multi_link_attach()
(Oleg Nesterov)
- New feature & ABI extension: allow events to use PERF_SAMPLE READ
with inheritance, enabling sample based profiling of a group of
counters over a hierarchy of processes or threads (Ben Gainey)
- Intel uncore & power events updates:
- Add Arrow Lake and Lunar Lake support
- Add PERF_EV_CAP_READ_SCOPE
- Clean up and enhance cpumask and hotplug support
(Kan Liang)
- Add LNL uncore iMC freerunning support
- Use D0:F0 as a default device
(Zhenyu Wang)
- Intel PT: fix AUX snapshot handling race (Adrian Hunter)
- Misc fixes and cleanups (James Clark, Jiri Olsa, Oleg Nesterov and
Peter Zijlstra)
* tag 'perf-core-2024-09-18' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (40 commits)
dmaengine: idxd: Clean up cpumask and hotplug for perfmon
iommu/vt-d: Clean up cpumask and hotplug for perfmon
perf/x86/intel/cstate: Clean up cpumask and hotplug
perf: Add PERF_EV_CAP_READ_SCOPE
perf: Generic hotplug support for a PMU with a scope
uprobes: perform lockless SRCU-protected uprobes_tree lookup
rbtree: provide rb_find_rcu() / rb_find_add_rcu()
perf/uprobe: split uprobe_unregister()
uprobes: travers uprobe's consumer list locklessly under SRCU protection
uprobes: get rid of enum uprobe_filter_ctx in uprobe filter callbacks
uprobes: protected uprobe lifetime with SRCU
uprobes: revamp uprobe refcounting and lifetime management
bpf: Fix use-after-free in bpf_uprobe_multi_link_attach()
perf/core: Fix small negative period being ignored
perf: Really fix event_function_call() locking
perf: Optimize __pmu_ctx_sched_out()
perf: Add context time freeze
perf: Fix event_function_call() locking
perf: Extract a few helpers
perf: Optimize context reschedule for single PMU cases
...
This commit is contained in:
+21
-17
@@ -3160,6 +3160,7 @@ struct bpf_uprobe {
|
||||
loff_t offset;
|
||||
unsigned long ref_ctr_offset;
|
||||
u64 cookie;
|
||||
struct uprobe *uprobe;
|
||||
struct uprobe_consumer consumer;
|
||||
};
|
||||
|
||||
@@ -3178,15 +3179,15 @@ struct bpf_uprobe_multi_run_ctx {
|
||||
struct bpf_uprobe *uprobe;
|
||||
};
|
||||
|
||||
static void bpf_uprobe_unregister(struct path *path, struct bpf_uprobe *uprobes,
|
||||
u32 cnt)
|
||||
static void bpf_uprobe_unregister(struct bpf_uprobe *uprobes, u32 cnt)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
uprobe_unregister(d_real_inode(path->dentry), uprobes[i].offset,
|
||||
&uprobes[i].consumer);
|
||||
}
|
||||
for (i = 0; i < cnt; i++)
|
||||
uprobe_unregister_nosync(uprobes[i].uprobe, &uprobes[i].consumer);
|
||||
|
||||
if (cnt)
|
||||
uprobe_unregister_sync();
|
||||
}
|
||||
|
||||
static void bpf_uprobe_multi_link_release(struct bpf_link *link)
|
||||
@@ -3194,7 +3195,7 @@ static void bpf_uprobe_multi_link_release(struct bpf_link *link)
|
||||
struct bpf_uprobe_multi_link *umulti_link;
|
||||
|
||||
umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
|
||||
bpf_uprobe_unregister(&umulti_link->path, umulti_link->uprobes, umulti_link->cnt);
|
||||
bpf_uprobe_unregister(umulti_link->uprobes, umulti_link->cnt);
|
||||
if (umulti_link->task)
|
||||
put_task_struct(umulti_link->task);
|
||||
path_put(&umulti_link->path);
|
||||
@@ -3322,8 +3323,7 @@ static int uprobe_prog_run(struct bpf_uprobe *uprobe,
|
||||
}
|
||||
|
||||
static bool
|
||||
uprobe_multi_link_filter(struct uprobe_consumer *con, enum uprobe_filter_ctx ctx,
|
||||
struct mm_struct *mm)
|
||||
uprobe_multi_link_filter(struct uprobe_consumer *con, struct mm_struct *mm)
|
||||
{
|
||||
struct bpf_uprobe *uprobe;
|
||||
|
||||
@@ -3480,22 +3480,26 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
&bpf_uprobe_multi_link_lops, prog);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
err = uprobe_register_refctr(d_real_inode(link->path.dentry),
|
||||
uprobes[i].offset,
|
||||
uprobes[i].ref_ctr_offset,
|
||||
&uprobes[i].consumer);
|
||||
if (err) {
|
||||
bpf_uprobe_unregister(&path, uprobes, i);
|
||||
goto error_free;
|
||||
uprobes[i].uprobe = uprobe_register(d_real_inode(link->path.dentry),
|
||||
uprobes[i].offset,
|
||||
uprobes[i].ref_ctr_offset,
|
||||
&uprobes[i].consumer);
|
||||
if (IS_ERR(uprobes[i].uprobe)) {
|
||||
err = PTR_ERR(uprobes[i].uprobe);
|
||||
link->cnt = i;
|
||||
goto error_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err)
|
||||
goto error_free;
|
||||
goto error_unregister;
|
||||
|
||||
return bpf_link_settle(&link_primer);
|
||||
|
||||
error_unregister:
|
||||
bpf_uprobe_unregister(uprobes, link->cnt);
|
||||
|
||||
error_free:
|
||||
kvfree(uprobes);
|
||||
kfree(link);
|
||||
|
||||
+20
-24
@@ -58,8 +58,8 @@ struct trace_uprobe {
|
||||
struct dyn_event devent;
|
||||
struct uprobe_consumer consumer;
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
char *filename;
|
||||
struct uprobe *uprobe;
|
||||
unsigned long offset;
|
||||
unsigned long ref_ctr_offset;
|
||||
unsigned long nhit;
|
||||
@@ -1078,43 +1078,40 @@ print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *e
|
||||
return trace_handle_return(s);
|
||||
}
|
||||
|
||||
typedef bool (*filter_func_t)(struct uprobe_consumer *self,
|
||||
enum uprobe_filter_ctx ctx,
|
||||
struct mm_struct *mm);
|
||||
typedef bool (*filter_func_t)(struct uprobe_consumer *self, struct mm_struct *mm);
|
||||
|
||||
static int trace_uprobe_enable(struct trace_uprobe *tu, filter_func_t filter)
|
||||
{
|
||||
int ret;
|
||||
struct inode *inode = d_real_inode(tu->path.dentry);
|
||||
struct uprobe *uprobe;
|
||||
|
||||
tu->consumer.filter = filter;
|
||||
tu->inode = d_real_inode(tu->path.dentry);
|
||||
uprobe = uprobe_register(inode, tu->offset, tu->ref_ctr_offset, &tu->consumer);
|
||||
if (IS_ERR(uprobe))
|
||||
return PTR_ERR(uprobe);
|
||||
|
||||
if (tu->ref_ctr_offset)
|
||||
ret = uprobe_register_refctr(tu->inode, tu->offset,
|
||||
tu->ref_ctr_offset, &tu->consumer);
|
||||
else
|
||||
ret = uprobe_register(tu->inode, tu->offset, &tu->consumer);
|
||||
|
||||
if (ret)
|
||||
tu->inode = NULL;
|
||||
|
||||
return ret;
|
||||
tu->uprobe = uprobe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __probe_event_disable(struct trace_probe *tp)
|
||||
{
|
||||
struct trace_uprobe *tu;
|
||||
bool sync = false;
|
||||
|
||||
tu = container_of(tp, struct trace_uprobe, tp);
|
||||
WARN_ON(!uprobe_filter_is_empty(tu->tp.event->filter));
|
||||
|
||||
list_for_each_entry(tu, trace_probe_probe_list(tp), tp.list) {
|
||||
if (!tu->inode)
|
||||
if (!tu->uprobe)
|
||||
continue;
|
||||
|
||||
uprobe_unregister(tu->inode, tu->offset, &tu->consumer);
|
||||
tu->inode = NULL;
|
||||
uprobe_unregister_nosync(tu->uprobe, &tu->consumer);
|
||||
sync = true;
|
||||
tu->uprobe = NULL;
|
||||
}
|
||||
if (sync)
|
||||
uprobe_unregister_sync();
|
||||
}
|
||||
|
||||
static int probe_event_enable(struct trace_event_call *call,
|
||||
@@ -1310,7 +1307,7 @@ static int uprobe_perf_close(struct trace_event_call *call,
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(tu, trace_probe_probe_list(tp), tp.list) {
|
||||
ret = uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
|
||||
ret = uprobe_apply(tu->uprobe, &tu->consumer, false);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@@ -1334,7 +1331,7 @@ static int uprobe_perf_open(struct trace_event_call *call,
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(tu, trace_probe_probe_list(tp), tp.list) {
|
||||
err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
|
||||
err = uprobe_apply(tu->uprobe, &tu->consumer, true);
|
||||
if (err) {
|
||||
uprobe_perf_close(call, event);
|
||||
break;
|
||||
@@ -1344,8 +1341,7 @@ static int uprobe_perf_open(struct trace_event_call *call,
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool uprobe_perf_filter(struct uprobe_consumer *uc,
|
||||
enum uprobe_filter_ctx ctx, struct mm_struct *mm)
|
||||
static bool uprobe_perf_filter(struct uprobe_consumer *uc, struct mm_struct *mm)
|
||||
{
|
||||
struct trace_uprobe_filter *filter;
|
||||
struct trace_uprobe *tu;
|
||||
@@ -1431,7 +1427,7 @@ static void __uprobe_perf_func(struct trace_uprobe *tu,
|
||||
static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs,
|
||||
struct uprobe_cpu_buffer **ucbp)
|
||||
{
|
||||
if (!uprobe_perf_filter(&tu->consumer, 0, current->mm))
|
||||
if (!uprobe_perf_filter(&tu->consumer, current->mm))
|
||||
return UPROBE_HANDLER_REMOVE;
|
||||
|
||||
if (!is_ret_probe(tu))
|
||||
|
||||
Reference in New Issue
Block a user