diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index d5d94510afd3..5eecdf9b8570 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -330,6 +330,13 @@ static void blk_trace_free(struct request_queue *q, struct blk_trace *bt) kfree(bt); } +static void blk_trace_free_rcu(struct blk_trace *bt) +{ + free_percpu(bt->sequence); + free_percpu(bt->msg_data); + kfree(bt); +} + static void get_probe_ref(void) { mutex_lock(&blk_probe_mutex); @@ -377,12 +384,32 @@ static int blk_trace_stop(struct blk_trace *bt) return 0; } +static void blk_trace_rcu_free(struct rcu_head *rcu) +{ + struct blk_trace *bt; + + bt = container_of(rcu, struct blk_trace, rcu); + if (bt) { + blk_trace_free_rcu(bt); + } +} + static void blk_trace_cleanup(struct request_queue *q, struct blk_trace *bt) { blk_trace_stop(bt); - synchronize_rcu(); - blk_trace_free(q, bt); + relay_close(bt->rchan); + /* + * If 'bt->dir' is not set, then both 'dropped' and 'msg' are created + * under 'q->debugfs_dir', thus lookup and remove them. + */ + if (!bt->dir) { + debugfs_lookup_and_remove("dropped", q->debugfs_dir); + debugfs_lookup_and_remove("msg", q->debugfs_dir); + } else { + debugfs_remove(bt->dir); + } put_probe_ref(); + call_rcu(&bt->rcu, blk_trace_rcu_free); } static int __blk_trace_remove(struct request_queue *q) diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 122c62e561fc..5f927328b7e6 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -26,6 +26,7 @@ struct blk_trace { struct dentry *dir; struct list_head running_list; atomic_t dropped; + struct rcu_head rcu; }; extern int blk_trace_ioctl(struct block_device *, unsigned, char __user *);