tracing: Add rmdir to remove multibuffer instances
Add hack to allow rmdir to work to remove a multibuffer
instance.
Example:
cd /debug/tracing/instances
mkdir hello
ls
hello/
rmdir hello
ls
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index dbd2412..cc9ced0 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -5159,6 +5159,93 @@
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 remove an instance\n\n";
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, text, sizeof(text));
+}
+
+static int instance_delete(const char *name)
+{
+ struct trace_array *tr;
+ int found = 0;
+ int ret;
+
+ 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);
+
+ ret = 0;
+
+ out_unlock:
+ mutex_unlock(&trace_types_lock);
+
+ return ret;
+}
+
+static ssize_t
+trace_del_instance_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char *buf;
+ char *name;
+ 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;
+
+ ret = instance_delete(name);
+
+ (*ppos) += cnt;
+
+ return cnt;
+
+ 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,
@@ -5166,6 +5253,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 int instance_mkdir (struct inode *inode, struct dentry *dentry, umode_t mode)
{
struct dentry *parent;
@@ -5181,7 +5275,7 @@
* take the mutex. As the instances directory can not be destroyed
* or changed in any other way, it is safe to unlock it, and
* let the dentry try. If two users try to make the same dir at
- * the same time, then the new_instance_create() determine the
+ * the same time, then the new_instance_create() will determine the
* winner.
*/
mutex_unlock(&inode->i_mutex);
@@ -5193,9 +5287,41 @@
return ret;
}
+static int instance_rmdir(struct inode *inode, struct dentry *dentry)
+{
+ struct dentry *parent;
+ int ret;
+
+ /* Paranoid: Make sure the parent is the "instances" directory */
+ parent = hlist_entry(inode->i_dentry.first, struct dentry, d_alias);
+ if (WARN_ON_ONCE(parent != trace_instance_dir))
+ return -ENOENT;
+
+ /* The caller did a dget() on dentry */
+ mutex_unlock(&dentry->d_inode->i_mutex);
+
+ /*
+ * The inode mutex is locked, but debugfs_create_dir() will also
+ * take the mutex. As the instances directory can not be destroyed
+ * or changed in any other way, it is safe to unlock it, and
+ * let the dentry try. If two users try to make the same dir at
+ * the same time, then the instance_delete() will determine the
+ * winner.
+ */
+ mutex_unlock(&inode->i_mutex);
+
+ ret = instance_delete(dentry->d_iname);
+
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock(&dentry->d_inode->i_mutex);
+
+ return ret;
+}
+
static const struct inode_operations instance_dir_inode_operations = {
.lookup = simple_lookup,
.mkdir = instance_mkdir,
+ .rmdir = instance_rmdir,
};
static __init void create_trace_instances(struct dentry *d_tracer)
@@ -5210,6 +5336,9 @@
return;
trace_create_file("new", 0644, trace_instance_dir, NULL,
&trace_new_instance_fops);
+
+ trace_create_file("free", 0644, trace_instance_dir, NULL,
+ &trace_del_instance_fops);
}
static void
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 8aeac9b..592e8f2 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -997,6 +997,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 58a6130..06d6bc2 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1709,6 +1709,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)
@@ -1793,6 +1807,25 @@
return 0;
}
+int event_trace_del_tracer(struct trace_array *tr)
+{
+ /* Disable any running events */
+ __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0);
+
+ 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_enable(void)
{
struct trace_array *tr = top_trace_array();