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