Skip to content

Commit 7ab0fc6

Browse files
committed
tracing: Move histogram trigger variables from stack to per CPU structure
The histogram trigger has three somewhat large arrays on the kernel stack: unsigned long entries[HIST_STACKTRACE_DEPTH]; u64 var_ref_vals[TRACING_MAP_VARS_MAX]; char compound_key[HIST_KEY_SIZE_MAX]; Checking the function event_hist_trigger() stack frame size, it currently uses 816 bytes for its stack frame due to these variables! Instead, allocate a per CPU structure that holds these arrays for each context level (normal, softirq, irq and NMI). That is, each CPU will have 4 of these structures. This will be allocated when the first histogram trigger is enabled and freed when the last is disabled. When the histogram callback triggers, it will request this structure. The request will disable preemption, get the per CPU structure at the index of the per CPU variable, and increment that variable. The callback will use the arrays in this structure to perform its work and then release the structure. That in turn will simply decrement the per CPU index and enable preemption. Moving the variables from the kernel stack to the per CPU structure brings the stack frame of event_hist_trigger() down to just 112 bytes. Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Tom Zanussi <zanussi@kernel.org> Link: https://lore.kernel.org/20250407123851.74ea8d58@gandalf.local.home Fixes: 067fe03 ("tracing: Add variable reference handling to hist triggers") Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent 872a0d9 commit 7ab0fc6

File tree

1 file changed

+105
-15
lines changed

1 file changed

+105
-15
lines changed

kernel/trace/trace_events_hist.c

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5244,30 +5244,113 @@ hist_trigger_actions(struct hist_trigger_data *hist_data,
52445244
}
52455245
}
52465246

5247+
/*
5248+
* The hist_pad structure is used to save information to create
5249+
* a histogram from the histogram trigger. It's too big to store
5250+
* on the stack, so when the histogram trigger is initialized
5251+
* a percpu array of 4 hist_pad structures is allocated.
5252+
* This will cover every context from normal, softirq, irq and NMI
5253+
* in the very unlikely event that a tigger happens at each of
5254+
* these contexts and interrupts a currently active trigger.
5255+
*/
5256+
struct hist_pad {
5257+
unsigned long entries[HIST_STACKTRACE_DEPTH];
5258+
u64 var_ref_vals[TRACING_MAP_VARS_MAX];
5259+
char compound_key[HIST_KEY_SIZE_MAX];
5260+
};
5261+
5262+
static struct hist_pad __percpu *hist_pads;
5263+
static DEFINE_PER_CPU(int, hist_pad_cnt);
5264+
static refcount_t hist_pad_ref;
5265+
5266+
/* One hist_pad for every context (normal, softirq, irq, NMI) */
5267+
#define MAX_HIST_CNT 4
5268+
5269+
static int alloc_hist_pad(void)
5270+
{
5271+
lockdep_assert_held(&event_mutex);
5272+
5273+
if (refcount_read(&hist_pad_ref)) {
5274+
refcount_inc(&hist_pad_ref);
5275+
return 0;
5276+
}
5277+
5278+
hist_pads = __alloc_percpu(sizeof(struct hist_pad) * MAX_HIST_CNT,
5279+
__alignof__(struct hist_pad));
5280+
if (!hist_pads)
5281+
return -ENOMEM;
5282+
5283+
refcount_set(&hist_pad_ref, 1);
5284+
return 0;
5285+
}
5286+
5287+
static void free_hist_pad(void)
5288+
{
5289+
lockdep_assert_held(&event_mutex);
5290+
5291+
if (!refcount_dec_and_test(&hist_pad_ref))
5292+
return;
5293+
5294+
free_percpu(hist_pads);
5295+
hist_pads = NULL;
5296+
}
5297+
5298+
static struct hist_pad *get_hist_pad(void)
5299+
{
5300+
struct hist_pad *hist_pad;
5301+
int cnt;
5302+
5303+
if (WARN_ON_ONCE(!hist_pads))
5304+
return NULL;
5305+
5306+
preempt_disable();
5307+
5308+
hist_pad = per_cpu_ptr(hist_pads, smp_processor_id());
5309+
5310+
if (this_cpu_read(hist_pad_cnt) == MAX_HIST_CNT) {
5311+
preempt_enable();
5312+
return NULL;
5313+
}
5314+
5315+
cnt = this_cpu_inc_return(hist_pad_cnt) - 1;
5316+
5317+
return &hist_pad[cnt];
5318+
}
5319+
5320+
static void put_hist_pad(void)
5321+
{
5322+
this_cpu_dec(hist_pad_cnt);
5323+
preempt_enable();
5324+
}
5325+
52475326
static void event_hist_trigger(struct event_trigger_data *data,
52485327
struct trace_buffer *buffer, void *rec,
52495328
struct ring_buffer_event *rbe)
52505329
{
52515330
struct hist_trigger_data *hist_data = data->private_data;
52525331
bool use_compound_key = (hist_data->n_keys > 1);
5253-
unsigned long entries[HIST_STACKTRACE_DEPTH];
5254-
u64 var_ref_vals[TRACING_MAP_VARS_MAX];
5255-
char compound_key[HIST_KEY_SIZE_MAX];
52565332
struct tracing_map_elt *elt = NULL;
52575333
struct hist_field *key_field;
5334+
struct hist_pad *hist_pad;
52585335
u64 field_contents;
52595336
void *key = NULL;
52605337
unsigned int i;
52615338

52625339
if (unlikely(!rbe))
52635340
return;
52645341

5265-
memset(compound_key, 0, hist_data->key_size);
5342+
hist_pad = get_hist_pad();
5343+
if (!hist_pad)
5344+
return;
5345+
5346+
memset(hist_pad->compound_key, 0, hist_data->key_size);
52665347

52675348
for_each_hist_key_field(i, hist_data) {
52685349
key_field = hist_data->fields[i];
52695350

52705351
if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
5352+
unsigned long *entries = hist_pad->entries;
5353+
52715354
memset(entries, 0, HIST_STACKTRACE_SIZE);
52725355
if (key_field->field) {
52735356
unsigned long *stack, n_entries;
@@ -5291,26 +5374,31 @@ static void event_hist_trigger(struct event_trigger_data *data,
52915374
}
52925375

52935376
if (use_compound_key)
5294-
add_to_key(compound_key, key, key_field, rec);
5377+
add_to_key(hist_pad->compound_key, key, key_field, rec);
52955378
}
52965379

52975380
if (use_compound_key)
5298-
key = compound_key;
5381+
key = hist_pad->compound_key;
52995382

53005383
if (hist_data->n_var_refs &&
5301-
!resolve_var_refs(hist_data, key, var_ref_vals, false))
5302-
return;
5384+
!resolve_var_refs(hist_data, key, hist_pad->var_ref_vals, false))
5385+
goto out;
53035386

53045387
elt = tracing_map_insert(hist_data->map, key);
53055388
if (!elt)
5306-
return;
5389+
goto out;
53075390

5308-
hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, var_ref_vals);
5391+
hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, hist_pad->var_ref_vals);
53095392

