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 51 p = strchr(event, '/'); 52 if (p) { 53 system = event; 54 event = p + 1; 55 *p = '\0'; 56 } 57 if (event[0] == '\0') 58 return -EINVAL; 59 60 mutex_lock(&event_mutex); 61 for_each_dyn_event_safe(pos, n) { 62 if (type && type != pos->ops) 63 continue; 64 if (pos->ops->match(system, event, pos)) { 65 ret = pos->ops->free(pos); 66 break; 67 } 68 } 69 mutex_unlock(&event_mutex); 70 71 return ret; 72 } 73 74 static int create_dyn_event(int argc, char **argv) 75 { 76 struct dyn_event_operations *ops; 77 int ret = -ENODEV; 78 79 if (argv[0][0] == '-' || argv[0][0] == '!') 80 return dyn_event_release(argc, argv, NULL); 81 82 mutex_lock(&dyn_event_ops_mutex); 83 list_for_each_entry(ops, &dyn_event_ops_list, list) { 84 ret = ops->create(argc, (const char **)argv); 85 if (!ret || ret != -ECANCELED) 86 break; 87 } 88 mutex_unlock(&dyn_event_ops_mutex); 89 if (ret == -ECANCELED) 90 ret = -EINVAL; 91 92 return ret; 93 } 94 95 /* Protected by event_mutex */ 96 LIST_HEAD(dyn_event_list); 97 98 void *dyn_event_seq_start(struct seq_file *m, loff_t *pos) 99 { 100 mutex_lock(&event_mutex); 101 return seq_list_start(&dyn_event_list, *pos); 102 } 103 104 void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos) 105 { 106 return seq_list_next(v, &dyn_event_list, pos); 107 } 108 109 void dyn_event_seq_stop(struct seq_file *m, void *v) 110 { 111 mutex_unlock(&event_mutex); 112 } 113 114 static int dyn_event_seq_show(struct seq_file *m, void *v) 115 { 116 struct dyn_event *ev = v; 117 118 if (ev && ev->ops) 119 return ev->ops->show(m, ev); 120 121 return 0; 122 } 123 124 static const struct seq_operations dyn_event_seq_op = { 125 .start = dyn_event_seq_start, 126 .next = dyn_event_seq_next, 127 .stop = dyn_event_seq_stop, 128 .show = dyn_event_seq_show 129 }; 130 131 /* 132 * dyn_events_release_all - Release all specific events 133 * @type: the dyn_event_operations * which filters releasing events 134 * 135 * This releases all events which ->ops matches @type. If @type is NULL, 136 * all events are released. 137 * Return -EBUSY if any of them are in use, and return other errors when 138 * it failed to free the given event. Except for -EBUSY, event releasing 139 * process will be aborted at that point and there may be some other 140 * releasable events on the list. 141 */ 142 int dyn_events_release_all(struct dyn_event_operations *type) 143 { 144 struct dyn_event *ev, *tmp; 145 int ret = 0; 146 147 mutex_lock(&event_mutex); 148 for_each_dyn_event(ev) { 149 if (type && ev->ops != type) 150 continue; 151 if (ev->ops->is_busy(ev)) { 152 ret = -EBUSY; 153 goto out; 154 } 155 } 156 for_each_dyn_event_safe(ev, tmp) { 157 if (type && ev->ops != type) 158 continue; 159 ret = ev->ops->free(ev); 160 if (ret) 161 break; 162 } 163 out: 164 mutex_unlock(&event_mutex); 165 166 return ret; 167 } 168 169 static int dyn_event_open(struct inode *inode, struct file *file) 170 { 171 int ret; 172 173 if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { 174 ret = dyn_events_release_all(NULL); 175 if (ret < 0) 176 return ret; 177 } 178 179 return seq_open(file, &dyn_event_seq_op); 180 } 181 182 static ssize_t dyn_event_write(struct file *file, const char __user *buffer, 183 size_t count, loff_t *ppos) 184 { 185 return trace_parse_run_command(file, buffer, count, ppos, 186 create_dyn_event); 187 } 188 189 static const struct file_operations dynamic_events_ops = { 190 .owner = THIS_MODULE, 191 .open = dyn_event_open, 192 .read = seq_read, 193 .llseek = seq_lseek, 194 .release = seq_release, 195 .write = dyn_event_write, 196 }; 197 198 /* Make a tracefs interface for controlling dynamic events */ 199 static __init int init_dynamic_event(void) 200 { 201 struct dentry *d_tracer; 202 struct dentry *entry; 203 204 d_tracer = tracing_init_dentry(); 205 if (IS_ERR(d_tracer)) 206 return 0; 207 208 entry = tracefs_create_file("dynamic_events", 0644, d_tracer, 209 NULL, &dynamic_events_ops); 210 211 /* Event list interface */ 212 if (!entry) 213 pr_warn("Could not create tracefs 'dynamic_events' entry\n"); 214 215 return 0; 216 } 217 fs_initcall(init_dynamic_event); 218