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 if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { 178 ret = dyn_events_release_all(NULL); 179 if (ret < 0) 180 return ret; 181 } 182 183 return seq_open(file, &dyn_event_seq_op); 184 } 185 186 static ssize_t dyn_event_write(struct file *file, const char __user *buffer, 187 size_t count, loff_t *ppos) 188 { 189 return trace_parse_run_command(file, buffer, count, ppos, 190 create_dyn_event); 191 } 192 193 static const struct file_operations dynamic_events_ops = { 194 .owner = THIS_MODULE, 195 .open = dyn_event_open, 196 .read = seq_read, 197 .llseek = seq_lseek, 198 .release = seq_release, 199 .write = dyn_event_write, 200 }; 201 202 /* Make a tracefs interface for controlling dynamic events */ 203 static __init int init_dynamic_event(void) 204 { 205 struct dentry *d_tracer; 206 struct dentry *entry; 207 208 d_tracer = tracing_init_dentry(); 209 if (IS_ERR(d_tracer)) 210 return 0; 211 212 entry = tracefs_create_file("dynamic_events", 0644, d_tracer, 213 NULL, &dynamic_events_ops); 214 215 /* Event list interface */ 216 if (!entry) 217 pr_warn("Could not create tracefs 'dynamic_events' entry\n"); 218 219 return 0; 220 } 221 fs_initcall(init_dynamic_event); 222