tracing: Add 'delete' file to remove multibuffer instances

Add a 'delete' file that is used to remove multibuffer instances.

Example:

  echo hello > /debug/tracing/instances/new
  ls /debug/tracing/instances
delete hello/ new
  echo hello > /debug/tracing/instances/delete
  ls /debug/tracing/instances
delete new

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index a45b9f9..15d9f07 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4885,6 +4885,83 @@
 	return ret;
 }
 
+static ssize_t
+trace_del_instance_read(struct file *filp, char __user *ubuf,
+			size_t cnt, loff_t *ppos)
+{
+	static char text[] =
+		"\nWrite into this file to delete an instance\n\n";
+
+	return simple_read_from_buffer(ubuf, cnt, ppos, text, sizeof(text));
+}
+
+static ssize_t
+trace_del_instance_write(struct file *filp, const char __user *ubuf,
+			 size_t cnt, loff_t *ppos)
+{
+	struct trace_array *tr;
+	char *buf;
+	char *name;
+	int found = 0;
+	int ret;
+
+	/* Don't let names be bigger than 1024 */
+	if (cnt > 1024)
+		return -EINVAL;
+
+	name = kmalloc(cnt+1, GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if (strncpy_from_user(name, ubuf, cnt) < 0)
+		goto out_free_name;
+
+	name[cnt] = '\0';
+
+	/* remove leading and trailing whitespace */
+	buf = strstrip(name);
+	buf = kstrdup(buf, GFP_KERNEL);
+	if (!buf)
+		goto out_free_name;
+	kfree(name);
+	name = buf;
+
+	mutex_lock(&trace_types_lock);
+
+	ret = -ENODEV;
+	list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+		if (tr->name && strcmp(tr->name, name) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		goto out_unlock;
+
+	list_del(&tr->list);
+
+	event_trace_del_tracer(tr);
+	debugfs_remove_recursive(tr->dir);
+	free_percpu(tr->data);
+	ring_buffer_free(tr->buffer);
+
+	kfree(tr->name);
+	kfree(tr);
+	mutex_unlock(&trace_types_lock);
+
+	(*ppos) += cnt;
+
+	return cnt;
+
+ out_unlock:
+	mutex_unlock(&trace_types_lock);
+
+ out_free_name:
+	kfree(name);
+	return ret;
+}
+
 static const struct file_operations trace_new_instance_fops = {
 	.open		= tracing_open_generic,
 	.read		= trace_new_instance_read,
@@ -4892,6 +4969,13 @@
 	.llseek		= default_llseek,
 };
 
+static const struct file_operations trace_del_instance_fops = {
+	.open		= tracing_open_generic,
+	.read		= trace_del_instance_read,
+	.write		= trace_del_instance_write,
+	.llseek		= default_llseek,
+};
+
 static __init void create_trace_instances(struct dentry *d_tracer)
 {
 	trace_instance_dir = debugfs_create_dir("instances", d_tracer);
@@ -4900,6 +4984,9 @@
 
 	trace_create_file("new", 0644, trace_instance_dir, NULL,
 			  &trace_new_instance_fops);
+
+	trace_create_file("delete", 0644, trace_instance_dir, NULL,
+			  &trace_del_instance_fops);
 }
 
 static void
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 4ec5c57..ce46423 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -890,6 +890,7 @@
 
 extern void trace_event_enable_cmd_record(bool enable);
 extern int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr);
+extern int event_trace_del_tracer(struct trace_array *tr);
 
 extern struct mutex event_mutex;
 extern struct list_head ftrace_events;
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 777a108..f421085 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1691,6 +1691,20 @@
 	}
 }
 
+/* Remove the event directory structure for a trace directory. */
+static void
+__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);
+		kfree(file);
+	}
+}
+
 static void
 __add_event_to_tracers(struct ftrace_event_call *call,
 		       struct ftrace_module_file_ops *file_ops)
@@ -1775,6 +1789,22 @@
 	return 0;
 }
 
+int event_trace_del_tracer(struct trace_array *tr)
+{
+	mutex_lock(&event_mutex);
+
+	down_write(&trace_event_mutex);
+	__trace_remove_event_dirs(tr);
+	debugfs_remove_recursive(tr->event_dir);
+	up_write(&trace_event_mutex);
+
+	tr->event_dir = NULL;
+
+	mutex_unlock(&event_mutex);
+
+	return 0;
+}
+
 static __init int event_trace_init(void)
 {
 	struct ftrace_event_call **call;