Commit 75b8e982 authored by Steven Rostedt's avatar Steven Rostedt Committed by Steven Rostedt

tracing/filter: Swap entire filter of events

When creating a new filter, instead of allocating the filter to the
event call first and then processing the filter, it is easier to
process a temporary filter and then just swap it with the call filter.
By doing this, it simplifies the code.

A filter is allocated and processed, when it is done, it is
swapped with the call filter, synchronize_sched() is called to make
sure all callers are done with the old filter (filters are called
with premption disabled), and then the old filter is freed.

Cc: Tom Zanussi <tzanussi@gmail.com>
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent bf93f9ed
...@@ -425,10 +425,15 @@ int filter_match_preds(struct event_filter *filter, void *rec) ...@@ -425,10 +425,15 @@ int filter_match_preds(struct event_filter *filter, void *rec)
struct filter_pred *preds; struct filter_pred *preds;
struct filter_pred *pred; struct filter_pred *pred;
struct filter_pred *root; struct filter_pred *root;
int n_preds = ACCESS_ONCE(filter->n_preds); int n_preds;
int done = 0; int done = 0;
/* no filter is considered a match */ /* no filter is considered a match */
if (!filter)
return 1;
n_preds = filter->n_preds;
if (!n_preds) if (!n_preds)
return 1; return 1;
...@@ -509,6 +514,9 @@ static void parse_error(struct filter_parse_state *ps, int err, int pos) ...@@ -509,6 +514,9 @@ static void parse_error(struct filter_parse_state *ps, int err, int pos)
static void remove_filter_string(struct event_filter *filter) static void remove_filter_string(struct event_filter *filter)
{ {
if (!filter)
return;
kfree(filter->filter_string); kfree(filter->filter_string);
filter->filter_string = NULL; filter->filter_string = NULL;
} }
...@@ -568,9 +576,10 @@ static void append_filter_err(struct filter_parse_state *ps, ...@@ -568,9 +576,10 @@ static void append_filter_err(struct filter_parse_state *ps,
void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s) void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
{ {
struct event_filter *filter = call->filter; struct event_filter *filter;
mutex_lock(&event_mutex); mutex_lock(&event_mutex);
filter = call->filter;
if (filter && filter->filter_string) if (filter && filter->filter_string)
trace_seq_printf(s, "%s\n", filter->filter_string); trace_seq_printf(s, "%s\n", filter->filter_string);
else else
...@@ -581,9 +590,10 @@ void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s) ...@@ -581,9 +590,10 @@ void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
void print_subsystem_event_filter(struct event_subsystem *system, void print_subsystem_event_filter(struct event_subsystem *system,
struct trace_seq *s) struct trace_seq *s)
{ {
struct event_filter *filter = system->filter; struct event_filter *filter;
mutex_lock(&event_mutex); mutex_lock(&event_mutex);
filter = system->filter;
if (filter && filter->filter_string) if (filter && filter->filter_string)
trace_seq_printf(s, "%s\n", filter->filter_string); trace_seq_printf(s, "%s\n", filter->filter_string);
else else
...@@ -745,26 +755,9 @@ static void __free_preds(struct event_filter *filter) ...@@ -745,26 +755,9 @@ static void __free_preds(struct event_filter *filter)
filter->n_preds = 0; filter->n_preds = 0;
} }
static void reset_preds(struct event_filter *filter) static void filter_disable(struct ftrace_event_call *call)
{
int n_preds = filter->n_preds;
int i;
filter->n_preds = 0;
filter->root = NULL;
if (!filter->preds)
return;
for (i = 0; i < n_preds; i++)
filter->preds[i].fn = filter_pred_none;
}
static void filter_disable_preds(struct ftrace_event_call *call)
{ {
struct event_filter *filter = call->filter;
call->flags &= ~TRACE_EVENT_FL_FILTERED; call->flags &= ~TRACE_EVENT_FL_FILTERED;
reset_preds(filter);
} }
static void __free_filter(struct event_filter *filter) static void __free_filter(struct event_filter *filter)
...@@ -777,11 +770,16 @@ static void __free_filter(struct event_filter *filter) ...@@ -777,11 +770,16 @@ static void __free_filter(struct event_filter *filter)
kfree(filter); kfree(filter);
} }
/*
* Called when destroying the ftrace_event_call.
* The call is being freed, so we do not need to worry about
* the call being currently used. This is for module code removing
* the tracepoints from within it.
*/
void destroy_preds(struct ftrace_event_call *call) void destroy_preds(struct ftrace_event_call *call)
{ {
__free_filter(call->filter); __free_filter(call->filter);
call->filter = NULL; call->filter = NULL;
call->flags &= ~TRACE_EVENT_FL_FILTERED;
} }
static struct event_filter *__alloc_filter(void) static struct event_filter *__alloc_filter(void)
...@@ -789,11 +787,6 @@ static struct event_filter *__alloc_filter(void) ...@@ -789,11 +787,6 @@ static struct event_filter *__alloc_filter(void)
struct event_filter *filter; struct event_filter *filter;
filter = kzalloc(sizeof(*filter), GFP_KERNEL); filter = kzalloc(sizeof(*filter), GFP_KERNEL);
if (!filter)
return ERR_PTR(-ENOMEM);
filter->n_preds = 0;
return filter; return filter;
} }
...@@ -838,46 +831,28 @@ static int __alloc_preds(struct event_filter *filter, int n_preds) ...@@ -838,46 +831,28 @@ static int __alloc_preds(struct event_filter *filter, int n_preds)
return 0; return 0;
} }
static int init_filter(struct ftrace_event_call *call) static void filter_free_subsystem_preds(struct event_subsystem *system)
{
if (call->filter)
return 0;
call->flags &= ~TRACE_EVENT_FL_FILTERED;
call->filter = __alloc_filter();
if (IS_ERR(call->filter))
return PTR_ERR(call->filter);
return 0;
}
static int init_subsystem_preds(struct event_subsystem *system)
{ {
struct ftrace_event_call *call; struct ftrace_event_call *call;
int err;
list_for_each_entry(call, &ftrace_events, list) { list_for_each_entry(call, &ftrace_events, list) {
if (strcmp(call->class->system, system->name) != 0) if (strcmp(call->class->system, system->name) != 0)
continue; continue;
err = init_filter(call); filter_disable(call);
if (err) remove_filter_string(call->filter);
return err;
} }
return 0;
} }
static void filter_free_subsystem_preds(struct event_subsystem *system) static void filter_free_subsystem_filters(struct event_subsystem *system)
{ {
struct ftrace_event_call *call; struct ftrace_event_call *call;
list_for_each_entry(call, &ftrace_events, list) { list_for_each_entry(call, &ftrace_events, list) {
if (strcmp(call->class->system, system->name) != 0) if (strcmp(call->class->system, system->name) != 0)
continue; continue;
__free_filter(call->filter);
filter_disable_preds(call); call->filter = NULL;
remove_filter_string(call->filter);
} }
} }
...@@ -1743,88 +1718,129 @@ static int replace_preds(struct ftrace_event_call *call, ...@@ -1743,88 +1718,129 @@ static int replace_preds(struct ftrace_event_call *call,
return err; return err;
} }
struct filter_list {
struct list_head list;
struct event_filter *filter;
};
static int replace_system_preds(struct event_subsystem *system, static int replace_system_preds(struct event_subsystem *system,
struct filter_parse_state *ps, struct filter_parse_state *ps,
char *filter_string) char *filter_string)
{ {
struct ftrace_event_call *call; struct ftrace_event_call *call;
struct filter_list *filter_item;
struct filter_list *tmp;
LIST_HEAD(filter_list);
bool fail = true; bool fail = true;
int err; int err;
list_for_each_entry(call, &ftrace_events, list) { list_for_each_entry(call, &ftrace_events, list) {
struct event_filter *filter = call->filter;
if (strcmp(call->class->system, system->name) != 0) if (strcmp(call->class->system, system->name) != 0)
continue; continue;
/* try to see if the filter can be applied */ /*
err = replace_preds(call, filter, ps, filter_string, true); * Try to see if the filter can be applied
* (filter arg is ignored on dry_run)
*/
err = replace_preds(call, NULL, ps, filter_string, true);
if (err) if (err)
goto fail; goto fail;
} }
/* set all filter pred counts to zero */
list_for_each_entry(call, &ftrace_events, list) { list_for_each_entry(call, &ftrace_events, list) {
struct event_filter *filter = call->filter; struct event_filter *filter;
if (strcmp(call->class->system, system->name) != 0) if (strcmp(call->class->system, system->name) != 0)
continue; continue;
reset_preds(filter); filter_item = kzalloc(sizeof(*filter_item), GFP_KERNEL);
} if (!filter_item)
goto fail_mem;
/* list_add_tail(&filter_item->list, &filter_list);
* Since some of the preds may be used under preemption
* we need to wait for them to finish before we may
* reallocate them.
*/
synchronize_sched();
list_for_each_entry(call, &ftrace_events, list) { filter_item->filter = __alloc_filter();
struct event_filter *filter = call->filter; if (!filter_item->filter)
goto fail_mem;
filter = filter_item->filter;
if (strcmp(call->class->system, system->name) != 0) /* Can only fail on no memory */
continue; err = replace_filter_string(filter, filter_string);
if (err)
goto fail_mem;
/* really apply the filter */
filter_disable_preds(call);
err = replace_preds(call, filter, ps, filter_string, false); err = replace_preds(call, filter, ps, filter_string, false);
if (err) if (err) {
filter_disable_preds(call); filter_disable(call);
else { parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
append_filter_err(ps, filter);
} else
call->flags |= TRACE_EVENT_FL_FILTERED; call->flags |= TRACE_EVENT_FL_FILTERED;
replace_filter_string(filter, filter_string); /*
} * Regardless of if this returned an error, we still
* replace the filter for the call.
*/
filter = call->filter;
call->filter = filter_item->filter;
filter_item->filter = filter;
fail = false; fail = false;
} }
if (fail) if (fail)
goto fail; goto fail;
/*
* The calls can still be using the old filters.
* Do a synchronize_sched() to ensure all calls are
* done with them before we free them.
*/
synchronize_sched();
list_for_each_entry_safe(filter_item, tmp, &filter_list, list) {
__free_filter(filter_item->filter);
list_del(&filter_item->list);
kfree(filter_item);
}
return 0; return 0;
fail: fail:
/* No call succeeded */
list_for_each_entry_safe(filter_item, tmp, &filter_list, list) {
list_del(&filter_item->list);
kfree(filter_item);
}
parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0); parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
return -EINVAL; return -EINVAL;
fail_mem:
/* If any call succeeded, we still need to sync */
if (!fail)
synchronize_sched();
list_for_each_entry_safe(filter_item, tmp, &filter_list, list) {
__free_filter(filter_item->filter);
list_del(&filter_item->list);
kfree(filter_item);
}
return -ENOMEM;
} }
int apply_event_filter(struct ftrace_event_call *call, char *filter_string) int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
{ {
int err;
struct filter_parse_state *ps; struct filter_parse_state *ps;
struct event_filter *filter;
struct event_filter *tmp;
int err = 0;
mutex_lock(&event_mutex); mutex_lock(&event_mutex);
err = init_filter(call);
if (err)
goto out_unlock;
if (!strcmp(strstrip(filter_string), "0")) { if (!strcmp(strstrip(filter_string), "0")) {
filter_disable_preds(call); filter_disable(call);
reset_preds(call->filter); filter = call->filter;
if (!filter)
goto out_unlock;
call->filter = NULL;
/* Make sure the filter is not being used */ /* Make sure the filter is not being used */
synchronize_sched(); synchronize_sched();
__free_preds(call->filter); __free_filter(filter);
remove_filter_string(call->filter);
goto out_unlock; goto out_unlock;
} }
...@@ -1833,29 +1849,41 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string) ...@@ -1833,29 +1849,41 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
if (!ps) if (!ps)
goto out_unlock; goto out_unlock;
filter_disable_preds(call); filter = __alloc_filter();
replace_filter_string(call->filter, filter_string); if (!filter) {
kfree(ps);
goto out_unlock;
}
replace_filter_string(filter, filter_string);
parse_init(ps, filter_ops, filter_string); parse_init(ps, filter_ops, filter_string);
err = filter_parse(ps); err = filter_parse(ps);
if (err) { if (err) {
append_filter_err(ps, call->filter); append_filter_err(ps, filter);
goto out; goto out;
} }
/* err = replace_preds(call, filter, ps, filter_string, false);
* Make sure all the pred counts are zero so that if (err) {
* no task is using it when we reallocate the preds array. filter_disable(call);
*/ append_filter_err(ps, filter);
reset_preds(call->filter); } else
synchronize_sched();
err = replace_preds(call, call->filter, ps, filter_string, false);
if (err)
append_filter_err(ps, call->filter);
else
call->flags |= TRACE_EVENT_FL_FILTERED; call->flags |= TRACE_EVENT_FL_FILTERED;
out: out:
/*
* Always swap the call filter with the new filter
* even if there was an error. If there was an error
* in the filter, we disable the filter and show the error
* string
*/
tmp = call->filter;
call->filter = filter;
if (tmp) {
/* Make sure the call is done with the filter */
synchronize_sched();
__free_filter(tmp);
}
filter_opstack_clear(ps); filter_opstack_clear(ps);
postfix_clear(ps); postfix_clear(ps);
kfree(ps); kfree(ps);
...@@ -1868,18 +1896,21 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string) ...@@ -1868,18 +1896,21 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
int apply_subsystem_event_filter(struct event_subsystem *system, int apply_subsystem_event_filter(struct event_subsystem *system,
char *filter_string) char *filter_string)
{ {
int err;
struct filter_parse_state *ps; struct filter_parse_state *ps;
struct event_filter *filter;
int err = 0;
mutex_lock(&event_mutex); mutex_lock(&event_mutex);
err = init_subsystem_preds(system);
if (err)
goto out_unlock;
if (!strcmp(strstrip(filter_string), "0")) { if (!strcmp(strstrip(filter_string), "0")) {
filter_free_subsystem_preds(system); filter_free_subsystem_preds(system);
remove_filter_string(system->filter); remove_filter_string(system->filter);
filter = system->filter;
system->filter = NULL;
/* Ensure all filters are no longer used */
synchronize_sched();
filter_free_subsystem_filters(system);
__free_filter(filter);
goto out_unlock; goto out_unlock;
} }
...@@ -1888,7 +1919,17 @@ int apply_subsystem_event_filter(struct event_subsystem *system, ...@@ -1888,7 +1919,17 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
if (!ps) if (!ps)
goto out_unlock; goto out_unlock;
replace_filter_string(system->filter, filter_string); filter = __alloc_filter();
if (!filter)
goto out;
replace_filter_string(filter, filter_string);
/*
* No event actually uses the system filter
* we can free it without synchronize_sched().
*/
__free_filter(system->filter);
system->filter = filter;
parse_init(ps, filter_ops, filter_string); parse_init(ps, filter_ops, filter_string);
err = filter_parse(ps); err = filter_parse(ps);
...@@ -1945,7 +1986,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id, ...@@ -1945,7 +1986,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id,
goto out_unlock; goto out_unlock;
filter = __alloc_filter(); filter = __alloc_filter();
if (IS_ERR(filter)) { if (!filter) {
err = PTR_ERR(filter); err = PTR_ERR(filter);
goto out_unlock; goto out_unlock;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment