diff options
Diffstat (limited to 'litmus/ctrldev.c')
-rw-r--r-- | litmus/ctrldev.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/litmus/ctrldev.c b/litmus/ctrldev.c new file mode 100644 index 000000000000..877f2786b4c8 --- /dev/null +++ b/litmus/ctrldev.c | |||
@@ -0,0 +1,160 @@ | |||
1 | #include <linux/sched.h> | ||
2 | #include <linux/mm.h> | ||
3 | #include <linux/fs.h> | ||
4 | #include <linux/miscdevice.h> | ||
5 | #include <linux/module.h> | ||
6 | |||
7 | #include <litmus/litmus.h> | ||
8 | |||
9 | /* only one page for now, but we might want to add a RO version at some point */ | ||
10 | |||
11 | #define CTRL_NAME "litmus/ctrl" | ||
12 | |||
13 | /* allocate t->rt_param.ctrl_page*/ | ||
14 | static int alloc_ctrl_page(struct task_struct *t) | ||
15 | { | ||
16 | int err = 0; | ||
17 | |||
18 | /* only allocate if the task doesn't have one yet */ | ||
19 | if (!tsk_rt(t)->ctrl_page) { | ||
20 | tsk_rt(t)->ctrl_page = (void*) get_zeroed_page(GFP_KERNEL); | ||
21 | if (!tsk_rt(t)->ctrl_page) | ||
22 | err = -ENOMEM; | ||
23 | /* will get de-allocated in task teardown */ | ||
24 | TRACE_TASK(t, "%s ctrl_page = %p\n", __FUNCTION__, | ||
25 | tsk_rt(t)->ctrl_page); | ||
26 | } | ||
27 | return err; | ||
28 | } | ||
29 | |||
30 | static int map_ctrl_page(struct task_struct *t, struct vm_area_struct* vma) | ||
31 | { | ||
32 | int err; | ||
33 | |||
34 | struct page* ctrl = virt_to_page(tsk_rt(t)->ctrl_page); | ||
35 | |||
36 | TRACE_CUR(CTRL_NAME | ||
37 | ": mapping %p (pfn:%lx) to 0x%lx (prot:%lx)\n", | ||
38 | tsk_rt(t)->ctrl_page,page_to_pfn(ctrl), vma->vm_start, | ||
39 | vma->vm_page_prot); | ||
40 | |||
41 | /* Map it into the vma. */ | ||
42 | err = vm_insert_page(vma, vma->vm_start, ctrl); | ||
43 | |||
44 | if (err) | ||
45 | TRACE_CUR(CTRL_NAME ": vm_insert_page() failed (%d)\n", err); | ||
46 | |||
47 | return err; | ||
48 | } | ||
49 | |||
50 | static void litmus_ctrl_vm_close(struct vm_area_struct* vma) | ||
51 | { | ||
52 | TRACE_CUR("%s flags=0x%x prot=0x%x\n", __FUNCTION__, | ||
53 | vma->vm_flags, vma->vm_page_prot); | ||
54 | |||
55 | TRACE_CUR(CTRL_NAME | ||
56 | ": %p:%p vma:%p vma->vm_private_data:%p closed.\n", | ||
57 | (void*) vma->vm_start, (void*) vma->vm_end, vma, | ||
58 | vma->vm_private_data); | ||
59 | } | ||
60 | |||
61 | static int litmus_ctrl_vm_fault(struct vm_area_struct* vma, | ||
62 | struct vm_fault* vmf) | ||
63 | { | ||
64 | TRACE_CUR("%s flags=0x%x (off:%ld)\n", __FUNCTION__, | ||
65 | vma->vm_flags, vmf->pgoff); | ||
66 | |||
67 | /* This function should never be called, since all pages should have | ||
68 | * been mapped by mmap() already. */ | ||
69 | WARN_ONCE(1, "Page faults should be impossible in the control page\n"); | ||
70 | |||
71 | return VM_FAULT_SIGBUS; | ||
72 | } | ||
73 | |||
74 | static struct vm_operations_struct litmus_ctrl_vm_ops = { | ||
75 | .close = litmus_ctrl_vm_close, | ||
76 | .fault = litmus_ctrl_vm_fault, | ||
77 | }; | ||
78 | |||
79 | static int litmus_ctrl_mmap(struct file* filp, struct vm_area_struct* vma) | ||
80 | { | ||
81 | int err = 0; | ||
82 | |||
83 | /* first make sure mapper knows what he's doing */ | ||
84 | |||
85 | /* you can only get one page */ | ||
86 | if (vma->vm_end - vma->vm_start != PAGE_SIZE) | ||
87 | return -EINVAL; | ||
88 | |||
89 | /* you can only map the "first" page */ | ||
90 | if (vma->vm_pgoff != 0) | ||
91 | return -EINVAL; | ||
92 | |||
93 | /* you can't share it with anyone */ | ||
94 | if (vma->vm_flags & (VM_MAYSHARE | VM_SHARED)) | ||
95 | return -EINVAL; | ||
96 | |||
97 | vma->vm_ops = &litmus_ctrl_vm_ops; | ||
98 | /* This mapping should not be kept across forks, | ||
99 | * cannot be expanded, and is not a "normal" page. */ | ||
100 | vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_READ | VM_WRITE; | ||
101 | |||
102 | /* We don't want the first write access to trigger a "minor" page fault | ||
103 | * to mark the page as dirty. This is transient, private memory, we | ||
104 | * don't care if it was touched or not. PAGE_SHARED means RW access, but | ||
105 | * not execute, and avoids copy-on-write behavior. | ||
106 | * See protection_map in mmap.c. */ | ||
107 | vma->vm_page_prot = PAGE_SHARED; | ||
108 | |||
109 | err = alloc_ctrl_page(current); | ||
110 | if (!err) | ||
111 | err = map_ctrl_page(current, vma); | ||
112 | |||
113 | TRACE_CUR("%s flags=0x%x prot=0x%lx\n", | ||
114 | __FUNCTION__, vma->vm_flags, vma->vm_page_prot); | ||
115 | |||
116 | return err; | ||
117 | } | ||
118 | |||
119 | static struct file_operations litmus_ctrl_fops = { | ||
120 | .owner = THIS_MODULE, | ||
121 | .mmap = litmus_ctrl_mmap, | ||
122 | }; | ||
123 | |||
124 | static struct miscdevice litmus_ctrl_dev = { | ||
125 | .name = CTRL_NAME, | ||
126 | .minor = MISC_DYNAMIC_MINOR, | ||
127 | .fops = &litmus_ctrl_fops, | ||
128 | }; | ||
129 | |||
130 | static int __init init_litmus_ctrl_dev(void) | ||
131 | { | ||
132 | int err; | ||
133 | |||
134 | BUILD_BUG_ON(sizeof(struct control_page) > PAGE_SIZE); | ||
135 | |||
136 | BUILD_BUG_ON(sizeof(union np_flag) != sizeof(uint32_t)); | ||
137 | |||
138 | BUILD_BUG_ON(offsetof(struct control_page, sched.raw) | ||
139 | != LITMUS_CP_OFFSET_SCHED); | ||
140 | BUILD_BUG_ON(offsetof(struct control_page, irq_count) | ||
141 | != LITMUS_CP_OFFSET_IRQ_COUNT); | ||
142 | BUILD_BUG_ON(offsetof(struct control_page, ts_syscall_start) | ||
143 | != LITMUS_CP_OFFSET_TS_SC_START); | ||
144 | BUILD_BUG_ON(offsetof(struct control_page, irq_syscall_start) | ||
145 | != LITMUS_CP_OFFSET_IRQ_SC_START); | ||
146 | |||
147 | printk("Initializing LITMUS^RT control device.\n"); | ||
148 | err = misc_register(&litmus_ctrl_dev); | ||
149 | if (err) | ||
150 | printk("Could not allocate %s device (%d).\n", CTRL_NAME, err); | ||
151 | return err; | ||
152 | } | ||
153 | |||
154 | static void __exit exit_litmus_ctrl_dev(void) | ||
155 | { | ||
156 | misc_deregister(&litmus_ctrl_dev); | ||
157 | } | ||
158 | |||
159 | module_init(init_litmus_ctrl_dev); | ||
160 | module_exit(exit_litmus_ctrl_dev); | ||