diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index d5d94510afd3..f3d02bf98a80 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,36 @@ 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); + if (!bt->dir) + bt->debugfs_dir = q->debugfs_dir; + mutex_unlock(&q->debugfs_mutex); + 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", bt->debugfs_dir); + debugfs_lookup_and_remove("msg", bt->debugfs_dir); + } else { + debugfs_remove(bt->dir); + } put_probe_ref(); + call_rcu(&bt->rcu, blk_trace_rcu_free); + mutex_lock(&q->debugfs_mutex); } 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..4920c201bd12 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -26,6 +26,8 @@ struct blk_trace { struct dentry *dir; struct list_head running_list; atomic_t dropped; + struct dentry *debugfs_dir; + struct rcu_head rcu; }; extern int blk_trace_ioctl(struct block_device *, unsigned, char __user *);