aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/litmus/ftdev.h49
-rw-r--r--litmus/ftdev.c352
2 files changed, 401 insertions, 0 deletions
diff --git a/include/litmus/ftdev.h b/include/litmus/ftdev.h
new file mode 100644
index 000000000000..7697b4616699
--- /dev/null
+++ b/include/litmus/ftdev.h
@@ -0,0 +1,49 @@
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 MAX_FTDEV_MINORS NR_CPUS
10
11#define FTDEV_ENABLE_CMD 0
12#define FTDEV_DISABLE_CMD 1
13
14struct ftdev;
15
16/* return 0 if buffer can be opened, otherwise -$REASON */
17typedef int (*ftdev_can_open_t)(struct ftdev* dev, unsigned int buf_no);
18/* return 0 on success, otherwise -$REASON */
19typedef int (*ftdev_alloc_t)(struct ftdev* dev, unsigned int buf_no);
20typedef void (*ftdev_free_t)(struct ftdev* dev, unsigned int buf_no);
21
22
23struct ftdev_event;
24
25struct ftdev_minor {
26 struct ft_buffer* buf;
27 unsigned int readers;
28 struct mutex lock;
29 /* FIXME: filter for authorized events */
30 struct ftdev_event* events;
31};
32
33struct ftdev {
34 struct cdev cdev;
35 /* FIXME: don't waste memory, allocate dynamically */
36 struct ftdev_minor minor[MAX_FTDEV_MINORS];
37 unsigned int minor_cnt;
38 ftdev_alloc_t alloc;
39 ftdev_free_t free;
40 ftdev_can_open_t can_open;
41};
42
43struct ft_buffer* alloc_ft_buffer(unsigned int count, size_t size);
44void free_ft_buffer(struct ft_buffer* buf);
45
46void ftdev_init(struct ftdev* ftdev, struct module* owner);
47int register_ftdev(struct ftdev* ftdev, const char* name, int major);
48
49#endif
diff --git a/litmus/ftdev.c b/litmus/ftdev.c
new file mode 100644
index 000000000000..1c1c241a0a69
--- /dev/null
+++ b/litmus/ftdev.c
@@ -0,0 +1,352 @@
1#include <linux/sched.h>
2#include <linux/fs.h>
3#include <linux/cdev.h>
4#include <asm/uaccess.h>
5#include <linux/module.h>
6
7#include <litmus/litmus.h>
8#include <litmus/feather_trace.h>
9#include <litmus/ftdev.h>
10
11struct ft_buffer* alloc_ft_buffer(unsigned int count, size_t size)
12{
13 struct ft_buffer* buf;
14 size_t total = (size + 1) * count;
15 char* mem;
16 int order = 0, pages = 1;
17
18 buf = kmalloc(sizeof(*buf), GFP_KERNEL);
19 if (!buf)
20 return NULL;
21
22 total = (total / PAGE_SIZE) + (total % PAGE_SIZE != 0);
23 while (pages < total) {
24 order++;
25 pages *= 2;
26 }
27
28 mem = (char*) __get_free_pages(GFP_KERNEL, order);
29 if (!mem) {
30 kfree(buf);
31 return NULL;
32 }
33
34 if (!init_ft_buffer(buf, count, size,
35 mem + (count * size), /* markers at the end */
36 mem)) { /* buffer objects */
37 free_pages((unsigned long) mem, order);
38 kfree(buf);
39 return NULL;
40 }
41 return buf;
42}
43
44void free_ft_buffer(struct ft_buffer* buf)
45{
46 int order = 0, pages = 1;
47 size_t total;
48
49 if (buf) {
50 total = (buf->slot_size + 1) * buf->slot_count;
51 total = (total / PAGE_SIZE) + (total % PAGE_SIZE != 0);
52 while (pages < total) {
53 order++;
54 pages *= 2;
55 }
56 free_pages((unsigned long) buf->buffer_mem, order);
57 kfree(buf);
58 }
59}
60
61struct ftdev_event {
62 int id;
63 struct ftdev_event* next;
64};
65
66static int activate(struct ftdev_event** chain, int id)
67{
68 struct ftdev_event* ev = kmalloc(sizeof(*ev), GFP_KERNEL);
69 if (ev) {
70 printk(KERN_INFO
71 "Enabling feather-trace event %d.\n", (int) id);
72 ft_enable_event(id);
73 ev->id = id;
74 ev->next = *chain;
75 *chain = ev;
76 }
77 return ev ? 0 : -ENOMEM;
78}
79
80static void deactivate(struct ftdev_event** chain, int id)
81{
82 struct ftdev_event **cur = chain;
83 struct ftdev_event *nxt;
84 while (*cur) {
85 if ((*cur)->id == id) {
86 nxt = (*cur)->next;
87 kfree(*cur);
88 *cur = nxt;
89 printk(KERN_INFO
90 "Disabling feather-trace event %d.\n", (int) id);
91 ft_disable_event(id);
92 break;
93 }
94 cur = &(*cur)->next;
95 }
96}
97
98static int ftdev_open(struct inode *in, struct file *filp)
99{
100 struct ftdev* ftdev;
101 struct ftdev_minor* ftdm;
102 unsigned int buf_idx = iminor(in);
103 int err = 0;
104
105 ftdev = container_of(in->i_cdev, struct ftdev, cdev);
106
107 if (buf_idx >= ftdev->minor_cnt) {
108 err = -ENODEV;
109 goto out;
110 }
111 if (ftdev->can_open && (err = ftdev->can_open(ftdev, buf_idx)))
112 goto out;
113
114 ftdm = ftdev->minor + buf_idx;
115 filp->private_data = ftdm;
116
117 if (mutex_lock_interruptible(&ftdm->lock)) {
118 err = -ERESTARTSYS;
119 goto out;
120 }
121
122 if (!ftdm->readers && ftdev->alloc)
123 err = ftdev->alloc(ftdev, buf_idx);
124 if (0 == err)
125 ftdm->readers++;
126
127 mutex_unlock(&ftdm->lock);
128out:
129 return err;
130}
131
132static int ftdev_release(struct inode *in, struct file *filp)
133{
134 struct ftdev* ftdev;
135 struct ftdev_minor* ftdm;
136 unsigned int buf_idx = iminor(in);
137 int err = 0;
138
139 ftdev = container_of(in->i_cdev, struct ftdev, cdev);
140
141 if (buf_idx >= ftdev->minor_cnt) {
142 err = -ENODEV;
143 goto out;
144 }
145 ftdm = ftdev->minor + buf_idx;
146
147 if (mutex_lock_interruptible(&ftdm->lock)) {
148 err = -ERESTARTSYS;
149 goto out;
150 }
151
152 if (ftdm->readers == 1) {
153 while (ftdm->events)
154 deactivate(&ftdm->events, ftdm->events->id);
155
156 /* wait for any pending events to complete */
157 set_current_state(TASK_UNINTERRUPTIBLE);
158 schedule_timeout(HZ);
159
160 printk(KERN_ALERT "Failed trace writes: %u\n",
161 ftdm->buf->failed_writes);
162
163 if (ftdev->free)
164 ftdev->free(ftdev, buf_idx);
165 }
166
167 ftdm->readers--;
168 mutex_unlock(&ftdm->lock);
169out:
170 return err;
171}
172
173/* based on ft_buffer_read
174 * @returns < 0 : page fault
175 * = 0 : no data available
176 * = 1 : one slot copied
177 */
178static int ft_buffer_copy_to_user(struct ft_buffer* buf, char __user *dest)
179{
180 unsigned int idx;
181 int err = 0;
182 if (buf->free_count != buf->slot_count) {
183 /* data available */
184 idx = buf->read_idx % buf->slot_count;
185 if (buf->slots[idx] == SLOT_READY) {
186 err = copy_to_user(dest, ((char*) buf->buffer_mem) +
187 idx * buf->slot_size,
188 buf->slot_size);
189 if (err == 0) {
190 /* copy ok */
191 buf->slots[idx] = SLOT_FREE;
192 buf->read_idx++;
193 fetch_and_inc(&buf->free_count);
194 err = 1;
195 }
196 }
197 }
198 return err;
199}
200
201static ssize_t ftdev_read(struct file *filp,
202 char __user *to, size_t len, loff_t *f_pos)
203{
204 /* we ignore f_pos, this is strictly sequential */
205
206 ssize_t err = 0;
207 size_t chunk;
208 int copied;
209 struct ftdev_minor* ftdm = filp->private_data;
210
211 if (mutex_lock_interruptible(&ftdm->lock)) {
212 err = -ERESTARTSYS;
213 goto out;
214 }
215
216
217 chunk = ftdm->buf->slot_size;
218 while (len >= chunk) {
219 copied = ft_buffer_copy_to_user(ftdm->buf, to);
220 if (copied == 1) {
221 len -= chunk;
222 to += chunk;
223 err += chunk;
224 } else if (err == 0 && copied == 0 && ftdm->events) {
225 /* Only wait if there are any events enabled and only
226 * if we haven't copied some data yet. We cannot wait
227 * here with copied data because that data would get
228 * lost if the task is interrupted (e.g., killed).
229 */
230 set_current_state(TASK_INTERRUPTIBLE);
231 schedule_timeout(50);
232 if (signal_pending(current)) {
233 if (err == 0)
234 /* nothing read yet, signal problem */
235 err = -ERESTARTSYS;
236 break;
237 }
238 } else if (copied < 0) {
239 /* page fault */
240 err = copied;
241 break;
242 } else
243 /* nothing left to get, return to user space */
244 break;
245 }
246 mutex_unlock(&ftdm->lock);
247out:
248 return err;
249}
250
251typedef uint32_t cmd_t;
252
253static ssize_t ftdev_write(struct file *filp, const char __user *from,
254 size_t len, loff_t *f_pos)
255{
256 struct ftdev_minor* ftdm = filp->private_data;
257 ssize_t err = -EINVAL;
258 cmd_t cmd;
259 cmd_t id;
260
261 if (len % sizeof(cmd) || len < 2 * sizeof(cmd))
262 goto out;
263
264 if (copy_from_user(&cmd, from, sizeof(cmd))) {
265 err = -EFAULT;
266 goto out;
267 }
268 len -= sizeof(cmd);
269 from += sizeof(cmd);
270
271 if (cmd != FTDEV_ENABLE_CMD && cmd != FTDEV_DISABLE_CMD)
272 goto out;
273
274 if (mutex_lock_interruptible(&ftdm->lock)) {
275 err = -ERESTARTSYS;
276 goto out;
277 }
278
279 err = sizeof(cmd);
280 while (len) {
281 if (copy_from_user(&id, from, sizeof(cmd))) {
282 err = -EFAULT;
283 goto out_unlock;
284 }
285 /* FIXME: check id against list of acceptable events */
286 len -= sizeof(cmd);
287 from += sizeof(cmd);
288 if (cmd == FTDEV_DISABLE_CMD)
289 deactivate(&ftdm->events, id);
290 else if (activate(&ftdm->events, id) != 0) {
291 err = -ENOMEM;
292 goto out_unlock;
293 }
294 err += sizeof(cmd);
295 }
296
297out_unlock:
298 mutex_unlock(&ftdm->lock);
299out:
300 return err;
301}
302
303struct file_operations ftdev_fops = {
304 .owner = THIS_MODULE,
305 .open = ftdev_open,
306 .release = ftdev_release,
307 .write = ftdev_write,
308 .read = ftdev_read,
309};
310
311
312void ftdev_init(struct ftdev* ftdev, struct module* owner)
313{
314 int i;
315 cdev_init(&ftdev->cdev, &ftdev_fops);
316 ftdev->cdev.owner = owner;
317 ftdev->cdev.ops = &ftdev_fops;
318 ftdev->minor_cnt = 0;
319 for (i = 0; i < MAX_FTDEV_MINORS; i++) {
320 mutex_init(&ftdev->minor[i].lock);
321 ftdev->minor[i].readers = 0;
322 ftdev->minor[i].buf = NULL;
323 ftdev->minor[i].events = NULL;
324 }
325 ftdev->alloc = NULL;
326 ftdev->free = NULL;
327 ftdev->can_open = NULL;
328}
329
330int register_ftdev(struct ftdev* ftdev, const char* name, int major)
331{
332 dev_t trace_dev;
333 int error = 0;
334
335 trace_dev = MKDEV(major, 0);
336 error = register_chrdev_region(trace_dev, ftdev->minor_cnt, name);
337 if (error)
338 {
339 printk(KERN_WARNING "ftdev(%s): "
340 "Could not register major/minor number %d/%u\n",
341 name, major, ftdev->minor_cnt);
342 return error;
343 }
344 error = cdev_add(&ftdev->cdev, trace_dev, ftdev->minor_cnt);
345 if (error) {
346 printk(KERN_WARNING "ftdev(%s): "
347 "Could not add cdev for major/minor = %d/%u.\n",
348 name, major, ftdev->minor_cnt);
349 return error;
350 }
351 return error;
352}