Skip to content

Commit 2246010

Browse files
tracing: Move histogram trigger variables from stack to per CPU structure
JIRA: https://issues.redhat.com/browse/RHEL-115358 commit 7ab0fc6 Author: Steven Rostedt <rostedt@goodmis.org> Date: Mon Apr 7 12:38:51 2025 -0400 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> Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
1 parent 0db160f commit 2246010

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
@@ -5249,30 +5249,113 @@ hist_trigger_actions(struct hist_trigger_data *hist_data,
52495249
}
52505250
}
52515251

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

52675344
if (unlikely(!rbe))
52685345
return;
52695346

5270-
memset(compound_key, 0, hist_data->key_size);
5347+
hist_pad = get_hist_pad();
5348+
if (!hist_pad)
5349+
return;
5350+
5351+
memset(hist_pad->compound_key, 0, hist_data->key_size);
52715352

52725353
for_each_hist_key_field(i, hist_data) {
52735354
key_field = hist_data->fields[i];
52745355

52755356
if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
5357+
unsigned long *entries = hist_pad->entries;
5358+
52765359
memset(entries, 0, HIST_STACKTRACE_SIZE);
52775360
if (key_field->field) {
52785361
unsigned long *stack, n_entries;
@@ -5296,26 +5379,31 @@ static void event_hist_trigger(struct event_trigger_data *data,
52965379
}
52975380

52985381
if (use_compound_key)
5299-
add_to_key(compound_key, key, key_field, rec);
5382+
add_to_key(hist_pad->compound_key, key, key_field, rec);
53005383
}
53015384

53025385
if (use_compound_key)
5303-
key = compound_key;
5386+
key = hist_pad->compound_key;
53045387

53055388
if (hist_data->n_var_refs &&
5306-
!resolve_var_refs(hist_data, key, var_ref_vals, false))
5307-
return;
5389+
!resolve_var_refs(hist_data, key, hist_pad->var_ref_vals, false))
5390+
goto out;
53085391

53095392
elt = tracing_map_insert(hist_data->map, key);
53105393
if (!elt)
5311-
return;
5394+
goto out;
53125395

5313-
hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, var_ref_vals);
5396+
hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, hist_pad->var_ref_vals);
53145397

5315-
if (resolve_var_refs(hist_data, key, var_ref_vals, true))
5316-
hist_trigger_actions(hist_data, elt, buffer, rec, rbe, key, var_ref_vals);
5398+
if (resolve_var_refs(hist_data, key, hist_pad->var_ref_vals, true)) {
5399+
hist_trigger_actions(hist_data, elt, buffer, rec, rbe,
5400+
key, hist_pad->var_ref_vals);
5401+
}
53175402

53185403
hist_poll_wakeup();
5404+
5405+
out:
5406+
put_hist_pad();
53195407
}
53205408

53215409
static void hist_trigger_stacktrace_print(struct seq_file *m,
@@ -6160,6 +6248,9 @@ static int event_hist_trigger_init(struct event_trigger_data *data)
61606248
{
61616249
struct hist_trigger_data *hist_data = data->private_data;
61626250

6251+
if (alloc_hist_pad() < 0)
6252+
return -ENOMEM;
6253+
61636254
if (!data->ref && hist_data->attrs->name)
61646255
save_named_trigger(hist_data->attrs->name, data);
61656256

@@ -6204,6 +6295,7 @@ static void event_hist_trigger_free(struct event_trigger_data *data)
62046295

62056296
destroy_hist_data(hist_data);
62066297
}
6298+
free_hist_pad();
62076299
}
62086300

62096301
static const struct event_trigger_ops event_hist_trigger_ops = {
@@ -6219,9 +6311,7 @@ static int event_hist_trigger_named_init(struct event_trigger_data *data)
62196311

62206312
save_named_trigger(data->named_data->name, data);
62216313

6222-
event_hist_trigger_init(data->named_data);
6223-
6224-
return 0;
6314+
return event_hist_trigger_init(data->named_data);
62256315
}
62266316

62276317
static void event_hist_trigger_named_free(struct event_trigger_data *data)

0 commit comments

Comments
 (0)