RDMA/qedr: Move variables reset to qedr_set_common_qp_params()
[linux-2.6-microblaze.git] / kernel / trace / trace_events_inject.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * trace_events_inject - trace event injection
4  *
5  * Copyright (C) 2019 Cong Wang <cwang@twitter.com>
6  */
7
8 #include <linux/module.h>
9 #include <linux/ctype.h>
10 #include <linux/mutex.h>
11 #include <linux/slab.h>
12 #include <linux/rculist.h>
13
14 #include "trace.h"
15
16 static int
17 trace_inject_entry(struct trace_event_file *file, void *rec, int len)
18 {
19         struct trace_event_buffer fbuffer;
20         int written = 0;
21         void *entry;
22
23         rcu_read_lock_sched();
24         entry = trace_event_buffer_reserve(&fbuffer, file, len);
25         if (entry) {
26                 memcpy(entry, rec, len);
27                 written = len;
28                 trace_event_buffer_commit(&fbuffer);
29         }
30         rcu_read_unlock_sched();
31
32         return written;
33 }
34
35 static int
36 parse_field(char *str, struct trace_event_call *call,
37             struct ftrace_event_field **pf, u64 *pv)
38 {
39         struct ftrace_event_field *field;
40         char *field_name;
41         int s, i = 0;
42         int len;
43         u64 val;
44
45         if (!str[i])
46                 return 0;
47         /* First find the field to associate to */
48         while (isspace(str[i]))
49                 i++;
50         s = i;
51         while (isalnum(str[i]) || str[i] == '_')
52                 i++;
53         len = i - s;
54         if (!len)
55                 return -EINVAL;
56
57         field_name = kmemdup_nul(str + s, len, GFP_KERNEL);
58         if (!field_name)
59                 return -ENOMEM;
60         field = trace_find_event_field(call, field_name);
61         kfree(field_name);
62         if (!field)
63                 return -ENOENT;
64
65         *pf = field;
66         while (isspace(str[i]))
67                 i++;
68         if (str[i] != '=')
69                 return -EINVAL;
70         i++;
71         while (isspace(str[i]))
72                 i++;
73         s = i;
74         if (isdigit(str[i]) || str[i] == '-') {
75                 char *num, c;
76                 int ret;
77
78                 /* Make sure the field is not a string */
79                 if (is_string_field(field))
80                         return -EINVAL;
81
82                 if (str[i] == '-')
83                         i++;
84
85                 /* We allow 0xDEADBEEF */
86                 while (isalnum(str[i]))
87                         i++;
88                 num = str + s;
89                 c = str[i];
90                 if (c != '\0' && !isspace(c))
91                         return -EINVAL;
92                 str[i] = '\0';
93                 /* Make sure it is a value */
94                 if (field->is_signed)
95                         ret = kstrtoll(num, 0, &val);
96                 else
97                         ret = kstrtoull(num, 0, &val);
98                 str[i] = c;
99                 if (ret)
100                         return ret;
101
102                 *pv = val;
103                 return i;
104         } else if (str[i] == '\'' || str[i] == '"') {
105                 char q = str[i];
106
107                 /* Make sure the field is OK for strings */
108                 if (!is_string_field(field))
109                         return -EINVAL;
110
111                 for (i++; str[i]; i++) {
112                         if (str[i] == '\\' && str[i + 1]) {
113                                 i++;
114                                 continue;
115                         }
116                         if (str[i] == q)
117                                 break;
118                 }
119                 if (!str[i])
120                         return -EINVAL;
121
122                 /* Skip quotes */
123                 s++;
124                 len = i - s;
125                 if (len >= MAX_FILTER_STR_VAL)
126                         return -EINVAL;
127
128                 *pv = (unsigned long)(str + s);
129                 str[i] = 0;
130                 /* go past the last quote */
131                 i++;
132                 return i;
133         }
134
135         return -EINVAL;
136 }
137
138 static int trace_get_entry_size(struct trace_event_call *call)
139 {
140         struct ftrace_event_field *field;
141         struct list_head *head;
142         int size = 0;
143
144         head = trace_get_fields(call);
145         list_for_each_entry(field, head, link) {
146                 if (field->size + field->offset > size)
147                         size = field->size + field->offset;
148         }
149
150         return size;
151 }
152
153 static void *trace_alloc_entry(struct trace_event_call *call, int *size)
154 {
155         int entry_size = trace_get_entry_size(call);
156         struct ftrace_event_field *field;
157         struct list_head *head;
158         void *entry = NULL;
159
160         /* We need an extra '\0' at the end. */
161         entry = kzalloc(entry_size + 1, GFP_KERNEL);
162         if (!entry)
163                 return NULL;
164
165         head = trace_get_fields(call);
166         list_for_each_entry(field, head, link) {
167                 if (!is_string_field(field))
168                         continue;
169                 if (field->filter_type == FILTER_STATIC_STRING)
170                         continue;
171                 if (field->filter_type == FILTER_DYN_STRING) {
172                         u32 *str_item;
173                         int str_loc = entry_size & 0xffff;
174
175                         str_item = (u32 *)(entry + field->offset);
176                         *str_item = str_loc; /* string length is 0. */
177                 } else {
178                         char **paddr;
179
180                         paddr = (char **)(entry + field->offset);
181                         *paddr = "";
182                 }
183         }
184
185         *size = entry_size + 1;
186         return entry;
187 }
188
189 #define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
190
191 /* Caller is responsible to free the *pentry. */
192 static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
193 {
194         struct ftrace_event_field *field;
195         void *entry = NULL;
196         int entry_size;
197         u64 val = 0;
198         int len;
199
200         entry = trace_alloc_entry(call, &entry_size);
201         *pentry = entry;
202         if (!entry)
203                 return -ENOMEM;
204
205         tracing_generic_entry_update(entry, call->event.type,
206                                      tracing_gen_ctx());
207
208         while ((len = parse_field(str, call, &field, &val)) > 0) {
209                 if (is_function_field(field))
210                         return -EINVAL;
211
212                 if (is_string_field(field)) {
213                         char *addr = (char *)(unsigned long) val;
214
215                         if (field->filter_type == FILTER_STATIC_STRING) {
216                                 strlcpy(entry + field->offset, addr, field->size);
217                         } else if (field->filter_type == FILTER_DYN_STRING) {
218                                 int str_len = strlen(addr) + 1;
219                                 int str_loc = entry_size & 0xffff;
220                                 u32 *str_item;
221
222                                 entry_size += str_len;
223                                 *pentry = krealloc(entry, entry_size, GFP_KERNEL);
224                                 if (!*pentry) {
225                                         kfree(entry);
226                                         return -ENOMEM;
227                                 }
228                                 entry = *pentry;
229
230                                 strlcpy(entry + (entry_size - str_len), addr, str_len);
231                                 str_item = (u32 *)(entry + field->offset);
232                                 *str_item = (str_len << 16) | str_loc;
233                         } else {
234                                 char **paddr;
235
236                                 paddr = (char **)(entry + field->offset);
237                                 *paddr = INJECT_STRING;
238                         }
239                 } else {
240                         switch (field->size) {
241                         case 1: {
242                                 u8 tmp = (u8) val;
243
244                                 memcpy(entry + field->offset, &tmp, 1);
245                                 break;
246                         }
247                         case 2: {
248                                 u16 tmp = (u16) val;
249
250                                 memcpy(entry + field->offset, &tmp, 2);
251                                 break;
252                         }
253                         case 4: {
254                                 u32 tmp = (u32) val;
255
256                                 memcpy(entry + field->offset, &tmp, 4);
257                                 break;
258                         }
259                         case 8:
260                                 memcpy(entry + field->offset, &val, 8);
261                                 break;
262                         default:
263                                 return -EINVAL;
264                         }
265                 }
266
267                 str += len;
268         }
269
270         if (len < 0)
271                 return len;
272
273         return entry_size;
274 }
275
276 static ssize_t
277 event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
278                    loff_t *ppos)
279 {
280         struct trace_event_call *call;
281         struct trace_event_file *file;
282         int err = -ENODEV, size;
283         void *entry = NULL;
284         char *buf;
285
286         if (cnt >= PAGE_SIZE)
287                 return -EINVAL;
288
289         buf = memdup_user_nul(ubuf, cnt);
290         if (IS_ERR(buf))
291                 return PTR_ERR(buf);
292         strim(buf);
293
294         mutex_lock(&event_mutex);
295         file = event_file_data(filp);
296         if (file) {
297                 call = file->event_call;
298                 size = parse_entry(buf, call, &entry);
299                 if (size < 0)
300                         err = size;
301                 else
302                         err = trace_inject_entry(file, entry, size);
303         }
304         mutex_unlock(&event_mutex);
305
306         kfree(entry);
307         kfree(buf);
308
309         if (err < 0)
310                 return err;
311
312         *ppos += err;
313         return cnt;
314 }
315
316 static ssize_t
317 event_inject_read(struct file *file, char __user *buf, size_t size,
318                   loff_t *ppos)
319 {
320         return -EPERM;
321 }
322
323 const struct file_operations event_inject_fops = {
324         .open = tracing_open_generic,
325         .read = event_inject_read,
326         .write = event_inject_write,
327 };