diff options
author | Nathan O <otternes@cs.unc.edu> | 2019-11-15 11:19:35 -0500 |
---|---|---|
committer | Nathan O <otternes@cs.unc.edu> | 2019-11-15 11:19:35 -0500 |
commit | 2627f203874e04500ea80f6e588cd659bec5866b (patch) | |
tree | feec07a6a87a24460a19808dcd88ba36ad03201d /litmus/ftdev.c | |
parent | bf929479893052b1c7bfe23a4e7a903643076350 (diff) |
Re-add LITMUS files, but don't "connect the wires"
- Added the LITMUS^RT source files from the most recent version of
LITMUS (based on the old kernel).
- This change does *not* actually make use of any of the new files,
because I wanted to make sure that any changes for getting LITMUS to
actually work are clearly visible in future commits. If I made any
such changes before committing the files themselves, then it wouldn't
be as easy to see what needed to change in the LITMUS source files,
and the large number of changes would make it more difficult to see
what needed to change in the base Linux sources, too.
Diffstat (limited to 'litmus/ftdev.c')
-rw-r--r-- | litmus/ftdev.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/litmus/ftdev.c b/litmus/ftdev.c new file mode 100644 index 000000000000..646e8c9fe230 --- /dev/null +++ b/litmus/ftdev.c | |||
@@ -0,0 +1,457 @@ | |||
1 | #include <linux/sched.h> | ||
2 | #include <linux/fs.h> | ||
3 | #include <linux/slab.h> | ||
4 | #include <linux/cdev.h> | ||
5 | #include <asm/uaccess.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/device.h> | ||
8 | #include <linux/vmalloc.h> | ||
9 | #include <linux/mutex.h> | ||
10 | |||
11 | #include <litmus/feather_trace.h> | ||
12 | #include <litmus/ftdev.h> | ||
13 | |||
14 | struct ft_buffer* alloc_ft_buffer(unsigned int count, size_t size) | ||
15 | { | ||
16 | struct ft_buffer* buf; | ||
17 | size_t total = (size + 1) * count; | ||
18 | char* mem; | ||
19 | |||
20 | buf = kmalloc(sizeof(*buf), GFP_KERNEL); | ||
21 | if (!buf) | ||
22 | return NULL; | ||
23 | |||
24 | |||
25 | mem = vmalloc(total); | ||
26 | |||
27 | if (!mem) { | ||
28 | kfree(buf); | ||
29 | return NULL; | ||
30 | } | ||
31 | |||
32 | if (!init_ft_buffer(buf, count, size, | ||
33 | mem + (count * size), /* markers at the end */ | ||
34 | mem)) { /* buffer objects */ | ||
35 | vfree(mem); | ||
36 | kfree(buf); | ||
37 | return NULL; | ||
38 | } | ||
39 | return buf; | ||
40 | } | ||
41 | |||
42 | void free_ft_buffer(struct ft_buffer* buf) | ||
43 | { | ||
44 | if (buf) { | ||
45 | vfree(buf->buffer_mem); | ||
46 | kfree(buf); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | struct ftdev_event { | ||
51 | int id; | ||
52 | struct ftdev_event* next; | ||
53 | }; | ||
54 | |||
55 | static DEFINE_MUTEX(ft_event_activation_mutex); | ||
56 | |||
57 | static int activate(struct ftdev_event** chain, int id) | ||
58 | { | ||
59 | struct ftdev_event* ev = kmalloc(sizeof(*ev), GFP_KERNEL); | ||
60 | if (ev) { | ||
61 | mutex_lock(&ft_event_activation_mutex); | ||
62 | printk(KERN_INFO | ||
63 | "Enabling feather-trace event %d.\n", (int) id); | ||
64 | ft_enable_event(id); | ||
65 | mutex_unlock(&ft_event_activation_mutex); | ||
66 | ev->id = id; | ||
67 | ev->next = *chain; | ||
68 | *chain = ev; | ||
69 | } | ||
70 | return ev ? 0 : -ENOMEM; | ||
71 | } | ||
72 | |||
73 | static void deactivate(struct ftdev_event** chain, int id) | ||
74 | { | ||
75 | struct ftdev_event **cur = chain; | ||
76 | struct ftdev_event *nxt; | ||
77 | while (*cur) { | ||
78 | if ((*cur)->id == id) { | ||
79 | nxt = (*cur)->next; | ||
80 | kfree(*cur); | ||
81 | *cur = nxt; | ||
82 | printk(KERN_INFO | ||
83 | "Disabling feather-trace event %d.\n", (int) id); | ||
84 | mutex_lock(&ft_event_activation_mutex); | ||
85 | ft_disable_event(id); | ||
86 | mutex_unlock(&ft_event_activation_mutex); | ||
87 | break; | ||
88 | } | ||
89 | cur = &(*cur)->next; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | static int ftdev_open(struct inode *in, struct file *filp) | ||
94 | { | ||
95 | struct ftdev* ftdev; | ||
96 | struct ftdev_minor* ftdm; | ||
97 | unsigned int buf_idx = iminor(in); | ||
98 | int err = 0; | ||
99 | |||
100 | ftdev = container_of(in->i_cdev, struct ftdev, cdev); | ||
101 | |||
102 | if (buf_idx >= ftdev->minor_cnt) { | ||
103 | err = -ENODEV; | ||
104 | goto out; | ||
105 | } | ||
106 | if (ftdev->can_open && (err = ftdev->can_open(ftdev, buf_idx))) | ||
107 | goto out; | ||
108 | |||
109 | ftdm = ftdev->minor + buf_idx; | ||
110 | ftdm->ftdev = ftdev; | ||
111 | filp->private_data = ftdm; | ||
112 | |||
113 | if (mutex_lock_interruptible(&ftdm->lock)) { | ||
114 | err = -ERESTARTSYS; | ||
115 | goto out; | ||
116 | } | ||
117 | |||
118 | if (!ftdm->readers && ftdev->alloc) | ||
119 | err = ftdev->alloc(ftdev, buf_idx); | ||
120 | if (0 == err) | ||
121 | ftdm->readers++; | ||
122 | |||
123 | mutex_unlock(&ftdm->lock); | ||
124 | out: | ||
125 | return err; | ||
126 | } | ||
127 | |||
128 | static int ftdev_release(struct inode *in, struct file *filp) | ||
129 | { | ||
130 | struct ftdev* ftdev; | ||
131 | struct ftdev_minor* ftdm; | ||
132 | unsigned int buf_idx = iminor(in); | ||
133 | int err = 0; | ||
134 | |||
135 | ftdev = container_of(in->i_cdev, struct ftdev, cdev); | ||
136 | |||
137 | if (buf_idx >= ftdev->minor_cnt) { | ||
138 | err = -ENODEV; | ||
139 | goto out; | ||
140 | } | ||
141 | ftdm = ftdev->minor + buf_idx; | ||
142 | |||
143 | if (mutex_lock_interruptible(&ftdm->lock)) { | ||
144 | err = -ERESTARTSYS; | ||
145 | goto out; | ||
146 | } | ||
147 | |||
148 | if (ftdm->readers == 1) { | ||
149 | while (ftdm->events) | ||
150 | deactivate(&ftdm->events, ftdm->events->id); | ||
151 | |||
152 | /* wait for any pending events to complete */ | ||
153 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
154 | schedule_timeout(HZ); | ||
155 | |||
156 | printk(KERN_ALERT "Failed trace writes: %u\n", | ||
157 | atomic_read(&ftdm->buf->failed_writes)); | ||
158 | |||
159 | if (ftdev->free) | ||
160 | ftdev->free(ftdev, buf_idx); | ||
161 | } | ||
162 | |||
163 | ftdm->readers--; | ||
164 | mutex_unlock(&ftdm->lock); | ||
165 | out: | ||
166 | return err; | ||
167 | } | ||
168 | |||
169 | /* based on ft_buffer_read | ||
170 | * @returns < 0 : page fault | ||
171 | * = 0 : no data available | ||
172 | * = 1 : one slot copied | ||
173 | */ | ||
174 | static int ft_buffer_copy_to_user(struct ft_buffer* buf, char __user *dest) | ||
175 | { | ||
176 | unsigned int idx; | ||
177 | int err = 0; | ||
178 | if (atomic_read(&buf->free_count) != buf->slot_count) { | ||
179 | /* data available */ | ||
180 | idx = buf->read_idx % buf->slot_count; | ||
181 | if (buf->slots[idx] == SLOT_READY) { | ||
182 | err = copy_to_user(dest, ((char*) buf->buffer_mem) + | ||
183 | idx * buf->slot_size, | ||
184 | buf->slot_size); | ||
185 | if (err == 0) { | ||
186 | /* copy ok */ | ||
187 | buf->slots[idx] = SLOT_FREE; | ||
188 | buf->read_idx++; | ||
189 | atomic_fetch_inc(&buf->free_count); | ||
190 | err = 1; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | return err; | ||
195 | } | ||
196 | |||
197 | static ssize_t ftdev_read(struct file *filp, | ||
198 | char __user *to, size_t len, loff_t *f_pos) | ||
199 | { | ||
200 | /* we ignore f_pos, this is strictly sequential */ | ||
201 | |||
202 | ssize_t err = 0; | ||
203 | size_t chunk; | ||
204 | int copied; | ||
205 | struct ftdev_minor* ftdm = filp->private_data; | ||
206 | |||
207 | if (mutex_lock_interruptible(&ftdm->lock)) { | ||
208 | err = -ERESTARTSYS; | ||
209 | goto out; | ||
210 | } | ||
211 | |||
212 | |||
213 | chunk = ftdm->buf->slot_size; | ||
214 | while (len >= chunk) { | ||
215 | copied = ft_buffer_copy_to_user(ftdm->buf, to); | ||
216 | if (copied == 1) { | ||
217 | len -= chunk; | ||
218 | to += chunk; | ||
219 | err += chunk; | ||
220 | } else if (err == 0 && copied == 0 && ftdm->events) { | ||
221 | /* Only wait if there are any events enabled and only | ||
222 | * if we haven't copied some data yet. We cannot wait | ||
223 | * here with copied data because that data would get | ||
224 | * lost if the task is interrupted (e.g., killed). | ||
225 | */ | ||
226 | |||
227 | /* Before sleeping, check wether a non-blocking | ||
228 | * read was requested. | ||
229 | */ | ||
230 | if (filp->f_flags & O_NONBLOCK) | ||
231 | { | ||
232 | /* bug out, userspace doesn't want us to sleep */ | ||
233 | err = -EWOULDBLOCK; | ||
234 | break; | ||
235 | } | ||
236 | |||
237 | mutex_unlock(&ftdm->lock); | ||
238 | set_current_state(TASK_INTERRUPTIBLE); | ||
239 | |||
240 | schedule_timeout(50); | ||
241 | |||
242 | if (signal_pending(current)) { | ||
243 | if (err == 0) | ||
244 | /* nothing read yet, signal problem */ | ||
245 | err = -ERESTARTSYS; | ||
246 | goto out; | ||
247 | } | ||
248 | if (mutex_lock_interruptible(&ftdm->lock)) { | ||
249 | err = -ERESTARTSYS; | ||
250 | goto out; | ||
251 | } | ||
252 | } else if (copied < 0) { | ||
253 | /* page fault */ | ||
254 | err = copied; | ||
255 | break; | ||
256 | } else | ||
257 | /* nothing left to get, return to user space */ | ||
258 | break; | ||
259 | } | ||
260 | mutex_unlock(&ftdm->lock); | ||
261 | out: | ||
262 | return err; | ||
263 | } | ||
264 | |||
265 | static long ftdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||
266 | { | ||
267 | long err = -ENOIOCTLCMD; | ||
268 | struct ftdev_minor* ftdm = filp->private_data; | ||
269 | |||
270 | if (mutex_lock_interruptible(&ftdm->lock)) { | ||
271 | err = -ERESTARTSYS; | ||
272 | goto out; | ||
273 | } | ||
274 | |||
275 | /* FIXME: check id against list of acceptable events */ | ||
276 | |||
277 | switch (cmd) { | ||
278 | case FTDEV_ENABLE_CMD: | ||
279 | if (activate(&ftdm->events, arg)) | ||
280 | err = -ENOMEM; | ||
281 | else | ||
282 | err = 0; | ||
283 | break; | ||
284 | |||
285 | case FTDEV_DISABLE_CMD: | ||
286 | deactivate(&ftdm->events, arg); | ||
287 | err = 0; | ||
288 | break; | ||
289 | |||
290 | case FTDEV_CALIBRATE: | ||
291 | if (ftdm->ftdev->calibrate) { | ||
292 | err = ftdm->ftdev->calibrate(ftdm->ftdev, iminor(file_inode(filp)), arg); | ||
293 | } | ||
294 | break; | ||
295 | |||
296 | default: | ||
297 | printk(KERN_DEBUG "ftdev: strange ioctl (%u, %lu)\n", cmd, arg); | ||
298 | }; | ||
299 | |||
300 | mutex_unlock(&ftdm->lock); | ||
301 | out: | ||
302 | return err; | ||
303 | } | ||
304 | |||
305 | static ssize_t ftdev_write(struct file *filp, const char __user *from, | ||
306 | size_t len, loff_t *f_pos) | ||
307 | { | ||
308 | struct ftdev_minor* ftdm = filp->private_data; | ||
309 | ssize_t err = -EINVAL; | ||
310 | struct ftdev* ftdev = ftdm->ftdev; | ||
311 | |||
312 | /* dispatch write to buffer-specific code, if available */ | ||
313 | if (ftdev->write) | ||
314 | err = ftdev->write(ftdm->buf, len, from); | ||
315 | |||
316 | return err; | ||
317 | } | ||
318 | |||
319 | struct file_operations ftdev_fops = { | ||
320 | .owner = THIS_MODULE, | ||
321 | .open = ftdev_open, | ||
322 | .release = ftdev_release, | ||
323 | .write = ftdev_write, | ||
324 | .read = ftdev_read, | ||
325 | .unlocked_ioctl = ftdev_ioctl, | ||
326 | }; | ||
327 | |||
328 | int ftdev_init( struct ftdev* ftdev, struct module* owner, | ||
329 | const int minor_cnt, const char* name) | ||
330 | { | ||
331 | int i, err; | ||
332 | |||
333 | BUG_ON(minor_cnt < 1); | ||
334 | |||
335 | cdev_init(&ftdev->cdev, &ftdev_fops); | ||
336 | ftdev->name = name; | ||
337 | ftdev->minor_cnt = minor_cnt; | ||
338 | ftdev->cdev.owner = owner; | ||
339 | ftdev->cdev.ops = &ftdev_fops; | ||
340 | ftdev->alloc = NULL; | ||
341 | ftdev->free = NULL; | ||
342 | ftdev->can_open = NULL; | ||
343 | ftdev->write = NULL; | ||
344 | ftdev->calibrate = NULL; | ||
345 | |||
346 | ftdev->minor = kcalloc(ftdev->minor_cnt, sizeof(*ftdev->minor), | ||
347 | GFP_KERNEL); | ||
348 | if (!ftdev->minor) { | ||
349 | printk(KERN_WARNING "ftdev(%s): Could not allocate memory\n", | ||
350 | ftdev->name); | ||
351 | err = -ENOMEM; | ||
352 | goto err_out; | ||
353 | } | ||
354 | |||
355 | for (i = 0; i < ftdev->minor_cnt; i++) { | ||
356 | mutex_init(&ftdev->minor[i].lock); | ||
357 | ftdev->minor[i].readers = 0; | ||
358 | ftdev->minor[i].buf = NULL; | ||
359 | ftdev->minor[i].events = NULL; | ||
360 | } | ||
361 | |||
362 | ftdev->class = class_create(owner, ftdev->name); | ||
363 | if (IS_ERR(ftdev->class)) { | ||
364 | err = PTR_ERR(ftdev->class); | ||
365 | printk(KERN_WARNING "ftdev(%s): " | ||
366 | "Could not create device class.\n", ftdev->name); | ||
367 | goto err_dealloc; | ||
368 | } | ||
369 | |||
370 | return 0; | ||
371 | |||
372 | err_dealloc: | ||
373 | kfree(ftdev->minor); | ||
374 | err_out: | ||
375 | return err; | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * Destroy minor devices up to, but not including, up_to. | ||
380 | */ | ||
381 | static void ftdev_device_destroy(struct ftdev* ftdev, unsigned int up_to) | ||
382 | { | ||
383 | dev_t minor_cntr; | ||
384 | |||
385 | if (up_to < 1) | ||
386 | up_to = (ftdev->minor_cnt < 1) ? 0 : ftdev->minor_cnt; | ||
387 | |||
388 | for (minor_cntr = 0; minor_cntr < up_to; ++minor_cntr) | ||
389 | device_destroy(ftdev->class, MKDEV(ftdev->major, minor_cntr)); | ||
390 | } | ||
391 | |||
392 | void ftdev_exit(struct ftdev* ftdev) | ||
393 | { | ||
394 | printk("ftdev(%s): Exiting\n", ftdev->name); | ||
395 | ftdev_device_destroy(ftdev, -1); | ||
396 | cdev_del(&ftdev->cdev); | ||
397 | unregister_chrdev_region(MKDEV(ftdev->major, 0), ftdev->minor_cnt); | ||
398 | class_destroy(ftdev->class); | ||
399 | kfree(ftdev->minor); | ||
400 | } | ||
401 | |||
402 | int register_ftdev(struct ftdev* ftdev) | ||
403 | { | ||
404 | struct device **device; | ||
405 | dev_t trace_dev_tmp, minor_cntr; | ||
406 | int err; | ||
407 | |||
408 | err = alloc_chrdev_region(&trace_dev_tmp, 0, ftdev->minor_cnt, | ||
409 | ftdev->name); | ||
410 | if (err) { | ||
411 | printk(KERN_WARNING "ftdev(%s): " | ||
412 | "Could not allocate char. device region (%d minors)\n", | ||
413 | ftdev->name, ftdev->minor_cnt); | ||
414 | goto err_out; | ||
415 | } | ||
416 | |||
417 | ftdev->major = MAJOR(trace_dev_tmp); | ||
418 | |||
419 | err = cdev_add(&ftdev->cdev, trace_dev_tmp, ftdev->minor_cnt); | ||
420 | if (err) { | ||
421 | printk(KERN_WARNING "ftdev(%s): " | ||
422 | "Could not add cdev for major %u with %u minor(s).\n", | ||
423 | ftdev->name, ftdev->major, ftdev->minor_cnt); | ||
424 | goto err_unregister; | ||
425 | } | ||
426 | |||
427 | /* create the minor device(s) */ | ||
428 | for (minor_cntr = 0; minor_cntr < ftdev->minor_cnt; ++minor_cntr) | ||
429 | { | ||
430 | trace_dev_tmp = MKDEV(ftdev->major, minor_cntr); | ||
431 | device = &ftdev->minor[minor_cntr].device; | ||
432 | |||
433 | *device = device_create(ftdev->class, NULL, trace_dev_tmp, NULL, | ||
434 | "litmus/%s%d", ftdev->name, minor_cntr); | ||
435 | if (IS_ERR(*device)) { | ||
436 | err = PTR_ERR(*device); | ||
437 | printk(KERN_WARNING "ftdev(%s): " | ||
438 | "Could not create device major/minor number " | ||
439 | "%u/%u\n", ftdev->name, ftdev->major, | ||
440 | minor_cntr); | ||
441 | printk(KERN_WARNING "ftdev(%s): " | ||
442 | "will attempt deletion of allocated devices.\n", | ||
443 | ftdev->name); | ||
444 | goto err_minors; | ||
445 | } | ||
446 | } | ||
447 | |||
448 | return 0; | ||
449 | |||
450 | err_minors: | ||
451 | ftdev_device_destroy(ftdev, minor_cntr); | ||
452 | cdev_del(&ftdev->cdev); | ||
453 | err_unregister: | ||
454 | unregister_chrdev_region(MKDEV(ftdev->major, 0), ftdev->minor_cnt); | ||
455 | err_out: | ||
456 | return err; | ||
457 | } | ||