Oleg Nesterov has been working hard in closing all the holes that can

lead to race conditions between deleting an event and accessing an event
 debugfs file. This included a fix to the debugfs system (acked by
 Greg Kroah-Hartman). We think that all the holes have been patched and
 hopefully we don't find more. I haven't marked all of them for stable
 because I need to examine them more to figure out how far back some of
 the changes need to go.
 
 Along the way, some other fixes have been made. Alexander Z Lam fixed
 some logic where the wrong buffer was being modifed.
 
 Andrew Vagin found a possible corruption for machines that actually
 allocate cpumask, as a reference to one was being zeroed out by mistake.
 
 Dhaval Giani found a bad prototype when tracing is not configured.
 
 And I not only had some changes to help Oleg, but also finally fixed
 a long standing bug that Dave Jones and others have been hitting, where
 a module unload and reload can cause the function tracing accounting
 to get screwed up.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.14 (GNU/Linux)
 
 iQEcBAABAgAGBQJR/7FvAAoJEOdOSU1xswtMCAgH/RpxS5mQcQnhrGfu1qnSk+3v
 CyL1X9IkvgP5f+N59tbU3ZuE8Q3YQvTII35na38fgbHbwWrhYjLSvlf3Pwtre8zr
 Rjm9b8zA6iSobHu3DyuuupzMsLE+SpBakVSmG6mi8izZyuCi0YHMZMnHTGNnW9Vv
 YFqkfGgQQWMqiUHLcueetOqWAjR2JiJaLfr47rsRLNflF6dB2MbG/7YbYJ0TBk7E
 hemVmR7JH7606eJZ6nR7bh/hYv0sL2SSqVVaOHO5nWFLFbI8CybpNVGcoD+nihZY
 M7LrZT1C1mn3nKrfDhyXy4dwwx6wBON0fY23eJTHMVNnc0tvY1xqH52vTdPILWg=
 =UfsE
 -----END PGP SIGNATURE-----

Merge tag 'trace-fixes-3.11-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing fixes from Steven Rostedt:
 "Oleg Nesterov has been working hard in closing all the holes that can
  lead to race conditions between deleting an event and accessing an
  event debugfs file.  This included a fix to the debugfs system (acked
  by Greg Kroah-Hartman).  We think that all the holes have been patched
  and hopefully we don't find more.  I haven't marked all of them for
  stable because I need to examine them more to figure out how far back
  some of the changes need to go.

  Along the way, some other fixes have been made.  Alexander Z Lam fixed
  some logic where the wrong buffer was being modifed.

  Andrew Vagin found a possible corruption for machines that actually
  allocate cpumask, as a reference to one was being zeroed out by
  mistake.

  Dhaval Giani found a bad prototype when tracing is not configured.

  And I not only had some changes to help Oleg, but also finally fixed a
  long standing bug that Dave Jones and others have been hitting, where
  a module unload and reload can cause the function tracing accounting
  to get screwed up"

* tag 'trace-fixes-3.11-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  tracing: Fix reset of time stamps during trace_clock changes
  tracing: Make TRACE_ITER_STOP_ON_FREE stop the correct buffer
  tracing: Fix trace_dump_stack() proto when CONFIG_TRACING is not set
  tracing: Fix fields of struct trace_iterator that are zeroed by mistake
  tracing/uprobes: Fail to unregister if probe event files are in use
  tracing/kprobes: Fail to unregister if probe event files are in use
  tracing: Add comment to describe special break case in probe_remove_event_call()
  tracing: trace_remove_event_call() should fail if call/file is in use
  debugfs: debugfs_remove_recursive() must not rely on list_empty(d_subdirs)
  ftrace: Check module functions being traced on reload
  ftrace: Consolidate some duplicate code for updating ftrace ops
  tracing: Change remove_event_file_dir() to clear "d_subdirs"->i_private
  tracing: Introduce remove_event_file_dir()
  tracing: Change f_start() to take event_mutex and verify i_private != NULL
  tracing: Change event_filter_read/write to verify i_private != NULL
  tracing: Change event_enable/disable_read() to verify i_private != NULL
  tracing: Turn event/id->i_private into call->event.type
