Commit ac1adc55 authored by Tom Zanussi's avatar Tom Zanussi Committed by Ingo Molnar

tracing/filters: add filter_mutex to protect filter predicates

This patch adds a filter_mutex to prevent the filter predicates from
being accessed concurrently by various external functions.

It's based on a previous patch by Li Zefan:
        "[PATCH 7/7] tracing/filters: make filter preds RCU safe"

v2 changes:

- fixed wrong value returned in a add_subsystem_pred() failure case
  noticed by Li Zefan.

[ Impact: fix trace filter corruption/crashes on parallel access ]
Signed-off-by: default avatarTom Zanussi <tzanussi@gmail.com>
Reviewed-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Tested-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: paulmck@linux.vnet.ibm.com
LKML-Reference: <1239946028.6639.13.camel@tropicana>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 46de405f
...@@ -757,13 +757,15 @@ struct filter_pred { ...@@ -757,13 +757,15 @@ struct filter_pred {
}; };
extern void filter_free_pred(struct filter_pred *pred); extern void filter_free_pred(struct filter_pred *pred);
extern void filter_print_preds(struct filter_pred **preds, int n_preds, extern void filter_print_preds(struct ftrace_event_call *call,
struct trace_seq *s); struct trace_seq *s);
extern int filter_parse(char **pbuf, struct filter_pred *pred); extern int filter_parse(char **pbuf, struct filter_pred *pred);
extern int filter_add_pred(struct ftrace_event_call *call, extern int filter_add_pred(struct ftrace_event_call *call,
struct filter_pred *pred); struct filter_pred *pred);
extern void filter_disable_preds(struct ftrace_event_call *call); extern void filter_disable_preds(struct ftrace_event_call *call);
extern void filter_free_subsystem_preds(struct event_subsystem *system); extern void filter_free_subsystem_preds(struct event_subsystem *system);
extern void filter_print_subsystem_preds(struct event_subsystem *system,
struct trace_seq *s);
extern int filter_add_subsystem_pred(struct event_subsystem *system, extern int filter_add_subsystem_pred(struct event_subsystem *system,
struct filter_pred *pred); struct filter_pred *pred);
......
...@@ -488,7 +488,7 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, ...@@ -488,7 +488,7 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
trace_seq_init(s); trace_seq_init(s);
filter_print_preds(call->preds, call->n_preds, s); filter_print_preds(call, s);
r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
kfree(s); kfree(s);
...@@ -558,7 +558,7 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, ...@@ -558,7 +558,7 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
trace_seq_init(s); trace_seq_init(s);
filter_print_preds(system->preds, system->n_preds, s); filter_print_subsystem_preds(system, s);
r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
kfree(s); kfree(s);
......
...@@ -22,10 +22,13 @@ ...@@ -22,10 +22,13 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/mutex.h>
#include "trace.h" #include "trace.h"
#include "trace_output.h" #include "trace_output.h"
static DEFINE_MUTEX(filter_mutex);
static int filter_pred_64(struct filter_pred *pred, void *event) static int filter_pred_64(struct filter_pred *pred, void *event)
{ {
u64 *addr = (u64 *)(event + pred->offset); u64 *addr = (u64 *)(event + pred->offset);
...@@ -112,7 +115,7 @@ int filter_match_preds(struct ftrace_event_call *call, void *rec) ...@@ -112,7 +115,7 @@ int filter_match_preds(struct ftrace_event_call *call, void *rec)
} }
EXPORT_SYMBOL_GPL(filter_match_preds); EXPORT_SYMBOL_GPL(filter_match_preds);
void filter_print_preds(struct filter_pred **preds, int n_preds, static void __filter_print_preds(struct filter_pred **preds, int n_preds,
struct trace_seq *s) struct trace_seq *s)
{ {
char *field_name; char *field_name;
...@@ -138,6 +141,21 @@ void filter_print_preds(struct filter_pred **preds, int n_preds, ...@@ -138,6 +141,21 @@ void filter_print_preds(struct filter_pred **preds, int n_preds,
} }
} }
void filter_print_preds(struct ftrace_event_call *call, struct trace_seq *s)
{
mutex_lock(&filter_mutex);
__filter_print_preds(call->preds, call->n_preds, s);
mutex_unlock(&filter_mutex);
}
void filter_print_subsystem_preds(struct event_subsystem *system,
struct trace_seq *s)
{
mutex_lock(&filter_mutex);
__filter_print_preds(system->preds, system->n_preds, s);
mutex_unlock(&filter_mutex);
}
static struct ftrace_event_field * static struct ftrace_event_field *
find_event_field(struct ftrace_event_call *call, char *name) find_event_field(struct ftrace_event_call *call, char *name)
{ {
...@@ -180,7 +198,7 @@ static int filter_set_pred(struct filter_pred *dest, ...@@ -180,7 +198,7 @@ static int filter_set_pred(struct filter_pred *dest,
return 0; return 0;
} }
void filter_disable_preds(struct ftrace_event_call *call) static void __filter_disable_preds(struct ftrace_event_call *call)
{ {
int i; int i;
...@@ -190,6 +208,13 @@ void filter_disable_preds(struct ftrace_event_call *call) ...@@ -190,6 +208,13 @@ void filter_disable_preds(struct ftrace_event_call *call)
call->preds[i]->fn = filter_pred_none; call->preds[i]->fn = filter_pred_none;
} }
void filter_disable_preds(struct ftrace_event_call *call)
{
mutex_lock(&filter_mutex);
__filter_disable_preds(call);
mutex_unlock(&filter_mutex);
}
int init_preds(struct ftrace_event_call *call) int init_preds(struct ftrace_event_call *call)
{ {
struct filter_pred *pred; struct filter_pred *pred;
...@@ -223,7 +248,7 @@ int init_preds(struct ftrace_event_call *call) ...@@ -223,7 +248,7 @@ int init_preds(struct ftrace_event_call *call)
} }
EXPORT_SYMBOL_GPL(init_preds); EXPORT_SYMBOL_GPL(init_preds);
void filter_free_subsystem_preds(struct event_subsystem *system) static void __filter_free_subsystem_preds(struct event_subsystem *system)
{ {
struct ftrace_event_call *call; struct ftrace_event_call *call;
int i; int i;
...@@ -241,18 +266,25 @@ void filter_free_subsystem_preds(struct event_subsystem *system) ...@@ -241,18 +266,25 @@ void filter_free_subsystem_preds(struct event_subsystem *system)
continue; continue;
if (!strcmp(call->system, system->name)) if (!strcmp(call->system, system->name))
filter_disable_preds(call); __filter_disable_preds(call);
} }
} }
static int __filter_add_pred(struct ftrace_event_call *call, void filter_free_subsystem_preds(struct event_subsystem *system)
{
mutex_lock(&filter_mutex);
__filter_free_subsystem_preds(system);
mutex_unlock(&filter_mutex);
}
static int filter_add_pred_fn(struct ftrace_event_call *call,
struct filter_pred *pred, struct filter_pred *pred,
filter_pred_fn_t fn) filter_pred_fn_t fn)
{ {
int idx, err; int idx, err;
if (call->n_preds && !pred->compound) if (call->n_preds && !pred->compound)
filter_disable_preds(call); __filter_disable_preds(call);
if (call->n_preds == MAX_FILTER_PRED) if (call->n_preds == MAX_FILTER_PRED)
return -ENOSPC; return -ENOSPC;
...@@ -276,7 +308,8 @@ static int is_string_field(const char *type) ...@@ -276,7 +308,8 @@ static int is_string_field(const char *type)
return 0; return 0;
} }
int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) static int __filter_add_pred(struct ftrace_event_call *call,
struct filter_pred *pred)
{ {
struct ftrace_event_field *field; struct ftrace_event_field *field;
filter_pred_fn_t fn; filter_pred_fn_t fn;
...@@ -293,7 +326,7 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) ...@@ -293,7 +326,7 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
return -EINVAL; return -EINVAL;
fn = filter_pred_string; fn = filter_pred_string;
pred->str_len = field->size; pred->str_len = field->size;
return __filter_add_pred(call, pred, fn); return filter_add_pred_fn(call, pred, fn);
} else { } else {
if (pred->str_len) if (pred->str_len)
return -EINVAL; return -EINVAL;
...@@ -316,7 +349,18 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) ...@@ -316,7 +349,18 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
return -EINVAL; return -EINVAL;
} }
return __filter_add_pred(call, pred, fn); return filter_add_pred_fn(call, pred, fn);
}
int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
{
int err;
mutex_lock(&filter_mutex);
err = __filter_add_pred(call, pred);
mutex_unlock(&filter_mutex);
return err;
} }
int filter_add_subsystem_pred(struct event_subsystem *system, int filter_add_subsystem_pred(struct event_subsystem *system,
...@@ -324,20 +368,27 @@ int filter_add_subsystem_pred(struct event_subsystem *system, ...@@ -324,20 +368,27 @@ int filter_add_subsystem_pred(struct event_subsystem *system,
{ {
struct ftrace_event_call *call; struct ftrace_event_call *call;
mutex_lock(&filter_mutex);
if (system->n_preds && !pred->compound) if (system->n_preds && !pred->compound)
filter_free_subsystem_preds(system); __filter_free_subsystem_preds(system);
if (!system->n_preds) { if (!system->n_preds) {
system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
GFP_KERNEL); GFP_KERNEL);
if (!system->preds) if (!system->preds) {
mutex_unlock(&filter_mutex);
return -ENOMEM; return -ENOMEM;
} }
}
if (system->n_preds == MAX_FILTER_PRED) if (system->n_preds == MAX_FILTER_PRED) {
mutex_unlock(&filter_mutex);
return -ENOSPC; return -ENOSPC;
}
system->preds[system->n_preds] = pred; system->preds[system->n_preds] = pred;
system->n_preds++;
list_for_each_entry(call, &ftrace_events, list) { list_for_each_entry(call, &ftrace_events, list) {
int err; int err;
...@@ -348,17 +399,16 @@ int filter_add_subsystem_pred(struct event_subsystem *system, ...@@ -348,17 +399,16 @@ int filter_add_subsystem_pred(struct event_subsystem *system,
if (strcmp(call->system, system->name)) if (strcmp(call->system, system->name))
continue; continue;
if (!find_event_field(call, pred->field_name)) err = __filter_add_pred(call, pred);
continue;
err = filter_add_pred(call, pred);
if (err == -ENOMEM) { if (err == -ENOMEM) {
system->preds[system->n_preds] = NULL; system->preds[system->n_preds] = NULL;
system->n_preds--;
mutex_unlock(&filter_mutex);
return err; return err;
} }
} }
system->n_preds++; mutex_unlock(&filter_mutex);
return 0; return 0;
} }
......
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