1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Generic dynamic event control interface 4 * 5 * Copyright (C) 2018 Masami Hiramatsu <mhiramat@kernel.org> 6 */ 7 8 #include <linux/debugfs.h> 9 #include <linux/kernel.h> 10 #include <linux/list.h> 11 #include <linux/mm.h> 12 #include <linux/mutex.h> 13 #include <linux/tracefs.h> 14 15 #include "trace.h" 16 #include "trace_dynevent.h" 17 18 static DEFINE_MUTEX(dyn_event_ops_mutex); 19 static LIST_HEAD(dyn_event_ops_list); 20 21 int dyn_event_register(struct dyn_event_operations *ops) 22 { 23 if (!ops || !ops->create || !ops->show || !ops->is_busy || 24 !ops->free || !ops->match) 25 return -EINVAL; 26 27 INIT_LIST_HEAD(&ops->list); 28 mutex_lock(&dyn_event_ops_mutex); 29 list_add_tail(&ops->list, &dyn_event_ops_list); 30 mutex_unlock(&dyn_event_ops_mutex); 31 return 0; 32 } 33 34 int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type) 35 { 36 struct dyn_event *pos, *n; 37 char *system = NULL, *event, *p; 38 int ret = -ENOENT; 39 40 if (argv[0][0] == '-') { 41 if (argv[0][1] != ':') 42 return -EINVAL; 43 event = &argv[0][2]; 44 } else { 45 event = strchr(argv[0], ':'); 46 if (!event) 47 return -EINVAL; 48 event++; 49 } 50 argc--; argv++; 51 52 p = strchr(event, '/'); 53 if (p) { 54 system = event; 55 event = p + 1; 56 *p = '\0'; 57 } 58 if (event[0] == '\0') 59 return -EINVAL; 60 61 mutex_lock(&event_mutex); 62 for_each_dyn_event_safe(pos, n) { 63 if (type && type != pos->ops) 64 continue; 65 if (!pos->ops->match(system, event, 66 argc, (const char **)argv, pos)) 67 continue; 68 69 ret = pos->ops->free(pos); 70 if (ret) 71 break; 72 } 73 mutex_unlock(&event_mutex); 74 75 return ret; 76 } 77 78 static int create_dyn_event(int argc, char **argv) 79 { 80 struct dyn_event_operations *ops; 81 int ret = -ENODEV; 82 83 if (argv[0][0] == '-' || argv[0][0] == '!') 84 return dyn_event_release(argc, argv, NULL); 85 86 mutex_lock(&dyn_event_ops_mutex); 87 list_for_each_entry(ops, &dyn_event_ops_list, list) { 88 ret = ops->create(argc, (const char **)argv); 89 if (!ret || ret != -ECANCELED) 90 break; 91 } 92 mutex_unlock(&dyn_event_ops_mutex); 93 if (ret == -ECANCELED) 94 ret = -EINVAL; 95 96 return ret; 97 } 98 99 /* Protected by event_mutex */ 100 LIST_HEAD(dyn_event_list); 101 102 void *dyn_event_seq_start(struct seq_file *m, loff_t *pos) 103 { 104 mutex_lock(&event_mutex); 105 return seq_list_start(&dyn_event_list, *pos); 106 } 107 108 void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos) 109 { 110 return seq_list_next(v, &dyn_event_list, pos); 111 } 112 113 void dyn_event_seq_stop(struct seq_file *m, void *v) 114 { 115 mutex_unlock(&event_mutex); 116 } 117 118 static int dyn_event_seq_show(struct seq_file *m, void *v) 119 { 120 struct dyn_event *ev = v; 121 122 if (ev && ev->ops) 123 return ev->ops->show(m, ev); 124 125 return 0; 126 } 127 128 static const struct seq_operations dyn_event_seq_op = { 129 .start = dyn_event_seq_start, 130 .next = dyn_event_seq_next, 131 .stop = dyn_event_seq_stop, 132 .show = dyn_event_seq_show 133 }; 134 135 /* 136 * dyn_events_release_all - Release all specific events 137 * @type: the dyn_event_operations * which filters releasing events 138 * 139 * This releases all events which ->ops matches @type. If @type is NULL, 140 * all events are released. 141 * Return -EBUSY if any of them are in use, and return other errors when 142 * it failed to free the given event. Except for -EBUSY, event releasing 143 * process will be aborted at that point and there may be some other 144 * releasable events on the list. 145 */ 146 int dyn_events_release_all(struct dyn_event_operations *type) 147 { 148 struct dyn_event *ev, *tmp; 149 int ret = 0; 150 151 mutex_lock(&event_mutex); 152 for_each_dyn_event(ev) { 153 if (type && ev->ops != type) 154 continue; 155 if (ev->ops->is_busy(ev)) { 156 ret = -EBUSY; 157 goto out; 158 } 159 } 160 for_each_dyn_event_safe(ev, tmp) { 161 if (type && ev->ops != type) 162 continue; 163 ret = ev->ops->free(ev); 164 if (ret) 165 break; 166 } 167 out: 168 mutex_unlock(&event_mutex); 169 170 return ret; 171 } 172 173 static int dyn_event_open(struct inode *inode, struct file *file) 174 { 175 int ret; 176 177 ret = tracing_check_open_get_tr(NULL); 178 if (ret) 179 return ret; 180 181 if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { 182 ret = dyn_events_release_all(NULL); 183 if (ret < 0) 184 return ret; 185 } 186 187 return seq_open(file, &dyn_event_seq_op); 188 } 189 190 static ssize_t dyn_event_write(struct file *file, const char __user *buffer, 191 size_t count, loff_t *ppos) 192 { 193 return trace_parse_run_command(file, buffer, count, ppos, 194 create_dyn_event); 195 } 196 197 static const struct file_operations dynamic_events_ops = { 198 .owner = THIS_MODULE, 199 .open = dyn_event_open, 200 .read = seq_read, 201 .llseek = seq_lseek, 202 .release = seq_release, 203 .write = dyn_event_write, 204 }; 205 206 /* Make a tracefs interface for controlling dynamic events */ 207 static __init int init_dynamic_event(void) 208 { 209 struct dentry *d_tracer; 210 struct dentry *entry; 211 212 d_tracer = tracing_init_dentry(); 213 if (IS_ERR(d_tracer)) 214 return 0; 215 216 entry = tracefs_create_file("dynamic_events", 0644, d_tracer, 217 NULL, &dynamic_events_ops); 218 219 /* Event list interface */ 220 if (!entry) 221 pr_warn("Could not create tracefs 'dynamic_events' entry\n"); 222 223 return 0; 224 } 225 fs_initcall(init_dynamic_event); 226