This commit is contained in:
Linus Torvalds 2013-08-07 13:01:30 -07:00
commit b7bc9e7d80
9 changed files with 305 additions and 187 deletions

View File

@ -533,8 +533,7 @@ EXPORT_SYMBOL_GPL(debugfs_remove);
*/
void debugfs_remove_recursive(struct dentry *dentry)
{
struct dentry *child;
struct dentry *parent;
struct dentry *child, *next, *parent;
if (IS_ERR_OR_NULL(dentry))
return;
@ -544,61 +543,37 @@ void debugfs_remove_recursive(struct dentry *dentry)
return;
parent = dentry;
down:
mutex_lock(&parent->d_inode->i_mutex);
list_for_each_entry_safe(child, next, &parent->d_subdirs, d_u.d_child) {
if (!debugfs_positive(child))
continue;
while (1) {
/*
* When all dentries under "parent" has been removed,
* walk up the tree until we reach our starting point.
*/
if (list_empty(&parent->d_subdirs)) {
mutex_unlock(&parent->d_inode->i_mutex);
if (parent == dentry)
break;
parent = parent->d_parent;
mutex_lock(&parent->d_inode->i_mutex);
}
child = list_entry(parent->d_subdirs.next, struct dentry,
d_u.d_child);
next_sibling:
/*
* If "child" isn't empty, walk down the tree and
* remove all its descendants first.
*/
/* perhaps simple_empty(child) makes more sense */
if (!list_empty(&child->d_subdirs)) {
mutex_unlock(&parent->d_inode->i_mutex);
parent = child;
mutex_lock(&parent->d_inode->i_mutex);
continue;
goto down;
}
__debugfs_remove(child, parent);
if (parent->d_subdirs.next == &child->d_u.d_child) {
/*
* Try the next sibling.
*/
if (child->d_u.d_child.next != &parent->d_subdirs) {
child = list_entry(child->d_u.d_child.next,
struct dentry,
d_u.d_child);
goto next_sibling;
}
/*
* Avoid infinite loop if we fail to remove
* one dentry.
*/
mutex_unlock(&parent->d_inode->i_mutex);
break;
}
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
up:
if (!__debugfs_remove(child, parent))
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
}
parent = dentry->d_parent;
mutex_lock(&parent->d_inode->i_mutex);
__debugfs_remove(dentry, parent);
mutex_unlock(&parent->d_inode->i_mutex);
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
child = parent;
parent = parent->d_parent;
mutex_lock(&parent->d_inode->i_mutex);
if (child != dentry) {
next = list_entry(child->d_u.d_child.next, struct dentry,
d_u.d_child);
goto up;
}
if (!__debugfs_remove(child, parent))
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
mutex_unlock(&parent->d_inode->i_mutex);
}
EXPORT_SYMBOL_GPL(debugfs_remove_recursive);

View File

@ -78,6 +78,11 @@ struct trace_iterator {
/* trace_seq for __print_flags() and __print_symbolic() etc. */
struct trace_seq tmp_seq;
cpumask_var_t started;
/* it's true when current open file is snapshot */
bool snapshot;
/* The below is zeroed out in pipe_read */
struct trace_seq seq;
struct trace_entry *ent;
@ -90,10 +95,7 @@ struct trace_iterator {
loff_t pos;
long idx;
cpumask_var_t started;
/* it's true when current open file is snapshot */
bool snapshot;
/* All new field here will be zeroed out in pipe_read */
};
enum trace_iter_flags {
@ -332,7 +334,7 @@ extern int trace_define_field(struct ftrace_event_call *call, const char *type,
const char *name, int offset, int size,
int is_signed, int filter_type);
extern int trace_add_event_call(struct ftrace_event_call *call);
extern void trace_remove_event_call(struct ftrace_event_call *call);
extern int trace_remove_event_call(struct ftrace_event_call *call);
#define is_signed_type(type) (((type)(-1)) < (type)1)

View File

@ -629,7 +629,7 @@ extern void ftrace_dump(enum ftrace_dump_mode oops_dump_mode);
static inline void tracing_start(void) { }
static inline void tracing_stop(void) { }
static inline void ftrace_off_permanent(void) { }
static inline void trace_dump_stack(void) { }
static inline void trace_dump_stack(int skip) { }
static inline void tracing_on(void) { }
static inline void tracing_off(void) { }

View File

@ -2169,12 +2169,57 @@ static cycle_t ftrace_update_time;
static unsigned long ftrace_update_cnt;
unsigned long ftrace_update_tot_cnt;
static int ops_traces_mod(struct ftrace_ops *ops)
static inline int ops_traces_mod(struct ftrace_ops *ops)
{
struct ftrace_hash *hash;
/*
* Filter_hash being empty will default to trace module.
* But notrace hash requires a test of individual module functions.
*/
return ftrace_hash_empty(ops->filter_hash) &&
ftrace_hash_empty(ops->notrace_hash);
}
hash = ops->filter_hash;
return ftrace_hash_empty(hash);
/*
* Check if the current ops references the record.
*
* If the ops traces all functions, then it was already accounted for.
* If the ops does not trace the current record function, skip it.
* If the ops ignores the function via notrace filter, skip it.
*/
static inline bool
ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec)
{
/* If ops isn't enabled, ignore it */
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
return 0;
/* If ops traces all mods, we already accounted for it */
if (ops_traces_mod(ops))
return 0;
/* The function must be in the filter */
if (!ftrace_hash_empty(ops->filter_hash) &&
!ftrace_lookup_ip(ops->filter_hash, rec->ip))
return 0;
/* If in notrace hash, we ignore it too */
if (ftrace_lookup_ip(ops->notrace_hash, rec->ip))
return 0;
return 1;
}
static int referenced_filters(struct dyn_ftrace *rec)
{
struct ftrace_ops *ops;
int cnt = 0;
for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) {
if (ops_references_rec(ops, rec))
cnt++;
}
return cnt;
}
static int ftrace_update_code(struct module *mod)
@ -2183,6 +2228,7 @@ static int ftrace_update_code(struct module *mod)
struct dyn_ftrace *p;
cycle_t start, stop;
unsigned long ref = 0;
bool test = false;
int i;
/*
@ -2196,9 +2242,12 @@ static int ftrace_update_code(struct module *mod)
for (ops = ftrace_ops_list;
ops != &ftrace_list_end; ops = ops->next) {
if (ops->flags & FTRACE_OPS_FL_ENABLED &&
ops_traces_mod(ops))
ref++;
if (ops->flags & FTRACE_OPS_FL_ENABLED) {
if (ops_traces_mod(ops))
ref++;
else
test = true;
}
}
}
@ -2208,12 +2257,16 @@ static int ftrace_update_code(struct module *mod)
for (pg = ftrace_new_pgs; pg; pg = pg->next) {
for (i = 0; i < pg->index; i++) {
int cnt = ref;
/* If something went wrong, bail without enabling anything */
if (unlikely(ftrace_disabled))
return -1;
p = &pg->records[i];
p->flags = ref;
if (test)
cnt += referenced_filters(p);
p->flags = cnt;
/*
* Do the initial record conversion from mcount jump
@ -2233,7 +2286,7 @@ static int ftrace_update_code(struct module *mod)
* conversion puts the module to the correct state, thus
* passing the ftrace_make_call check.
*/
if (ftrace_start_up && ref) {
if (ftrace_start_up && cnt) {
int failed = __ftrace_replace_code(p, 1);
if (failed)
ftrace_bug(failed, p->ip);
@ -3384,6 +3437,12 @@ ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove)
return add_hash_entry(hash, ip);
}
static void ftrace_ops_update_code(struct ftrace_ops *ops)
{
if (ops->flags & FTRACE_OPS_FL_ENABLED && ftrace_enabled)
ftrace_run_update_code(FTRACE_UPDATE_CALLS);
}
static int
ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len,
unsigned long ip, int remove, int reset, int enable)
@ -3426,9 +3485,8 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len,
mutex_lock(&ftrace_lock);
ret = ftrace_hash_move(ops, enable, orig_hash, hash);
if (!ret && ops->flags & FTRACE_OPS_FL_ENABLED
&& ftrace_enabled)
ftrace_run_update_code(FTRACE_UPDATE_CALLS);
if (!ret)
ftrace_ops_update_code(ops);
mutex_unlock(&ftrace_lock);
@ -3655,9 +3713,8 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
mutex_lock(&ftrace_lock);
ret = ftrace_hash_move(iter->ops, filter_hash,
orig_hash, iter->hash);
if (!ret && (iter->ops->flags & FTRACE_OPS_FL_ENABLED)
&& ftrace_enabled)
ftrace_run_update_code(FTRACE_UPDATE_CALLS);
if (!ret)
ftrace_ops_update_code(iter->ops);
mutex_unlock(&ftrace_lock);
}

