diff options
author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2008-09-17 13:29:04 -0400 |
---|---|---|
committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2008-09-17 13:29:04 -0400 |
commit | cc5954a935380af2cf0bec81386f10eb4b425a25 (patch) | |
tree | 34d092a173c81dfe9d74fcfc8d3a76557b439387 | |
parent | 74784b9c59b5e41ebed5a9634b45ec154802a5fd (diff) |
feather-trace: introduce ftdev device driver
This will help to redruce code duplication in the long run.
-rw-r--r-- | include/litmus/ftdev.h | 42 | ||||
-rw-r--r-- | litmus/Makefile | 2 | ||||
-rw-r--r-- | litmus/ftdev.c | 312 |
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 | |||
14 | struct ftdev; | ||
15 | |||
16 | typedef int (*ftdev_alloc_t)(struct ftdev* dev, unsigned int buf_no); | ||
17 | typedef void (*ftdev_free_t)(struct ftdev* dev, unsigned int buf_no); | ||
18 | |||
19 | struct ftdev_minor { | ||
20 | struct ft_buffer* buf; | ||
21 | unsigned int readers; | ||
22 | struct mutex lock; | ||
23 | unsigned active_events; | ||
24 | }; | ||
25 | |||
26 | struct 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 | |||
36 | struct ft_buffer* alloc_ft_buffer(unsigned int count, size_t size); | ||
37 | void free_ft_buffer(struct ft_buffer* buf); | ||
38 | |||
39 | void ftdev_init(struct ftdev* ftdev); | ||
40 | int 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 | ||
14 | obj-$(CONFIG_FEATHER_TRACE) += trace.o ft_event.o | 14 | obj-$(CONFIG_FEATHER_TRACE) += trace.o ft_event.o ftdev.o |
15 | obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o \ No newline at end of file | 15 | obj-$(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 | |||
11 | struct 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 | |||
44 | void 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 | |||
61 | static 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); | ||
88 | out: | ||
89 | return err; | ||
90 | } | ||
91 | |||
92 | static 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); | ||
129 | out: | ||
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 | */ | ||
138 | static 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 | |||
161 | static 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); | ||
203 | out: | ||
204 | return err; | ||
205 | } | ||
206 | |||
207 | typedef uint32_t cmd_t; | ||
208 | |||
209 | static 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 | |||
259 | out_unlock: | ||
260 | mutex_unlock(&ftdm->lock); | ||
261 | out: | ||
262 | return err; | ||
263 | } | ||
264 | |||
265 | struct 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 | |||
274 | void 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 | |||
290 | int 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 | } | ||