5310-
if (resolve_var_refs(hist_data, key, var_ref_vals, true))
5311-
hist_trigger_actions(hist_data, elt, buffer, rec, rbe, key, var_ref_vals);
5393+
if (resolve_var_refs(hist_data, key, hist_pad->var_ref_vals, true)) {
5394+
hist_trigger_actions(hist_data, elt, buffer, rec, rbe,
5395+
key, hist_pad->var_ref_vals);
5396+
}
53125397

53135398
hist_poll_wakeup();
5399+
5400+
out:
5401+
put_hist_pad();
53145402
}
53155403

53165404
static void hist_trigger_stacktrace_print(struct seq_file *m,
@@ -6155,6 +6243,9 @@ static int event_hist_trigger_init(struct event_trigger_data *data)
61556243
{
61566244
struct hist_trigger_data *hist_data = data->private_data;
61576245

6246+
if (alloc_hist_pad() < 0)
6247+
return -ENOMEM;
6248+
61586249
if (!data->ref && hist_data->attrs->name)
61596250
save_named_trigger(hist_data->attrs->name, data);
61606251

@@ -6199,6 +6290,7 @@ static void event_hist_trigger_free(struct event_trigger_data *data)
61996290

62006291
destroy_hist_data(hist_data);
62016292
}
6293+
free_hist_pad();
62026294
}
62036295

62046296
static const struct event_trigger_ops event_hist_trigger_ops = {
@@ -6214,9 +6306,7 @@ static int event_hist_trigger_named_init(struct event_trigger_data *data)
62146306

62156307
save_named_trigger(data->named_data->name, data);
62166308

6217-
event_hist_trigger_init(data->named_data);
6218-
6219-
return 0;
6309+
return event_hist_trigger_init(data->named_data);
62206310
}
62216311

62226312
static void event_hist_trigger_named_free(struct event_trigger_data *data)

0 commit comments

Comments
 (0)