View File

@ -243,20 +243,25 @@ int filter_current_check_discard(struct ring_buffer *buffer,
}
EXPORT_SYMBOL_GPL(filter_current_check_discard);
cycle_t ftrace_now(int cpu)
cycle_t buffer_ftrace_now(struct trace_buffer *buf, int cpu)
{
u64 ts;
/* Early boot up does not have a buffer yet */
if (!global_trace.trace_buffer.buffer)
if (!buf->buffer)
return trace_clock_local();
ts = ring_buffer_time_stamp(global_trace.trace_buffer.buffer, cpu);
ring_buffer_normalize_time_stamp(global_trace.trace_buffer.buffer, cpu, &ts);
ts = ring_buffer_time_stamp(buf->buffer, cpu);
ring_buffer_normalize_time_stamp(buf->buffer, cpu, &ts);
return ts;
}
cycle_t ftrace_now(int cpu)
{
return buffer_ftrace_now(&global_trace.trace_buffer, cpu);
}
/**
* tracing_is_enabled - Show if global_trace has been disabled
*
@ -1211,7 +1216,7 @@ void tracing_reset_online_cpus(struct trace_buffer *buf)
/* Make sure all commits have finished */
synchronize_sched();
buf->time_start = ftrace_now(buf->cpu);
buf->time_start = buffer_ftrace_now(buf, buf->cpu);
for_each_online_cpu(cpu)
ring_buffer_reset_cpu(buffer, cpu);
@ -1219,11 +1224,6 @@ void tracing_reset_online_cpus(struct trace_buffer *buf)
ring_buffer_record_enable(buffer);
}
void tracing_reset_current(int cpu)
{
tracing_reset(&global_trace.trace_buffer, cpu);
}
/* Must have trace_types_lock held */
void tracing_reset_all_online_cpus(void)
{
@ -4151,6 +4151,7 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
memset(&iter->seq, 0,
sizeof(struct trace_iterator) -
offsetof(struct trace_iterator, seq));
cpumask_clear(iter->started);
iter->pos = -1;
trace_event_read_lock();
@ -4468,7 +4469,7 @@ tracing_free_buffer_release(struct inode *inode, struct file *filp)
/* disable tracing ? */
if (trace_flags & TRACE_ITER_STOP_ON_FREE)
tracing_off();
tracer_tracing_off(tr);
/* resize the ring buffer to 0 */
tracing_resize_ring_buffer(tr, 0, RING_BUFFER_ALL_CPUS);
@ -4633,12 +4634,12 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
* New clock may not be consistent with the previous clock.
* Reset the buffer so that it doesn't have incomparable timestamps.
*/
tracing_reset_online_cpus(&global_trace.trace_buffer);
tracing_reset_online_cpus(&tr->trace_buffer);
#ifdef CONFIG_TRACER_MAX_TRACE
if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer)
ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func);
tracing_reset_online_cpus(&global_trace.max_buffer);
tracing_reset_online_cpus(&tr->max_buffer);
#endif
mutex_unlock(&trace_types_lock);

