summaryrefslogtreecommitdiffstats
path: root/litmus/ftdev.c
diff options
context:
space:
mode:
authorNathan O <otternes@cs.unc.edu>2019-11-15 11:19:35 -0500
committerNathan O <otternes@cs.unc.edu>2019-11-15 11:19:35 -0500
commit2627f203874e04500ea80f6e588cd659bec5866b (patch)
treefeec07a6a87a24460a19808dcd88ba36ad03201d /litmus/ftdev.c
parentbf929479893052b1c7bfe23a4e7a903643076350 (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.c457
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
14struct 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
42void free_ft_buffer(struct ft_buffer* buf)
43{
44 if (buf) {
45 vfree(buf->buffer_mem);
46 kfree(buf);
47 }
48}
49
50struct ftdev_event {
51 int id;
52 struct ftdev_event* next;
53};
54
55static DEFINE_MUTEX(ft_event_activation_mutex);
56
57static 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
73static 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
93static 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);
124out:
125 return err;
126}
127
128static 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);
165out:
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 */
174static 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
197static 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);
261out:
262 return err;
263}
264
265static 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);
301out:
302 return err;
303}
304
305static 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
319struct 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
328int 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
372err_dealloc:
373 kfree(ftdev->minor);
374err_out:
375 return err;
376}
377
378/*
379 * Destroy minor devices up to, but not including, up_to.
380 */
381static 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
392void 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
402int 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
450err_minors:
451 ftdev_device_destroy(ftdev, minor_cntr);
452 cdev_del(&ftdev->cdev);
453err_unregister:
454 unregister_chrdev_region(MKDEV(ftdev->major, 0), ftdev->minor_cnt);
455err_out:
456 return err;
457}