Branch data Line data Source code
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 : 3 : int dyn_event_register(struct dyn_event_operations *ops) 22 : : { 23 : 3 : if (!ops || !ops->create || !ops->show || !ops->is_busy || 24 : 3 : !ops->free || !ops->match) 25 : : return -EINVAL; 26 : : 27 : 3 : INIT_LIST_HEAD(&ops->list); 28 : 3 : mutex_lock(&dyn_event_ops_mutex); 29 : : list_add_tail(&ops->list, &dyn_event_ops_list); 30 : 3 : mutex_unlock(&dyn_event_ops_mutex); 31 : 3 : return 0; 32 : : } 33 : : 34 : 0 : 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 : 0 : if (argv[0][0] == '-') { 41 : 0 : if (argv[0][1] != ':') 42 : : return -EINVAL; 43 : 0 : event = &argv[0][2]; 44 : : } else { 45 : 0 : event = strchr(argv[0], ':'); 46 : 0 : if (!event) 47 : : return -EINVAL; 48 : 0 : event++; 49 : : } 50 : 0 : argc--; argv++; 51 : : 52 : 0 : p = strchr(event, '/'); 53 : 0 : if (p) { 54 : : system = event; 55 : 0 : event = p + 1; 56 : 0 : *p = '\0'; 57 : : } 58 : 0 : if (event[0] == '\0') 59 : : return -EINVAL; 60 : : 61 : 0 : mutex_lock(&event_mutex); 62 : 0 : for_each_dyn_event_safe(pos, n) { 63 : 0 : if (type && type != pos->ops) 64 : 0 : continue; 65 : 0 : if (!pos->ops->match(system, event, 66 : : argc, (const char **)argv, pos)) 67 : 0 : continue; 68 : : 69 : 0 : ret = pos->ops->free(pos); 70 : 0 : if (ret) 71 : : break; 72 : : } 73 : 0 : mutex_unlock(&event_mutex); 74 : : 75 : 0 : return ret; 76 : : } 77 : : 78 : 0 : static int create_dyn_event(int argc, char **argv) 79 : : { 80 : : struct dyn_event_operations *ops; 81 : : int ret = -ENODEV; 82 : : 83 : 0 : if (argv[0][0] == '-' || argv[0][0] == '!') 84 : 0 : return dyn_event_release(argc, argv, NULL); 85 : : 86 : 0 : mutex_lock(&dyn_event_ops_mutex); 87 : 0 : list_for_each_entry(ops, &dyn_event_ops_list, list) { 88 : 0 : ret = ops->create(argc, (const char **)argv); 89 : 0 : if (!ret || ret != -ECANCELED) 90 : : break; 91 : : } 92 : 0 : mutex_unlock(&dyn_event_ops_mutex); 93 : 0 : if (ret == -ECANCELED) 94 : : ret = -EINVAL; 95 : : 96 : 0 : return ret; 97 : : } 98 : : 99 : : /* Protected by event_mutex */ 100 : : LIST_HEAD(dyn_event_list); 101 : : 102 : 0 : void *dyn_event_seq_start(struct seq_file *m, loff_t *pos) 103 : : { 104 : 0 : mutex_lock(&event_mutex); 105 : 0 : return seq_list_start(&dyn_event_list, *pos); 106 : : } 107 : : 108 : 0 : void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos) 109 : : { 110 : 0 : return seq_list_next(v, &dyn_event_list, pos); 111 : : } 112 : : 113 : 0 : void dyn_event_seq_stop(struct seq_file *m, void *v) 114 : : { 115 : 0 : mutex_unlock(&event_mutex); 116 : 0 : } 117 : : 118 : 0 : static int dyn_event_seq_show(struct seq_file *m, void *v) 119 : : { 120 : : struct dyn_event *ev = v; 121 : : 122 : 0 : if (ev && ev->ops) 123 : 0 : 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 : 0 : int dyn_events_release_all(struct dyn_event_operations *type) 147 : : { 148 : : struct dyn_event *ev, *tmp; 149 : : int ret = 0; 150 : : 151 : 0 : mutex_lock(&event_mutex); 152 : 0 : for_each_dyn_event(ev) { 153 : 0 : if (type && ev->ops != type) 154 : 0 : continue; 155 : 0 : if (ev->ops->is_busy(ev)) { 156 : : ret = -EBUSY; 157 : : goto out; 158 : : } 159 : : } 160 : 0 : for_each_dyn_event_safe(ev, tmp) { 161 : 0 : if (type && ev->ops != type) 162 : 0 : continue; 163 : 0 : ret = ev->ops->free(ev); 164 : 0 : if (ret) 165 : : break; 166 : : } 167 : : out: 168 : 0 : mutex_unlock(&event_mutex); 169 : : 170 : 0 : return ret; 171 : : } 172 : : 173 : 0 : static int dyn_event_open(struct inode *inode, struct file *file) 174 : : { 175 : : int ret; 176 : : 177 : 0 : ret = tracing_check_open_get_tr(NULL); 178 : 0 : if (ret) 179 : : return ret; 180 : : 181 : 0 : if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { 182 : 0 : ret = dyn_events_release_all(NULL); 183 : 0 : if (ret < 0) 184 : : return ret; 185 : : } 186 : : 187 : 0 : return seq_open(file, &dyn_event_seq_op); 188 : : } 189 : : 190 : 0 : static ssize_t dyn_event_write(struct file *file, const char __user *buffer, 191 : : size_t count, loff_t *ppos) 192 : : { 193 : 0 : 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 : 3 : static __init int init_dynamic_event(void) 208 : : { 209 : : struct dentry *d_tracer; 210 : : struct dentry *entry; 211 : : 212 : 3 : d_tracer = tracing_init_dentry(); 213 : 3 : if (IS_ERR(d_tracer)) 214 : : return 0; 215 : : 216 : 3 : entry = tracefs_create_file("dynamic_events", 0644, d_tracer, 217 : : NULL, &dynamic_events_ops); 218 : : 219 : : /* Event list interface */ 220 : 3 : if (!entry) 221 : 0 : pr_warn("Could not create tracefs 'dynamic_events' entry\n"); 222 : : 223 : : return 0; 224 : : } 225 : : fs_initcall(init_dynamic_event);