View File

@ -409,33 +409,42 @@ static void put_system(struct ftrace_subsystem_dir *dir)
mutex_unlock(&event_mutex);
}
/*
* Open and update trace_array ref count.
* Must have the current trace_array passed to it.
*/
static int tracing_open_generic_file(struct inode *inode, struct file *filp)
static void remove_subsystem(struct ftrace_subsystem_dir *dir)
{
struct ftrace_event_file *file = inode->i_private;
struct trace_array *tr = file->tr;
int ret;
if (!dir)
return;
if (trace_array_get(tr) < 0)
return -ENODEV;
ret = tracing_open_generic(inode, filp);
if (ret < 0)
trace_array_put(tr);
return ret;
if (!--dir->nr_events) {
debugfs_remove_recursive(dir->entry);
list_del(&dir->list);
__put_system_dir(dir);
}
}
static int tracing_release_generic_file(struct inode *inode, struct file *filp)
static void *event_file_data(struct file *filp)
{
struct ftrace_event_file *file = inode->i_private;
struct trace_array *tr = file->tr;
return ACCESS_ONCE(file_inode(filp)->i_private);
}
trace_array_put(tr);
static void remove_event_file_dir(struct ftrace_event_file *file)
{
struct dentry *dir = file->dir;
struct dentry *child;
return 0;
if (dir) {
spin_lock(&dir->d_lock); /* probably unneeded */
list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) {
if (child->d_inode) /* probably unneeded */
child->d_inode->i_private = NULL;
}
spin_unlock(&dir->d_lock);
debugfs_remove_recursive(dir);
}
list_del(&file->list);
remove_subsystem(file->system);
kmem_cache_free(file_cachep, file);
}
/*
@ -679,15 +688,25 @@ static ssize_t
event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_file *file = filp->private_data;
struct ftrace_event_file *file;
unsigned long flags;
char buf[4] = "0";
if (file->flags & FTRACE_EVENT_FL_ENABLED &&
!(file->flags & FTRACE_EVENT_FL_SOFT_DISABLED))
mutex_lock(&event_mutex);
file = event_file_data(filp);
if (likely(file))
flags = file->flags;
mutex_unlock(&event_mutex);
if (!file)
return -ENODEV;
if (flags & FTRACE_EVENT_FL_ENABLED &&
!(flags & FTRACE_EVENT_FL_SOFT_DISABLED))
strcpy(buf, "1");
if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED ||
file->flags & FTRACE_EVENT_FL_SOFT_MODE)
if (flags & FTRACE_EVENT_FL_SOFT_DISABLED ||
flags & FTRACE_EVENT_FL_SOFT_MODE)
strcat(buf, "*");
strcat(buf, "\n");
@ -699,13 +718,10 @@ static ssize_t
event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_file *file = filp->private_data;
struct ftrace_event_file *file;
unsigned long val;
int ret;
if (!file)
return -EINVAL;
ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
if (ret)
return ret;
@ -717,8 +733,11 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
switch (val) {
case 0:
case 1:
ret = -ENODEV;
mutex_lock(&event_mutex);
ret = ftrace_event_enable_disable(file, val);
file = event_file_data(filp);
if (likely(file))
ret = ftrace_event_enable_disable(file, val);
mutex_unlock(&event_mutex);
break;
@ -825,7 +844,7 @@ enum {
static void *f_next(struct seq_file *m, void *v, loff_t *pos)
{
struct ftrace_event_call *call = m->private;
struct ftrace_event_call *call = event_file_data(m->private);
struct list_head *common_head = &ftrace_common_fields;
struct list_head *head = trace_get_fields(call);
struct list_head *node = v;
@ -857,7 +876,7 @@ static void *f_next(struct seq_file *m, void *v, loff_t *pos)
static int f_show(struct seq_file *m, void *v)
{
struct ftrace_event_call *call = m->private;
struct ftrace_event_call *call = event_file_data(m->private);
struct ftrace_event_field *field;
const char *array_descriptor;
@ -910,6 +929,11 @@ static void *f_start(struct seq_file *m, loff_t *pos)
void *p = (void *)FORMAT_HEADER;
loff_t l = 0;
/* ->stop() is called even if ->start() fails */
mutex_lock(&event_mutex);
if (!event_file_data(m->private))
return ERR_PTR(-ENODEV);
while (l < *pos && p)
p = f_next(m, p, &l);
@ -918,6 +942,7 @@ static void *f_start(struct seq_file *m, loff_t *pos)
static void f_stop(struct seq_file *m, void *p)
{
mutex_unlock(&event_mutex);
}
static const struct seq_operations trace_format_seq_ops = {
@ -929,7 +954,6 @@ static const struct seq_operations trace_format_seq_ops = {
static int trace_format_open(struct inode *inode, struct file *file)
{
struct ftrace_event_call *call = inode->i_private;
struct seq_file *m;
int ret;
@ -938,7 +962,7 @@ static int trace_format_open(struct inode *inode, struct file *file)
return ret;
m = file->private_data;
m->private = call;
m->private = file;
return 0;
}
@ -946,14 +970,18 @@ static int trace_format_open(struct inode *inode, struct file *file)
static ssize_t
event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
int id = (long)event_file_data(filp);
char buf[32];
int len;
if (*ppos)
return 0;
len = sprintf(buf, "%d\n", call->event.type);
if (unlikely(!id))
return -ENODEV;
len = sprintf(buf, "%d\n", id);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
}
@ -961,21 +989,28 @@ static ssize_t
event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
struct ftrace_event_call *call;
struct trace_seq *s;
int r;
int r = -ENODEV;
if (*ppos)
return 0;
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
trace_seq_init(s);
print_event_filter(call, s);
r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
mutex_lock(&event_mutex);
call = event_file_data(filp);
if (call)
print_event_filter(call, s);
mutex_unlock(&event_mutex);
if (call)
r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
kfree(s);
@ -986,9 +1021,9 @@ static ssize_t
event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
struct ftrace_event_call *call;
char *buf;
int err;
int err = -ENODEV;
if (cnt >= PAGE_SIZE)
return -EINVAL;
@ -1003,7 +1038,12 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
}
buf[cnt] = '\0';
err = apply_event_filter(call, buf);
mutex_lock(&event_mutex);
call = event_file_data(filp);
if (call)
err = apply_event_filter(call, buf);
mutex_unlock(&event_mutex);
free_page((unsigned long) buf);
if (err < 0)
return err;
@ -1225,10 +1265,9 @@ static const struct file_operations ftrace_set_event_fops = {
};
static const struct file_operations ftrace_enable_fops = {
.open = tracing_open_generic_file,
.open = tracing_open_generic,
.read = event_enable_read,
.write = event_enable_write,
.release = tracing_release_generic_file,
.llseek = default_llseek,
};
@ -1240,7 +1279,6 @@ static const struct file_operations ftrace_event_format_fops = {
};
static const struct file_operations ftrace_event_id_fops = {
.open = tracing_open_generic,
.read = event_id_read,
.llseek = default_llseek,
};
@ -1488,8 +1526,8 @@ event_create_dir(struct dentry *parent,
#ifdef CONFIG_PERF_EVENTS
if (call->event.type && call->class->reg)
trace_create_file("id", 0444, file->dir, call,
id);
trace_create_file("id", 0444, file->dir,
(void *)(long)call->event.type, id);
#endif
/*
@ -1514,33 +1552,16 @@ event_create_dir(struct dentry *parent,
return 0;
}
static void remove_subsystem(struct ftrace_subsystem_dir *dir)
{
if (!dir)
return;
if (!--dir->nr_events) {
debugfs_remove_recursive(dir->entry);
list_del(&dir->list);
__put_system_dir(dir);
}
}
static void remove_event_from_tracers(struct ftrace_event_call *call)
{
struct ftrace_event_file *file;
struct trace_array *tr;
do_for_each_event_file_safe(tr, file) {
if (file->event_call != call)
continue;
list_del(&file->list);
debugfs_remove_recursive(file->dir);
remove_subsystem(file->system);
kmem_cache_free(file_cachep, file);
remove_event_file_dir(file);
/*
* The do_for_each_event_file_safe() is
* a double loop. After finding the call for this
@ -1692,16 +1713,53 @@ static void __trace_remove_event_call(struct ftrace_event_call *call)
destroy_preds(call);
}
/* Remove an event_call */
void trace_remove_event_call(struct ftrace_event_call *call)
static int probe_remove_event_call(struct ftrace_event_call *call)
{
struct trace_array *tr;
struct ftrace_event_file *file;
#ifdef CONFIG_PERF_EVENTS
if (call->perf_refcount)
return -EBUSY;
#endif
do_for_each_event_file(tr, file) {
if (file->event_call != call)
continue;
/*
* We can't rely on ftrace_event_enable_disable(enable => 0)
* we are going to do, FTRACE_EVENT_FL_SOFT_MODE can suppress
* TRACE_REG_UNREGISTER.
*/
if (file->flags & FTRACE_EVENT_FL_ENABLED)
return -EBUSY;
/*
* The do_for_each_event_file_safe() is
* a double loop. After finding the call for this
* trace_array, we use break to jump to the next
* trace_array.
*/
break;
} while_for_each_event_file();
__trace_remove_event_call(call);
return 0;
}
/* Remove an event_call */
int trace_remove_event_call(struct ftrace_event_call *call)
{
int ret;
mutex_lock(&trace_types_lock);
mutex_lock(&event_mutex);
down_write(&trace_event_sem);
__trace_remove_event_call(call);
ret = probe_remove_event_call(call);
up_write(&trace_event_sem);
mutex_unlock(&event_mutex);
mutex_unlock(&trace_types_lock);
return ret;
}
#define for_each_event(event, start, end) \
@ -2270,12 +2328,8 @@ __trace_remove_event_dirs(struct trace_array *tr)
{
struct ftrace_event_file *file, *next;
list_for_each_entry_safe(file, next, &tr->events, list) {
list_del(&file->list);
debugfs_remove_recursive(file->dir);
remove_subsystem(file->system);
kmem_cache_free(file_cachep, file);
}
list_for_each_entry_safe(file, next, &tr->events, list)
remove_event_file_dir(file);
}
static void

View File

@ -637,17 +637,15 @@ static void append_filter_err(struct filter_parse_state *ps,
free_page((unsigned long) buf);
}
/* caller must hold event_mutex */
void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
{
struct event_filter *filter;
struct event_filter *filter = call->filter;
mutex_lock(&event_mutex);
filter = call->filter;
if (filter && filter->filter_string)
trace_seq_printf(s, "%s\n", filter->filter_string);
else
trace_seq_puts(s, "none\n");
mutex_unlock(&event_mutex);
}
void print_subsystem_event_filter(struct event_subsystem *system,
@ -1841,23 +1839,22 @@ static int create_system_filter(struct event_subsystem *system,
return err;
}
/* caller must hold event_mutex */
int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
{
struct event_filter *filter;
int err = 0;
mutex_lock(&event_mutex);
int err;
if (!strcmp(strstrip(filter_string), "0")) {
filter_disable(call);
filter = call->filter;
if (!filter)
goto out_unlock;
return 0;
RCU_INIT_POINTER(call->filter, NULL);
/* Make sure the filter is not being used */
synchronize_sched();
__free_filter(filter);
goto out_unlock;
return 0;
}
err = create_filter(call, filter_string, true, &filter);
@ -1884,8 +1881,6 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
__free_filter(tmp);
}
}
out_unlock:
mutex_unlock(&event_mutex);
return err;
}

