aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern B. Brandenburg <bbb@cs.unc.edu>2008-09-17 13:29:04 -0400
committerBjoern B. Brandenburg <bbb@cs.unc.edu>2008-09-17 13:29:04 -0400
commitcc5954a935380af2cf0bec81386f10eb4b425a25 (patch)
tree34d092a173c81dfe9d74fcfc8d3a76557b439387
parent74784b9c59b5e41ebed5a9634b45ec154802a5fd (diff)
feather-trace: introduce ftdev device driver
This will help to redruce code duplication in the long run.
-rw-r--r--include/litmus/ftdev.h42
-rw-r--r--litmus/Makefile2
-rw-r--r--litmus/ftdev.c312
3 files changed, 355 insertions, 1 deletions
diff --git a/include/litmus/ftdev.h b/include/litmus/ftdev.h
new file mode 100644
index 0000000000..ac937fb733
--- /dev/null
+++ b/include/litmus/ftdev.h
@@ -0,0 +1,42 @@
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
16typedef int (*ftdev_alloc_t)(struct ftdev* dev, unsigned int buf_no);
17typedef void (*ftdev_free_t)(struct ftdev* dev, unsigned int buf_no);
18
19struct ftdev_minor {
20 struct ft_buffer* buf;
21 unsigned int readers;
22 struct mutex lock;
23 unsigned active_events;
24};
25
26struct ftdev {
27 struct cdev cdev;
28 /* FIXME: don't waste memory, allocate dynamically */
29 struct ftdev_minor minor[MAX_FTDEV_MINORS];
30 unsigned int minor_cnt;
31 /* FIXME: track enabled/disabled events */
32 ftdev_alloc_t alloc;
33 ftdev_free_t free;
34};
35
36struct ft_buffer* alloc_ft_buffer(unsigned int count, size_t size);
37void free_ft_buffer(struct ft_buffer* buf);
38
39void ftdev_init(struct ftdev* ftdev);
40int register_ftdev(struct ftdev* ftdev, const char* name, int major);
41
42#endif
diff --git a/litmus/Makefile b/litmus/Makefile
index 3c39899f93..2b2ab5be1f 100644
--- a/litmus/Makefile
+++ b/litmus/Makefile
@@ -11,5 +11,5 @@ obj-y = sched_plugin.o litmus.o \
11 sched_cedf.o \ 11 sched_cedf.o \
12 sched_pfair.o 12 sched_pfair.o
13 13
14obj-$(CONFIG_FEATHER_TRACE) += trace.o ft_event.o 14obj-$(CONFIG_FEATHER_TRACE) += trace.o ft_event.o ftdev.o
15obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o \ No newline at end of file 15obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o \ No newline at end of file
diff --git a/litmus/ftdev.c b/litmus/ftdev.c
new file mode 100644
index 0000000000..8e7d8e9dfe
--- /dev/null
+++ b/litmus/ftdev.c
@@ -0,0 +1,312 @@
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(struct ft_buffer), 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
61static int ftdev_open(struct inode *in, struct file *filp)
62{
63 struct ftdev* ftdev;
64 struct ftdev_minor* ftdm;
65 unsigned int buf_idx = iminor(in);
66 int err = 0;
67
68 ftdev = container_of(in->i_cdev, struct ftdev, cdev);
69
70 if (buf_idx >= ftdev->minor_cnt) {
71 err = -ENODEV;
72 goto out;
73 }
74 ftdm = ftdev->minor + buf_idx;
75 filp->private_data = ftdm;
76
77 if (mutex_lock_interruptible(&ftdm->lock)) {
78 err = -ERESTARTSYS;
79 goto out;
80 }
81
82 if (!ftdm->readers && ftdev->alloc)
83 err = ftdev->alloc(ftdev, buf_idx);
84 if (0 == err)
85 ftdm->readers++;
86
87 mutex_unlock(&ftdm->lock);
88out:
89 return err;
90}
91
92static int ftdev_release(struct inode *in, struct file *filp)
93{
94 struct ftdev* ftdev;
95 struct ftdev_minor* ftdm;
96 unsigned int buf_idx = iminor(in);
97 int err = 0;
98
99 ftdev = container_of(in->i_cdev, struct ftdev, cdev);
100
101 if (buf_idx >= ftdev->minor_cnt) {
102 err = -ENODEV;
103 goto out;
104 }
105 ftdm = ftdev->minor + buf_idx;
106
107 if (mutex_lock_interruptible(&ftdm->lock)) {
108 err = -ERESTARTSYS;
109 goto out;
110 }
111
112 if (ftdm->readers == 1) {
113 /*FIXME: disable events */
114 ftdm->active_events = 0;
115
116 /* wait for any pending events to complete */
117 set_current_state(TASK_UNINTERRUPTIBLE);
118 schedule_timeout(HZ);
119
120 printk(KERN_ALERT "Failed trace writes: %u\n",
121 ftdm->buf->failed_writes);
122
123 if (ftdev->free)
124 ftdev->free(ftdev, buf_idx);
125 }
126
127 ftdm->readers--;
128 mutex_unlock(&ftdm->lock);
129out:
130 return err;
131}
132
133/* based on ft_buffer_read
134 * @returns < 0 : page fault
135 * = 0 : no data available
136 * = 1 : one slot copied
137 */
138static int ft_buffer_copy_to_user(struct ft_buffer* buf, char __user *dest)
139{
140 unsigned int idx;
141 int err = 0;
142 if (buf->free_count != buf->slot_count) {
143 /* data available */
144 idx = buf->read_idx % buf->slot_count;
145 if (buf->slots[idx] == SLOT_READY) {
146 err = copy_to_user(dest, ((char*) buf->buffer_mem) +
147 idx * buf->slot_size,
148 buf->slot_size);
149 if (err == 0) {
150 /* copy ok */
151 buf->slots[idx] = SLOT_FREE;
152 buf->read_idx++;
153 fetch_and_inc(&buf->free_count);
154 err = 1;
155 }
156 }
157 }
158 return err;
159}
160
161static ssize_t ftdev_read(struct file *filp,
162 char __user *to, size_t len, loff_t *f_pos)
163{
164 /* we ignore f_pos, this is strictly sequential */
165
166 ssize_t err = 0;
167 size_t chunk;
168 int copied;
169 struct ftdev_minor* ftdm = filp->private_data;
170
171 if (mutex_lock_interruptible(&ftdm->lock)) {
172 err = -ERESTARTSYS;
173 goto out;
174 }
175
176
177 chunk = ftdm->buf->slot_size;
178 while (len >= chunk) {
179 copied = ft_buffer_copy_to_user(ftdm->buf, to);
180 if (copied == 1) {
181 len -= chunk;
182 to += chunk;
183 err += chunk;
184 } else if (copied == 0 && ftdm->active_events) {
185 /* only wait if there are any events enabled */
186 set_current_state(TASK_INTERRUPTIBLE);
187 schedule_timeout(50);
188 if (signal_pending(current)) {
189 if (err == 0)
190 /* nothing read yet, signal problem */
191 err = -ERESTARTSYS;
192 break;
193 }
194 } else if (copied < 0) {
195 /* page fault */
196 err = copied;
197 break;
198 } else
199 /* nothing left to get, return to user space */
200 break;
201 }
202 mutex_unlock(&ftdm->lock);
203out:
204 return err;
205}
206
207typedef uint32_t cmd_t;
208
209static ssize_t ftdev_write(struct file *filp, const char __user *from,
210 size_t len, loff_t *f_pos)
211{
212 struct ftdev_minor* ftdm = filp->private_data;
213 ssize_t err = -EINVAL;
214 cmd_t cmd;
215 cmd_t id;
216
217 if (len % sizeof(cmd_t) || len < 2 * sizeof(cmd_t))
218 goto out;
219
220 if (copy_from_user(&cmd, from, sizeof(cmd_t))) {
221 err = -EFAULT;
222 goto out;
223 }
224 len -= sizeof(cmd_t);
225 from += sizeof(cmd_t);
226
227 if (cmd != FTDEV_ENABLE_CMD && cmd != FTDEV_DISABLE_CMD)
228 goto out;
229
230 if (mutex_lock_interruptible(&ftdm->lock)) {
231 err = -ERESTARTSYS;
232 goto out;
233 }
234
235 err = sizeof(cmd_t);
236 while (len) {
237 if (copy_from_user(&id, from, sizeof(cmd_t))) {
238 err = -EFAULT;
239 goto out_unlock;
240 }
241 /* FIXME: check id against list of acceptable events */
242 /* FIXME: track which events must be disable at release time */
243 len -= sizeof(cmd_t);
244 from += sizeof(cmd_t);
245 if (cmd == FTDEV_ENABLE_CMD) {
246 printk(KERN_INFO
247 "Disabling feather-trace event %d.\n", (int) id);
248 ft_disable_event(id);
249 ftdm->active_events--;
250 } else {
251 printk(KERN_INFO
252 "Enabling feather-trace event %d.\n", (int) id);
253 ft_enable_event(id);
254 ftdm->active_events++;
255 }
256 err += sizeof(cmd_t);
257 }
258
259out_unlock:
260 mutex_unlock(&ftdm->lock);
261out:
262 return err;
263}
264
265struct file_operations ftdev_fops = {
266 .owner = THIS_MODULE,
267 .open = ftdev_open,
268 .release = ftdev_release,
269 .write = ftdev_write,
270 .read = ftdev_read,
271};
272
273
274void ftdev_init(struct ftdev* ftdev)
275{
276 int i;
277 ftdev->cdev.owner = THIS_MODULE;
278 ftdev->cdev.ops = &ftdev_fops;
279 ftdev->minor_cnt = 0;
280 for (i = 0; i < MAX_FTDEV_MINORS; i++) {
281 mutex_init(&ftdev->minor[i].lock);
282 ftdev->minor[i].readers = 0;
283 ftdev->minor[i].buf = NULL;
284 ftdev->minor[i].active_events = 0;
285 }
286 ftdev->alloc = NULL;
287 ftdev->free = NULL;
288}
289
290int register_ftdev(struct ftdev* ftdev, const char* name, int major)
291{
292 dev_t trace_dev;
293 int error = 0;
294
295 trace_dev = MKDEV(major, 0);
296 error = register_chrdev_region(trace_dev, ftdev->minor_cnt, name);
297 if (error)
298 {
299 printk(KERN_WARNING "ftdev(%s): "
300 "Could not register major/minor number %d/%u\n",
301 name, major, ftdev->minor_cnt);
302 return error;
303 }
304 error = cdev_add(&ftdev->cdev, trace_dev, ftdev->minor_cnt);
305 if (error) {
306 printk(KERN_WARNING "ftdev(%s): "
307 "Could not add cdev for major/minor = %d/%u.\n",
308 name, major, ftdev->minor_cnt);
309 return error;
310 }
311 return error;
312}