summaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_events_hist.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace/trace_events_hist.c')
-rw-r--r--kernel/trace/trace_events_hist.c148
1 files changed, 131 insertions, 17 deletions
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 4b02f8ab4dd3..bdea5603cbde 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -117,6 +117,7 @@ struct hist_trigger_attrs {
char *keys_str;
char *vals_str;
char *sort_key_str;
+ char *name;
bool pause;
bool cont;
bool clear;
@@ -200,6 +201,7 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
if (!attrs)
return;
+ kfree(attrs->name);
kfree(attrs->sort_key_str);
kfree(attrs->keys_str);
kfree(attrs->vals_str);
@@ -227,6 +229,8 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
attrs->vals_str = kstrdup(str, GFP_KERNEL);
else if (strncmp(str, "sort=", strlen("sort=")) == 0)
attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
+ else if (strncmp(str, "name=", strlen("name=")) == 0)
+ attrs->name = kstrdup(str, GFP_KERNEL);
else if (strcmp(str, "pause") == 0)
attrs->pause = true;
else if ((strcmp(str, "cont") == 0) ||
@@ -1131,7 +1135,12 @@ static int event_hist_trigger_print(struct seq_file *m,
struct hist_field *key_field;
unsigned int i;
- seq_puts(m, "hist:keys=");
+ seq_puts(m, "hist:");
+
+ if (data->name)
+ seq_printf(m, "%s:", data->name);
+
+ seq_puts(m, "keys=");
for_each_hist_key_field(i, hist_data) {
key_field = hist_data->fields[i];
@@ -1196,6 +1205,19 @@ static int event_hist_trigger_print(struct seq_file *m,
return 0;
}
+static int event_hist_trigger_init(struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
+{
+ struct hist_trigger_data *hist_data = data->private_data;
+
+ if (!data->ref && hist_data->attrs->name)
+ save_named_trigger(hist_data->attrs->name, data);
+
+ data->ref++;
+
+ return 0;
+}
+
static void event_hist_trigger_free(struct event_trigger_ops *ops,
struct event_trigger_data *data)
{
@@ -1206,6 +1228,8 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
data->ref--;
if (!data->ref) {
+ if (data->name)
+ del_named_trigger(data);
trigger_data_free(data);
destroy_hist_data(hist_data);
}
@@ -1214,10 +1238,44 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
static struct event_trigger_ops event_hist_trigger_ops = {
.func = event_hist_trigger,
.print = event_hist_trigger_print,
- .init = event_trigger_init,
+ .init = event_hist_trigger_init,
.free = event_hist_trigger_free,
};
+static int event_hist_trigger_named_init(struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
+{
+ data->ref++;
+
+ save_named_trigger(data->named_data->name, data);
+
+ event_hist_trigger_init(ops, data->named_data);
+
+ return 0;
+}
+
+static void event_hist_trigger_named_free(struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
+{
+ if (WARN_ON_ONCE(data->ref <= 0))
+ return;
+
+ event_hist_trigger_free(ops, data->named_data);
+
+ data->ref--;
+ if (!data->ref) {
+ del_named_trigger(data);
+ trigger_data_free(data);
+ }
+}
+
+static struct event_trigger_ops event_hist_trigger_named_ops = {
+ .func = event_hist_trigger,
+ .print = event_hist_trigger_print,
+ .init = event_hist_trigger_named_init,
+ .free = event_hist_trigger_named_free,
+};
+
static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
char *param)
{
@@ -1227,26 +1285,54 @@ static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
static void hist_clear(struct event_trigger_data *data)
{
struct hist_trigger_data *hist_data = data->private_data;
- bool paused;
- paused = data->paused;
- data->paused = true;
+ if (data->name)
+ pause_named_trigger(data);
synchronize_sched();
tracing_map_clear(hist_data->map);
- data->paused = paused;
+ if (data->name)
+ unpause_named_trigger(data);
+}
+
+static bool compatible_field(struct ftrace_event_field *field,
+ struct ftrace_event_field *test_field)
+{
+ if (field == test_field)
+ return true;
+ if (field == NULL || test_field == NULL)
+ return false;
+ if (strcmp(field->name, test_field->name) != 0)
+ return false;
+ if (strcmp(field->type, test_field->type) != 0)
+ return false;
+ if (field->size != test_field->size)
+ return false;
+ if (field->is_signed != test_field->is_signed)
+ return false;
+
+ return true;
}
static bool hist_trigger_match(struct event_trigger_data *data,
- struct event_trigger_data *data_test)
+ struct event_trigger_data *data_test,
+ struct event_trigger_data *named_data,
+ bool ignore_filter)
{
struct tracing_map_sort_key *sort_key, *sort_key_test;
struct hist_trigger_data *hist_data, *hist_data_test;
struct hist_field *key_field, *key_field_test;
unsigned int i;
+ if (named_data && (named_data != data_test) &&
+ (named_data != data_test->named_data))
+ return false;
+
+ if (!named_data && is_named_trigger(data_test))
+ return false;
+
hist_data = data->private_data;
hist_data_test = data_test->private_data;
@@ -1255,9 +1341,11 @@ static bool hist_trigger_match(struct event_trigger_data *data,
hist_data->n_sort_keys != hist_data_test->n_sort_keys)
return false;
- if ((data->filter_str && !data_test->filter_str) ||
- (!data->filter_str && data_test->filter_str))
- return false;
+ if (!ignore_filter) {
+ if ((data->filter_str && !data_test->filter_str) ||
+ (!data->filter_str && data_test->filter_str))
+ return false;
+ }
for_each_hist_field(i, hist_data) {
key_field = hist_data->fields[i];
@@ -1265,7 +1353,7 @@ static bool hist_trigger_match(struct event_trigger_data *data,
if (key_field->flags != key_field_test->flags)
return false;
- if (key_field->field != key_field_test->field)
+ if (!compatible_field(key_field->field, key_field_test->field))
return false;
if (key_field->offset != key_field_test->offset)
return false;
@@ -1280,7 +1368,7 @@ static bool hist_trigger_match(struct event_trigger_data *data,
return false;
}
- if (data->filter_str &&
+ if (!ignore_filter && data->filter_str &&
(strcmp(data->filter_str, data_test->filter_str) != 0))
return false;
@@ -1292,12 +1380,26 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
struct trace_event_file *file)
{
struct hist_trigger_data *hist_data = data->private_data;
- struct event_trigger_data *test;
+ struct event_trigger_data *test, *named_data = NULL;
int ret = 0;
+ if (hist_data->attrs->name) {
+ named_data = find_named_trigger(hist_data->attrs->name);
+ if (named_data) {
+ if (!hist_trigger_match(data, named_data, named_data,
+ true)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+ if (hist_data->attrs->name && !named_data)
+ goto new;
+
list_for_each_entry_rcu(test, &file->triggers, list) {
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
- if (!hist_trigger_match(data, test))
+ if (!hist_trigger_match(data, test, named_data, false))
continue;
if (hist_data->attrs->pause)
test->paused = true;
@@ -1310,12 +1412,19 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
goto out;
}
}
-
+ new:
if (hist_data->attrs->cont || hist_data->attrs->clear) {
ret = -ENOENT;
goto out;
}
+ if (named_data) {
+ destroy_hist_data(data->private_data);
+ data->private_data = named_data->private_data;
+ set_named_trigger_data(data, named_data);
+ data->ops = &event_hist_trigger_named_ops;
+ }
+
if (hist_data->attrs->pause)
data->paused = true;
@@ -1329,6 +1438,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
ret++;
update_cond_flag(file);
+
if (trace_event_trigger_enable_disable(file, 1) < 0) {
list_del_rcu(&data->list);
update_cond_flag(file);
@@ -1342,12 +1452,16 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
struct event_trigger_data *data,
struct trace_event_file *file)
{
- struct event_trigger_data *test;
+ struct hist_trigger_data *hist_data = data->private_data;
+ struct event_trigger_data *test, *named_data = NULL;
bool unregistered = false;
+ if (hist_data->attrs->name)
+ named_data = find_named_trigger(hist_data->attrs->name);
+
list_for_each_entry_rcu(test, &file->triggers, list) {
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
- if (!hist_trigger_match(data, test))
+ if (!hist_trigger_match(data, test, named_data, false))
continue;
unregistered = true;
list_del_rcu(&test->list);