diff options
Diffstat (limited to 'kernel/resource.c')
-rw-r--r-- | kernel/resource.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/kernel/resource.c b/kernel/resource.c new file mode 100644 index 000000000000..35c99ac02c7c --- /dev/null +++ b/kernel/resource.c | |||
@@ -0,0 +1,551 @@ | |||
1 | /* | ||
2 | * linux/kernel/resource.c | ||
3 | * | ||
4 | * Copyright (C) 1999 Linus Torvalds | ||
5 | * Copyright (C) 1999 Martin Mares <mj@ucw.cz> | ||
6 | * | ||
7 | * Arbitrary resource management. | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/ioport.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/proc_fs.h> | ||
20 | #include <linux/seq_file.h> | ||
21 | #include <asm/io.h> | ||
22 | |||
23 | |||
24 | struct resource ioport_resource = { | ||
25 | .name = "PCI IO", | ||
26 | .start = 0x0000, | ||
27 | .end = IO_SPACE_LIMIT, | ||
28 | .flags = IORESOURCE_IO, | ||
29 | }; | ||
30 | |||
31 | EXPORT_SYMBOL(ioport_resource); | ||
32 | |||
33 | struct resource iomem_resource = { | ||
34 | .name = "PCI mem", | ||
35 | .start = 0UL, | ||
36 | .end = ~0UL, | ||
37 | .flags = IORESOURCE_MEM, | ||
38 | }; | ||
39 | |||
40 | EXPORT_SYMBOL(iomem_resource); | ||
41 | |||
42 | static DEFINE_RWLOCK(resource_lock); | ||
43 | |||
44 | #ifdef CONFIG_PROC_FS | ||
45 | |||
46 | enum { MAX_IORES_LEVEL = 5 }; | ||
47 | |||
48 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) | ||
49 | { | ||
50 | struct resource *p = v; | ||
51 | (*pos)++; | ||
52 | if (p->child) | ||
53 | return p->child; | ||
54 | while (!p->sibling && p->parent) | ||
55 | p = p->parent; | ||
56 | return p->sibling; | ||
57 | } | ||
58 | |||
59 | static void *r_start(struct seq_file *m, loff_t *pos) | ||
60 | __acquires(resource_lock) | ||
61 | { | ||
62 | struct resource *p = m->private; | ||
63 | loff_t l = 0; | ||
64 | read_lock(&resource_lock); | ||
65 | for (p = p->child; p && l < *pos; p = r_next(m, p, &l)) | ||
66 | ; | ||
67 | return p; | ||
68 | } | ||
69 | |||
70 | static void r_stop(struct seq_file *m, void *v) | ||
71 | __releases(resource_lock) | ||
72 | { | ||
73 | read_unlock(&resource_lock); | ||
74 | } | ||
75 | |||
76 | static int r_show(struct seq_file *m, void *v) | ||
77 | { | ||
78 | struct resource *root = m->private; | ||
79 | struct resource *r = v, *p; | ||
80 | int width = root->end < 0x10000 ? 4 : 8; | ||
81 | int depth; | ||
82 | |||
83 | for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent) | ||
84 | if (p->parent == root) | ||
85 | break; | ||
86 | seq_printf(m, "%*s%0*lx-%0*lx : %s\n", | ||
87 | depth * 2, "", | ||
88 | width, r->start, | ||
89 | width, r->end, | ||
90 | r->name ? r->name : "<BAD>"); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static struct seq_operations resource_op = { | ||
95 | .start = r_start, | ||
96 | .next = r_next, | ||
97 | .stop = r_stop, | ||
98 | .show = r_show, | ||
99 | }; | ||
100 | |||
101 | static int ioports_open(struct inode *inode, struct file *file) | ||
102 | { | ||
103 | int res = seq_open(file, &resource_op); | ||
104 | if (!res) { | ||
105 | struct seq_file *m = file->private_data; | ||
106 | m->private = &ioport_resource; | ||
107 | } | ||
108 | return res; | ||
109 | } | ||
110 | |||
111 | static int iomem_open(struct inode *inode, struct file *file) | ||
112 | { | ||
113 | int res = seq_open(file, &resource_op); | ||
114 | if (!res) { | ||
115 | struct seq_file *m = file->private_data; | ||
116 | m->private = &iomem_resource; | ||
117 | } | ||
118 | return res; | ||
119 | } | ||
120 | |||
121 | static struct file_operations proc_ioports_operations = { | ||
122 | .open = ioports_open, | ||
123 | .read = seq_read, | ||
124 | .llseek = seq_lseek, | ||
125 | .release = seq_release, | ||
126 | }; | ||
127 | |||
128 | static struct file_operations proc_iomem_operations = { | ||
129 | .open = iomem_open, | ||
130 | .read = seq_read, | ||
131 | .llseek = seq_lseek, | ||
132 | .release = seq_release, | ||
133 | }; | ||
134 | |||
135 | static int __init ioresources_init(void) | ||
136 | { | ||
137 | struct proc_dir_entry *entry; | ||
138 | |||
139 | entry = create_proc_entry("ioports", 0, NULL); | ||
140 | if (entry) | ||
141 | entry->proc_fops = &proc_ioports_operations; | ||
142 | entry = create_proc_entry("iomem", 0, NULL); | ||
143 | if (entry) | ||
144 | entry->proc_fops = &proc_iomem_operations; | ||
145 | return 0; | ||
146 | } | ||
147 | __initcall(ioresources_init); | ||
148 | |||
149 | #endif /* CONFIG_PROC_FS */ | ||
150 | |||
151 | /* Return the conflict entry if you can't request it */ | ||
152 | static struct resource * __request_resource(struct resource *root, struct resource *new) | ||
153 | { | ||
154 | unsigned long start = new->start; | ||
155 | unsigned long end = new->end; | ||
156 | struct resource *tmp, **p; | ||
157 | |||
158 | if (end < start) | ||
159 | return root; | ||
160 | if (start < root->start) | ||
161 | return root; | ||
162 | if (end > root->end) | ||
163 | return root; | ||
164 | p = &root->child; | ||
165 | for (;;) { | ||
166 | tmp = *p; | ||
167 | if (!tmp || tmp->start > end) { | ||
168 | new->sibling = tmp; | ||
169 | *p = new; | ||
170 | new->parent = root; | ||
171 | return NULL; | ||
172 | } | ||
173 | p = &tmp->sibling; | ||
174 | if (tmp->end < start) | ||
175 | continue; | ||
176 | return tmp; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static int __release_resource(struct resource *old) | ||
181 | { | ||
182 | struct resource *tmp, **p; | ||
183 | |||
184 | p = &old->parent->child; | ||
185 | for (;;) { | ||
186 | tmp = *p; | ||
187 | if (!tmp) | ||
188 | break; | ||
189 | if (tmp == old) { | ||
190 | *p = tmp->sibling; | ||
191 | old->parent = NULL; | ||
192 | return 0; | ||
193 | } | ||
194 | p = &tmp->sibling; | ||
195 | } | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | |||
199 | int request_resource(struct resource *root, struct resource *new) | ||
200 | { | ||
201 | struct resource *conflict; | ||
202 | |||
203 | write_lock(&resource_lock); | ||
204 | conflict = __request_resource(root, new); | ||
205 | write_unlock(&resource_lock); | ||
206 | return conflict ? -EBUSY : 0; | ||
207 | } | ||
208 | |||
209 | EXPORT_SYMBOL(request_resource); | ||
210 | |||
211 | struct resource *____request_resource(struct resource *root, struct resource *new) | ||
212 | { | ||
213 | struct resource *conflict; | ||
214 | |||
215 | write_lock(&resource_lock); | ||
216 | conflict = __request_resource(root, new); | ||
217 | write_unlock(&resource_lock); | ||
218 | return conflict; | ||
219 | } | ||
220 | |||
221 | EXPORT_SYMBOL(____request_resource); | ||
222 | |||
223 | int release_resource(struct resource *old) | ||
224 | { | ||
225 | int retval; | ||
226 | |||
227 | write_lock(&resource_lock); | ||
228 | retval = __release_resource(old); | ||
229 | write_unlock(&resource_lock); | ||
230 | return retval; | ||
231 | } | ||
232 | |||
233 | EXPORT_SYMBOL(release_resource); | ||
234 | |||
235 | /* | ||
236 | * Find empty slot in the resource tree given range and alignment. | ||
237 | */ | ||
238 | static int find_resource(struct resource *root, struct resource *new, | ||
239 | unsigned long size, | ||
240 | unsigned long min, unsigned long max, | ||
241 | unsigned long align, | ||
242 | void (*alignf)(void *, struct resource *, | ||
243 | unsigned long, unsigned long), | ||
244 | void *alignf_data) | ||
245 | { | ||
246 | struct resource *this = root->child; | ||
247 | |||
248 | new->start = root->start; | ||
249 | /* | ||
250 | * Skip past an allocated resource that starts at 0, since the assignment | ||
251 | * of this->start - 1 to new->end below would cause an underflow. | ||
252 | */ | ||
253 | if (this && this->start == 0) { | ||
254 | new->start = this->end + 1; | ||
255 | this = this->sibling; | ||
256 | } | ||
257 | for(;;) { | ||
258 | if (this) | ||
259 | new->end = this->start - 1; | ||
260 | else | ||
261 | new->end = root->end; | ||
262 | if (new->start < min) | ||
263 | new->start = min; | ||
264 | if (new->end > max) | ||
265 | new->end = max; | ||
266 | new->start = (new->start + align - 1) & ~(align - 1); | ||
267 | if (alignf) | ||
268 | alignf(alignf_data, new, size, align); | ||
269 | if (new->start < new->end && new->end - new->start + 1 >= size) { | ||
270 | new->end = new->start + size - 1; | ||
271 | return 0; | ||
272 | } | ||
273 | if (!this) | ||
274 | break; | ||
275 | new->start = this->end + 1; | ||
276 | this = this->sibling; | ||
277 | } | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * Allocate empty slot in the resource tree given range and alignment. | ||
283 | */ | ||
284 | int allocate_resource(struct resource *root, struct resource *new, | ||
285 | unsigned long size, | ||
286 | unsigned long min, unsigned long max, | ||
287 | unsigned long align, | ||
288 | void (*alignf)(void *, struct resource *, | ||
289 | unsigned long, unsigned long), | ||
290 | void *alignf_data) | ||
291 | { | ||
292 | int err; | ||
293 | |||
294 | write_lock(&resource_lock); | ||
295 | err = find_resource(root, new, size, min, max, align, alignf, alignf_data); | ||
296 | if (err >= 0 && __request_resource(root, new)) | ||
297 | err = -EBUSY; | ||
298 | write_unlock(&resource_lock); | ||
299 | return err; | ||
300 | } | ||
301 | |||
302 | EXPORT_SYMBOL(allocate_resource); | ||
303 | |||
304 | /** | ||
305 | * insert_resource - Inserts a resource in the resource tree | ||
306 | * @parent: parent of the new resource | ||
307 | * @new: new resource to insert | ||
308 | * | ||
309 | * Returns 0 on success, -EBUSY if the resource can't be inserted. | ||
310 | * | ||
311 | * This function is equivalent of request_resource when no conflict | ||
312 | * happens. If a conflict happens, and the conflicting resources | ||
313 | * entirely fit within the range of the new resource, then the new | ||
314 | * resource is inserted and the conflicting resources become childs of | ||
315 | * the new resource. Otherwise the new resource becomes the child of | ||
316 | * the conflicting resource | ||
317 | */ | ||
318 | int insert_resource(struct resource *parent, struct resource *new) | ||
319 | { | ||
320 | int result; | ||
321 | struct resource *first, *next; | ||
322 | |||
323 | write_lock(&resource_lock); | ||
324 | begin: | ||
325 | result = 0; | ||
326 | first = __request_resource(parent, new); | ||
327 | if (!first) | ||
328 | goto out; | ||
329 | |||
330 | result = -EBUSY; | ||
331 | if (first == parent) | ||
332 | goto out; | ||
333 | |||
334 | /* Resource fully contained by the clashing resource? Recurse into it */ | ||
335 | if (first->start <= new->start && first->end >= new->end) { | ||
336 | parent = first; | ||
337 | goto begin; | ||
338 | } | ||
339 | |||
340 | for (next = first; ; next = next->sibling) { | ||
341 | /* Partial overlap? Bad, and unfixable */ | ||
342 | if (next->start < new->start || next->end > new->end) | ||
343 | goto out; | ||
344 | if (!next->sibling) | ||
345 | break; | ||
346 | if (next->sibling->start > new->end) | ||
347 | break; | ||
348 | } | ||
349 | |||
350 | result = 0; | ||
351 | |||
352 | new->parent = parent; | ||
353 | new->sibling = next->sibling; | ||
354 | new->child = first; | ||
355 | |||
356 | next->sibling = NULL; | ||
357 | for (next = first; next; next = next->sibling) | ||
358 | next->parent = new; | ||
359 | |||
360 | if (parent->child == first) { | ||
361 | parent->child = new; | ||
362 | } else { | ||
363 | next = parent->child; | ||
364 | while (next->sibling != first) | ||
365 | next = next->sibling; | ||
366 | next->sibling = new; | ||
367 | } | ||
368 | |||
369 | out: | ||
370 | write_unlock(&resource_lock); | ||
371 | return result; | ||
372 | } | ||
373 | |||
374 | EXPORT_SYMBOL(insert_resource); | ||
375 | |||
376 | /* | ||
377 | * Given an existing resource, change its start and size to match the | ||
378 | * arguments. Returns -EBUSY if it can't fit. Existing children of | ||
379 | * the resource are assumed to be immutable. | ||
380 | */ | ||
381 | int adjust_resource(struct resource *res, unsigned long start, unsigned long size) | ||
382 | { | ||
383 | struct resource *tmp, *parent = res->parent; | ||
384 | unsigned long end = start + size - 1; | ||
385 | int result = -EBUSY; | ||
386 | |||
387 | write_lock(&resource_lock); | ||
388 | |||
389 | if ((start < parent->start) || (end > parent->end)) | ||
390 | goto out; | ||
391 | |||
392 | for (tmp = res->child; tmp; tmp = tmp->sibling) { | ||
393 | if ((tmp->start < start) || (tmp->end > end)) | ||
394 | goto out; | ||
395 | } | ||
396 | |||
397 | if (res->sibling && (res->sibling->start <= end)) | ||
398 | goto out; | ||
399 | |||
400 | tmp = parent->child; | ||
401 | if (tmp != res) { | ||
402 | while (tmp->sibling != res) | ||
403 | tmp = tmp->sibling; | ||
404 | if (start <= tmp->end) | ||
405 | goto out; | ||
406 | } | ||
407 | |||
408 | res->start = start; | ||
409 | res->end = end; | ||
410 | result = 0; | ||
411 | |||
412 | out: | ||
413 | write_unlock(&resource_lock); | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | EXPORT_SYMBOL(adjust_resource); | ||
418 | |||
419 | /* | ||
420 | * This is compatibility stuff for IO resources. | ||
421 | * | ||
422 | * Note how this, unlike the above, knows about | ||
423 | * the IO flag meanings (busy etc). | ||
424 | * | ||
425 | * Request-region creates a new busy region. | ||
426 | * | ||
427 | * Check-region returns non-zero if the area is already busy | ||
428 | * | ||
429 | * Release-region releases a matching busy region. | ||
430 | */ | ||
431 | struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name) | ||
432 | { | ||
433 | struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); | ||
434 | |||
435 | if (res) { | ||
436 | memset(res, 0, sizeof(*res)); | ||
437 | res->name = name; | ||
438 | res->start = start; | ||
439 | res->end = start + n - 1; | ||
440 | res->flags = IORESOURCE_BUSY; | ||
441 | |||
442 | write_lock(&resource_lock); | ||
443 | |||
444 | for (;;) { | ||
445 | struct resource *conflict; | ||
446 | |||
447 | conflict = __request_resource(parent, res); | ||
448 | if (!conflict) | ||
449 | break; | ||
450 | if (conflict != parent) { | ||
451 | parent = conflict; | ||
452 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
453 | continue; | ||
454 | } | ||
455 | |||
456 | /* Uhhuh, that didn't work out.. */ | ||
457 | kfree(res); | ||
458 | res = NULL; | ||
459 | break; | ||
460 | } | ||
461 | write_unlock(&resource_lock); | ||
462 | } | ||
463 | return res; | ||
464 | } | ||
465 | |||
466 | EXPORT_SYMBOL(__request_region); | ||
467 | |||
468 | int __deprecated __check_region(struct resource *parent, unsigned long start, unsigned long n) | ||
469 | { | ||
470 | struct resource * res; | ||
471 | |||
472 | res = __request_region(parent, start, n, "check-region"); | ||
473 | if (!res) | ||
474 | return -EBUSY; | ||
475 | |||
476 | release_resource(res); | ||
477 | kfree(res); | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | EXPORT_SYMBOL(__check_region); | ||
482 | |||
483 | void __release_region(struct resource *parent, unsigned long start, unsigned long n) | ||
484 | { | ||
485 | struct resource **p; | ||
486 | unsigned long end; | ||
487 | |||
488 | p = &parent->child; | ||
489 | end = start + n - 1; | ||
490 | |||
491 | write_lock(&resource_lock); | ||
492 | |||
493 | for (;;) { | ||
494 | struct resource *res = *p; | ||
495 | |||
496 | if (!res) | ||
497 | break; | ||
498 | if (res->start <= start && res->end >= end) { | ||
499 | if (!(res->flags & IORESOURCE_BUSY)) { | ||
500 | p = &res->child; | ||
501 | continue; | ||
502 | } | ||
503 | if (res->start != start || res->end != end) | ||
504 | break; | ||
505 | *p = res->sibling; | ||
506 | write_unlock(&resource_lock); | ||
507 | kfree(res); | ||
508 | return; | ||
509 | } | ||
510 | p = &res->sibling; | ||
511 | } | ||
512 | |||
513 | write_unlock(&resource_lock); | ||
514 | |||
515 | printk(KERN_WARNING "Trying to free nonexistent resource <%08lx-%08lx>\n", start, end); | ||
516 | } | ||
517 | |||
518 | EXPORT_SYMBOL(__release_region); | ||
519 | |||
520 | /* | ||
521 | * Called from init/main.c to reserve IO ports. | ||
522 | */ | ||
523 | #define MAXRESERVE 4 | ||
524 | static int __init reserve_setup(char *str) | ||
525 | { | ||
526 | static int reserved; | ||
527 | static struct resource reserve[MAXRESERVE]; | ||
528 | |||
529 | for (;;) { | ||
530 | int io_start, io_num; | ||
531 | int x = reserved; | ||
532 | |||
533 | if (get_option (&str, &io_start) != 2) | ||
534 | break; | ||
535 | if (get_option (&str, &io_num) == 0) | ||
536 | break; | ||
537 | if (x < MAXRESERVE) { | ||
538 | struct resource *res = reserve + x; | ||
539 | res->name = "reserved"; | ||
540 | res->start = io_start; | ||
541 | res->end = io_start + io_num - 1; | ||
542 | res->flags = IORESOURCE_BUSY; | ||
543 | res->child = NULL; | ||
544 | if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0) | ||
545 | reserved = x+1; | ||
546 | } | ||
547 | } | ||
548 | return 1; | ||
549 | } | ||
550 | |||
551 | __setup("reserve=", reserve_setup); | ||