View File

@ -95,7 +95,7 @@ static __kprobes bool trace_probe_is_on_module(struct trace_probe *tp)
}
static int register_probe_event(struct trace_probe *tp);
static void unregister_probe_event(struct trace_probe *tp);
static int unregister_probe_event(struct trace_probe *tp);
static DEFINE_MUTEX(probe_lock);
static LIST_HEAD(probe_list);
@ -351,9 +351,12 @@ static int unregister_trace_probe(struct trace_probe *tp)
if (trace_probe_is_enabled(tp))
return -EBUSY;
/* Will fail if probe is being used by ftrace or perf */
if (unregister_probe_event(tp))
return -EBUSY;
__unregister_trace_probe(tp);
list_del(&tp->list);
unregister_probe_event(tp);
return 0;
}
@ -632,7 +635,9 @@ static int release_all_trace_probes(void)
/* TODO: Use batch unregistration */
while (!list_empty(&probe_list)) {
tp = list_entry(probe_list.next, struct trace_probe, list);
unregister_trace_probe(tp);
ret = unregister_trace_probe(tp);
if (ret)
goto end;
free_trace_probe(tp);
}
@ -1247,11 +1252,15 @@ static int register_probe_event(struct trace_probe *tp)
return ret;
}
static void unregister_probe_event(struct trace_probe *tp)
static int unregister_probe_event(struct trace_probe *tp)
{
int ret;
/* tp->event is unregistered in trace_remove_event_call() */
trace_remove_event_call(&tp->call);
kfree(tp->call.print_fmt);
ret = trace_remove_event_call(&tp->call);
if (!ret)
kfree(tp->call.print_fmt);
return ret;
}
/* Make a debugfs interface for controlling probe points */

