Skip to content

Commit 97c79a3

Browse files
committed
perf core: Per event callchain limit
Additionally to being able to control the system wide maximum depth via /proc/sys/kernel/perf_event_max_stack, now we are able to ask for different depths per event, using perf_event_attr.sample_max_stack for that. This uses an u16 hole at the end of perf_event_attr, that, when perf_event_attr.sample_type has the PERF_SAMPLE_CALLCHAIN, if sample_max_stack is zero, means use perf_event_max_stack, otherwise it'll be bounds checked under callchain_mutex. Cc: Adrian Hunter <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Brendan Gregg <[email protected]> Cc: David Ahern <[email protected]> Cc: Frederic Weisbecker <[email protected]> Cc: He Kuang <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Masami Hiramatsu <[email protected]> Cc: Milian Wolff <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Stephane Eranian <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Vince Weaver <[email protected]> Cc: Wang Nan <[email protected]> Cc: Zefan Li <[email protected]> Link: http://lkml.kernel.org/n/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 480ca35 commit 97c79a3

File tree

5 files changed

+23
-6
lines changed

5 files changed

+23
-6
lines changed

include/linux/perf_event.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ extern void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct
10761076
extern struct perf_callchain_entry *
10771077
get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
10781078
u32 max_stack, bool crosstask, bool add_mark);
1079-
extern int get_callchain_buffers(void);
1079+
extern int get_callchain_buffers(int max_stack);
10801080
extern void put_callchain_buffers(void);
10811081

10821082
extern int sysctl_perf_event_max_stack;

include/uapi/linux/perf_event.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ enum perf_event_read_format {
276276

277277
/*
278278
* Hardware event_id to monitor via a performance monitoring event:
279+
*
280+
* @sample_max_stack: Max number of frame pointers in a callchain,
281+
* should be < /proc/sys/kernel/perf_event_max_stack
279282
*/
280283
struct perf_event_attr {
281284

@@ -385,7 +388,8 @@ struct perf_event_attr {
385388
* Wakeup watermark for AUX area
386389
*/
387390
__u32 aux_watermark;
388-
__u32 __reserved_2; /* align to __u64 */
391+
__u16 sample_max_stack;
392+
__u16 __reserved_2; /* align to __u64 */
389393
};
390394

391395
#define perf_flags(attr) (*(&(attr)->read_format + 1))

kernel/bpf/stackmap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr)
9999
if (err)
100100
goto free_smap;
101101

102-
err = get_callchain_buffers();
102+
err = get_callchain_buffers(sysctl_perf_event_max_stack);
103103
if (err)
104104
goto free_smap;
105105

kernel/events/callchain.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ static int alloc_callchain_buffers(void)
104104
return -ENOMEM;
105105
}
106106

107-
int get_callchain_buffers(void)
107+
int get_callchain_buffers(int event_max_stack)
108108
{
109109
int err = 0;
110110
int count;
@@ -121,6 +121,15 @@ int get_callchain_buffers(void)
121121
/* If the allocation failed, give up */
122122
if (!callchain_cpus_entries)
123123
err = -ENOMEM;
124+
/*
125+
* If requesting per event more than the global cap,
126+
* return a different error to help userspace figure
127+
* this out.
128+
*
129+
* And also do it here so that we have &callchain_mutex held.
130+
*/
131+
if (event_max_stack > sysctl_perf_event_max_stack)
132+
err = -EOVERFLOW;
124133
goto exit;
125134
}
126135

@@ -174,11 +183,12 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
174183
bool user = !event->attr.exclude_callchain_user;
175184
/* Disallow cross-task user callchains. */
176185
bool crosstask = event->ctx->task && event->ctx->task != current;
186+
const u32 max_stack = event->attr.sample_max_stack;
177187

178188
if (!kernel && !user)
179189
return NULL;
180190

181-
return get_perf_callchain(regs, 0, kernel, user, sysctl_perf_event_max_stack, crosstask, true);
191+
return get_perf_callchain(regs, 0, kernel, user, max_stack, crosstask, true);
182192
}
183193

184194
struct perf_callchain_entry *

kernel/events/core.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8843,7 +8843,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
88438843

88448844
if (!event->parent) {
88458845
if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
8846-
err = get_callchain_buffers();
8846+
err = get_callchain_buffers(attr->sample_max_stack);
88478847
if (err)
88488848
goto err_addr_filters;
88498849
}
@@ -9165,6 +9165,9 @@ SYSCALL_DEFINE5(perf_event_open,
91659165
return -EINVAL;
91669166
}
91679167

9168+
if (!attr.sample_max_stack)
9169+
attr.sample_max_stack = sysctl_perf_event_max_stack;
9170+
91689171
/*
91699172
* In cgroup mode, the pid argument is used to pass the fd
91709173
* opened to the cgroup directory in cgroupfs. The cpu argument

0 commit comments

Comments
 (0)