diff options
author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2013-06-25 01:27:07 -0400 |
---|---|---|
committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2013-08-07 03:46:49 -0400 |
commit | 543810eb67bea9c3046ecb58388493bca39fe796 (patch) | |
tree | cf65010367e53dfbd3e39a9eb6e89dacf92348f3 /litmus/litmus_proc.c | |
parent | 1412c8b72e192a14b8dd620f58a75f55a5490783 (diff) |
Add LITMUS^RT core implementation
This patch adds the core of LITMUS^RT:
- library functionality (heaps, rt_domain, prioritization, etc.)
- budget enforcement logic
- job management
- system call backends
- virtual devices (control page, etc.)
- scheduler plugin API (and dummy plugin)
This code compiles, but is not yet integrated with the rest of Linux.
Diffstat (limited to 'litmus/litmus_proc.c')
-rw-r--r-- | litmus/litmus_proc.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/litmus/litmus_proc.c b/litmus/litmus_proc.c new file mode 100644 index 000000000000..1ebf1277f5d3 --- /dev/null +++ b/litmus/litmus_proc.c | |||
@@ -0,0 +1,407 @@ | |||
1 | /* | ||
2 | * litmus_proc.c -- Implementation of the /proc/litmus directory tree. | ||
3 | */ | ||
4 | |||
5 | #include <linux/sched.h> | ||
6 | #include <linux/uaccess.h> | ||
7 | #include <linux/seq_file.h> | ||
8 | |||
9 | #include <litmus/litmus.h> | ||
10 | #include <litmus/litmus_proc.h> | ||
11 | |||
12 | #include <litmus/clustered.h> | ||
13 | |||
14 | /* in litmus/litmus.c */ | ||
15 | extern atomic_t rt_task_count; | ||
16 | |||
17 | static struct proc_dir_entry *litmus_dir = NULL, | ||
18 | *curr_file = NULL, | ||
19 | *stat_file = NULL, | ||
20 | *plugs_dir = NULL, | ||
21 | #ifdef CONFIG_RELEASE_MASTER | ||
22 | *release_master_file = NULL, | ||
23 | #endif | ||
24 | *plugs_file = NULL; | ||
25 | |||
26 | /* in litmus/sync.c */ | ||
27 | int count_tasks_waiting_for_release(void); | ||
28 | |||
29 | static int litmus_stats_proc_show(struct seq_file *m, void *v) | ||
30 | { | ||
31 | seq_printf(m, | ||
32 | "real-time tasks = %d\n" | ||
33 | "ready for release = %d\n", | ||
34 | atomic_read(&rt_task_count), | ||
35 | count_tasks_waiting_for_release()); | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static int litmus_stats_proc_open(struct inode *inode, struct file *file) | ||
40 | { | ||
41 | return single_open(file, litmus_stats_proc_show, PDE_DATA(inode)); | ||
42 | } | ||
43 | |||
44 | static const struct file_operations litmus_stats_proc_fops = { | ||
45 | .open = litmus_stats_proc_open, | ||
46 | .read = seq_read, | ||
47 | .llseek = seq_lseek, | ||
48 | .release = single_release, | ||
49 | }; | ||
50 | |||
51 | |||
52 | static int litmus_loaded_proc_show(struct seq_file *m, void *v) | ||
53 | { | ||
54 | print_sched_plugins(m); | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int litmus_loaded_proc_open(struct inode *inode, struct file *file) | ||
59 | { | ||
60 | return single_open(file, litmus_loaded_proc_show, PDE_DATA(inode)); | ||
61 | } | ||
62 | |||
63 | static const struct file_operations litmus_loaded_proc_fops = { | ||
64 | .open = litmus_loaded_proc_open, | ||
65 | .read = seq_read, | ||
66 | .llseek = seq_lseek, | ||
67 | .release = single_release, | ||
68 | }; | ||
69 | |||
70 | |||
71 | |||
72 | |||
73 | /* in litmus/litmus.c */ | ||
74 | int switch_sched_plugin(struct sched_plugin*); | ||
75 | |||
76 | static ssize_t litmus_active_proc_write(struct file *file, | ||
77 | const char __user *buffer, size_t count, | ||
78 | loff_t *ppos) | ||
79 | { | ||
80 | char name[65]; | ||
81 | struct sched_plugin* found; | ||
82 | ssize_t ret = -EINVAL; | ||
83 | int err; | ||
84 | |||
85 | |||
86 | ret = copy_and_chomp(name, sizeof(name), buffer, count); | ||
87 | if (ret < 0) | ||
88 | return ret; | ||
89 | |||
90 | found = find_sched_plugin(name); | ||
91 | |||
92 | if (found) { | ||
93 | err = switch_sched_plugin(found); | ||
94 | if (err) { | ||
95 | printk(KERN_INFO "Could not switch plugin: %d\n", err); | ||
96 | ret = err; | ||
97 | } | ||
98 | } else { | ||
99 | printk(KERN_INFO "Plugin '%s' is unknown.\n", name); | ||
100 | ret = -ESRCH; | ||
101 | } | ||
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | static int litmus_active_proc_show(struct seq_file *m, void *v) | ||
107 | { | ||
108 | seq_printf(m, "%s\n", litmus->plugin_name); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int litmus_active_proc_open(struct inode *inode, struct file *file) | ||
113 | { | ||
114 | return single_open(file, litmus_active_proc_show, PDE_DATA(inode)); | ||
115 | } | ||
116 | |||
117 | static const struct file_operations litmus_active_proc_fops = { | ||
118 | .open = litmus_active_proc_open, | ||
119 | .read = seq_read, | ||
120 | .llseek = seq_lseek, | ||
121 | .release = single_release, | ||
122 | .write = litmus_active_proc_write, | ||
123 | }; | ||
124 | |||
125 | |||
126 | #ifdef CONFIG_RELEASE_MASTER | ||
127 | static ssize_t litmus_release_master_proc_write( | ||
128 | struct file *file, | ||
129 | const char __user *buffer, size_t count, | ||
130 | loff_t *ppos) | ||
131 | { | ||
132 | int cpu, err, online = 0; | ||
133 | char msg[64]; | ||
134 | ssize_t len; | ||
135 | |||
136 | len = copy_and_chomp(msg, sizeof(msg), buffer, count); | ||
137 | |||
138 | if (len < 0) | ||
139 | return len; | ||
140 | |||
141 | if (strcmp(msg, "NO_CPU") == 0) | ||
142 | atomic_set(&release_master_cpu, NO_CPU); | ||
143 | else { | ||
144 | err = sscanf(msg, "%d", &cpu); | ||
145 | if (err == 1 && cpu >= 0 && (online = cpu_online(cpu))) { | ||
146 | atomic_set(&release_master_cpu, cpu); | ||
147 | } else { | ||
148 | TRACE("invalid release master: '%s' " | ||
149 | "(err:%d cpu:%d online:%d)\n", | ||
150 | msg, err, cpu, online); | ||
151 | len = -EINVAL; | ||
152 | } | ||
153 | } | ||
154 | return len; | ||
155 | } | ||
156 | |||
157 | static int litmus_release_master_proc_show(struct seq_file *m, void *v) | ||
158 | { | ||
159 | int master; | ||
160 | master = atomic_read(&release_master_cpu); | ||
161 | if (master == NO_CPU) | ||
162 | seq_printf(m, "NO_CPU\n"); | ||
163 | else | ||
164 | seq_printf(m, "%d\n", master); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int litmus_release_master_proc_open(struct inode *inode, struct file *file) | ||
169 | { | ||
170 | return single_open(file, litmus_release_master_proc_show, PDE_DATA(inode)); | ||
171 | } | ||
172 | |||
173 | static const struct file_operations litmus_release_master_proc_fops = { | ||
174 | .open = litmus_release_master_proc_open, | ||
175 | .read = seq_read, | ||
176 | .llseek = seq_lseek, | ||
177 | .release = single_release, | ||
178 | .write = litmus_release_master_proc_write, | ||
179 | }; | ||
180 | #endif | ||
181 | |||
182 | int __init init_litmus_proc(void) | ||
183 | { | ||
184 | litmus_dir = proc_mkdir("litmus", NULL); | ||
185 | if (!litmus_dir) { | ||
186 | printk(KERN_ERR "Could not allocate LITMUS^RT procfs entry.\n"); | ||
187 | return -ENOMEM; | ||
188 | } | ||
189 | |||
190 | curr_file = proc_create("active_plugin", 0644, litmus_dir, | ||
191 | &litmus_active_proc_fops); | ||
192 | |||
193 | if (!curr_file) { | ||
194 | printk(KERN_ERR "Could not allocate active_plugin " | ||
195 | "procfs entry.\n"); | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | |||
199 | #ifdef CONFIG_RELEASE_MASTER | ||
200 | release_master_file = proc_create("release_master", 0644, litmus_dir, | ||
201 | &litmus_release_master_proc_fops); | ||
202 | if (!release_master_file) { | ||
203 | printk(KERN_ERR "Could not allocate release_master " | ||
204 | "procfs entry.\n"); | ||
205 | return -ENOMEM; | ||
206 | } | ||
207 | #endif | ||
208 | |||
209 | stat_file = proc_create("stats", 0444, litmus_dir, &litmus_stats_proc_fops); | ||
210 | |||
211 | plugs_dir = proc_mkdir("plugins", litmus_dir); | ||
212 | if (!plugs_dir){ | ||
213 | printk(KERN_ERR "Could not allocate plugins directory " | ||
214 | "procfs entry.\n"); | ||
215 | return -ENOMEM; | ||
216 | } | ||
217 | |||
218 | plugs_file = proc_create("loaded", 0444, plugs_dir, | ||
219 | &litmus_loaded_proc_fops); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | void exit_litmus_proc(void) | ||
225 | { | ||
226 | if (plugs_file) | ||
227 | remove_proc_entry("loaded", plugs_dir); | ||
228 | if (plugs_dir) | ||
229 | remove_proc_entry("plugins", litmus_dir); | ||
230 | if (stat_file) | ||
231 | remove_proc_entry("stats", litmus_dir); | ||
232 | if (curr_file) | ||
233 | remove_proc_entry("active_plugin", litmus_dir); | ||
234 | #ifdef CONFIG_RELEASE_MASTER | ||
235 | if (release_master_file) | ||
236 | remove_proc_entry("release_master", litmus_dir); | ||
237 | #endif | ||
238 | if (litmus_dir) | ||
239 | remove_proc_entry("litmus", NULL); | ||
240 | } | ||
241 | |||
242 | long make_plugin_proc_dir(struct sched_plugin* plugin, | ||
243 | struct proc_dir_entry** pde_in) | ||
244 | { | ||
245 | struct proc_dir_entry *pde_new = NULL; | ||
246 | long rv; | ||
247 | |||
248 | if (!plugin || !plugin->plugin_name){ | ||
249 | printk(KERN_ERR "Invalid plugin struct passed to %s.\n", | ||
250 | __func__); | ||
251 | rv = -EINVAL; | ||
252 | goto out_no_pde; | ||
253 | } | ||
254 | |||
255 | if (!plugs_dir){ | ||
256 | printk(KERN_ERR "Could not make plugin sub-directory, because " | ||
257 | "/proc/litmus/plugins does not exist.\n"); | ||
258 | rv = -ENOENT; | ||
259 | goto out_no_pde; | ||
260 | } | ||
261 | |||
262 | pde_new = proc_mkdir(plugin->plugin_name, plugs_dir); | ||
263 | if (!pde_new){ | ||
264 | printk(KERN_ERR "Could not make plugin sub-directory: " | ||
265 | "out of memory?.\n"); | ||
266 | rv = -ENOMEM; | ||
267 | goto out_no_pde; | ||
268 | } | ||
269 | |||
270 | rv = 0; | ||
271 | *pde_in = pde_new; | ||
272 | goto out_ok; | ||
273 | |||
274 | out_no_pde: | ||
275 | *pde_in = NULL; | ||
276 | out_ok: | ||
277 | return rv; | ||
278 | } | ||
279 | |||
280 | void remove_plugin_proc_dir(struct sched_plugin* plugin) | ||
281 | { | ||
282 | if (!plugin || !plugin->plugin_name){ | ||
283 | printk(KERN_ERR "Invalid plugin struct passed to %s.\n", | ||
284 | __func__); | ||
285 | return; | ||
286 | } | ||
287 | remove_proc_entry(plugin->plugin_name, plugs_dir); | ||
288 | } | ||
289 | |||
290 | |||
291 | |||
292 | /* misc. I/O helper functions */ | ||
293 | |||
294 | int copy_and_chomp(char *kbuf, unsigned long ksize, | ||
295 | __user const char* ubuf, unsigned long ulength) | ||
296 | { | ||
297 | /* caller must provide buffer space */ | ||
298 | BUG_ON(!ksize); | ||
299 | |||
300 | ksize--; /* leave space for null byte */ | ||
301 | |||
302 | if (ksize > ulength) | ||
303 | ksize = ulength; | ||
304 | |||
305 | if(copy_from_user(kbuf, ubuf, ksize)) | ||
306 | return -EFAULT; | ||
307 | |||
308 | kbuf[ksize] = '\0'; | ||
309 | |||
310 | /* chomp kbuf */ | ||
311 | if (ksize > 0 && kbuf[ksize - 1] == '\n') | ||
312 | kbuf[ksize - 1] = '\0'; | ||
313 | |||
314 | return ksize; | ||
315 | } | ||
316 | |||
317 | /* helper functions for clustered plugins */ | ||
318 | static const char* cache_level_names[] = { | ||
319 | "ALL", | ||
320 | "L1", | ||
321 | "L2", | ||
322 | "L3", | ||
323 | }; | ||
324 | |||
325 | int parse_cache_level(const char *cache_name, enum cache_level *level) | ||
326 | { | ||
327 | int err = -EINVAL; | ||
328 | int i; | ||
329 | /* do a quick and dirty comparison to find the cluster size */ | ||
330 | for (i = GLOBAL_CLUSTER; i <= L3_CLUSTER; i++) | ||
331 | if (!strcmp(cache_name, cache_level_names[i])) { | ||
332 | *level = (enum cache_level) i; | ||
333 | err = 0; | ||
334 | break; | ||
335 | } | ||
336 | return err; | ||
337 | } | ||
338 | |||
339 | const char* cache_level_name(enum cache_level level) | ||
340 | { | ||
341 | int idx = level; | ||
342 | |||
343 | if (idx >= GLOBAL_CLUSTER && idx <= L3_CLUSTER) | ||
344 | return cache_level_names[idx]; | ||
345 | else | ||
346 | return "INVALID"; | ||
347 | } | ||
348 | |||
349 | |||
350 | |||
351 | |||
352 | /* proc file interface to configure the cluster size */ | ||
353 | |||
354 | static ssize_t litmus_cluster_proc_write(struct file *file, | ||
355 | const char __user *buffer, size_t count, | ||
356 | loff_t *ppos) | ||
357 | { | ||
358 | enum cache_level *level = (enum cache_level *) PDE_DATA(file_inode(file)); | ||
359 | ssize_t len; | ||
360 | char cache_name[8]; | ||
361 | |||
362 | len = copy_and_chomp(cache_name, sizeof(cache_name), buffer, count); | ||
363 | |||
364 | if (len > 0 && parse_cache_level(cache_name, level)) { | ||
365 | printk(KERN_INFO "Cluster '%s' is unknown.\n", cache_name); | ||
366 | len = -EINVAL; | ||
367 | } | ||
368 | |||
369 | return len; | ||
370 | } | ||
371 | |||
372 | static int litmus_cluster_proc_show(struct seq_file *m, void *v) | ||
373 | { | ||
374 | enum cache_level *level = (enum cache_level *) m->private; | ||
375 | |||
376 | seq_printf(m, "%s\n", cache_level_name(*level)); | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int litmus_cluster_proc_open(struct inode *inode, struct file *file) | ||
381 | { | ||
382 | return single_open(file, litmus_cluster_proc_show, PDE_DATA(inode)); | ||
383 | } | ||
384 | |||
385 | static const struct file_operations litmus_cluster_proc_fops = { | ||
386 | .open = litmus_cluster_proc_open, | ||
387 | .read = seq_read, | ||
388 | .llseek = seq_lseek, | ||
389 | .release = single_release, | ||
390 | .write = litmus_cluster_proc_write, | ||
391 | }; | ||
392 | |||
393 | struct proc_dir_entry* create_cluster_file(struct proc_dir_entry* parent, | ||
394 | enum cache_level* level) | ||
395 | { | ||
396 | struct proc_dir_entry* cluster_file; | ||
397 | |||
398 | |||
399 | cluster_file = proc_create_data("cluster", 0644, parent, | ||
400 | &litmus_cluster_proc_fops, | ||
401 | (void *) level); | ||
402 | if (!cluster_file) { | ||
403 | printk(KERN_ERR | ||
404 | "Could not cluster procfs entry.\n"); | ||
405 | } | ||
406 | return cluster_file; | ||
407 | } | ||