View File

@ -70,7 +70,7 @@ struct trace_uprobe {
(sizeof(struct probe_arg) * (n)))
static int register_uprobe_event(struct trace_uprobe *tu);
static void unregister_uprobe_event(struct trace_uprobe *tu);
static int unregister_uprobe_event(struct trace_uprobe *tu);
static DEFINE_MUTEX(uprobe_lock);
static LIST_HEAD(uprobe_list);
@ -164,11 +164,17 @@ static struct trace_uprobe *find_probe_event(const char *event, const char *grou
}
/* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */
static void unregister_trace_uprobe(struct trace_uprobe *tu)
static int unregister_trace_uprobe(struct trace_uprobe *tu)
{
int ret;
ret = unregister_uprobe_event(tu);
if (ret)
return ret;
list_del(&tu->list);
unregister_uprobe_event(tu);
free_trace_uprobe(tu);
return 0;
}
/* Register a trace_uprobe and probe_event */
@ -181,9 +187,12 @@ static int register_trace_uprobe(struct trace_uprobe *tu)
/* register as an event */
old_tp = find_probe_event(tu->call.name, tu->call.class->system);
if (old_tp)
if (old_tp) {
/* delete old event */
unregister_trace_uprobe(old_tp);
ret = unregister_trace_uprobe(old_tp);
if (ret)
goto end;
}
ret = register_uprobe_event(tu);
if (ret) {
@ -256,6 +265,8 @@ static int create_trace_uprobe(int argc, char **argv)
group = UPROBE_EVENT_SYSTEM;
if (is_delete) {
int ret;
if (!event) {
pr_info("Delete command needs an event name.\n");
return -EINVAL;
@ -269,9 +280,9 @@ static int create_trace_uprobe(int argc, char **argv)
return -ENOENT;
}
/* delete an event */
unregister_trace_uprobe(tu);
ret = unregister_trace_uprobe(tu);
mutex_unlock(&uprobe_lock);
return 0;
return ret;
}
if (argc < 2) {
@ -408,16 +419,20 @@ static int create_trace_uprobe(int argc, char **argv)
return ret;
}
static void cleanup_all_probes(void)
static int cleanup_all_probes(void)
{
struct trace_uprobe *tu;
int ret = 0;
mutex_lock(&uprobe_lock);
while (!list_empty(&uprobe_list)) {
tu = list_entry(uprobe_list.next, struct trace_uprobe, list);
unregister_trace_uprobe(tu);
ret = unregister_trace_uprobe(tu);
if (ret)
break;
}
mutex_unlock(&uprobe_lock);
return ret;
}
/* Probes listing interfaces */
@ -462,8 +477,13 @@ static const struct seq_operations probes_seq_op = {
static int probes_open(struct inode *inode, struct file *file)
{
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC))
cleanup_all_probes();
int ret;
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
ret = cleanup_all_probes();
if (ret)
return ret;
}
return seq_open(file, &probes_seq_op);
}
@ -968,12 +988,17 @@ static int register_uprobe_event(struct trace_uprobe *tu)
return ret;
}
static void unregister_uprobe_event(struct trace_uprobe *tu)
static int unregister_uprobe_event(struct trace_uprobe *tu)
{
int ret;
/* tu->event is unregistered in trace_remove_event_call() */
trace_remove_event_call(&tu->call);
ret = trace_remove_event_call(&tu->call);
if (ret)
return ret;
kfree(tu->call.print_fmt);
tu->call.print_fmt = NULL;
return 0;
}
/* Make a trace interface for controling probe points */