diff options
Diffstat (limited to 'kernel/resource.c')
-rw-r--r-- | kernel/resource.c | 126 |
1 files changed, 109 insertions, 17 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index af96c1e4b54b..7b36976e5dea 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
16 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
17 | #include <linux/proc_fs.h> | 17 | #include <linux/proc_fs.h> |
18 | #include <linux/sched.h> | ||
18 | #include <linux/seq_file.h> | 19 | #include <linux/seq_file.h> |
19 | #include <linux/device.h> | 20 | #include <linux/device.h> |
20 | #include <linux/pfn.h> | 21 | #include <linux/pfn.h> |
@@ -188,20 +189,65 @@ static int __release_resource(struct resource *old) | |||
188 | return -EINVAL; | 189 | return -EINVAL; |
189 | } | 190 | } |
190 | 191 | ||
192 | static void __release_child_resources(struct resource *r) | ||
193 | { | ||
194 | struct resource *tmp, *p; | ||
195 | resource_size_t size; | ||
196 | |||
197 | p = r->child; | ||
198 | r->child = NULL; | ||
199 | while (p) { | ||
200 | tmp = p; | ||
201 | p = p->sibling; | ||
202 | |||
203 | tmp->parent = NULL; | ||
204 | tmp->sibling = NULL; | ||
205 | __release_child_resources(tmp); | ||
206 | |||
207 | printk(KERN_DEBUG "release child resource %pR\n", tmp); | ||
208 | /* need to restore size, and keep flags */ | ||
209 | size = resource_size(tmp); | ||
210 | tmp->start = 0; | ||
211 | tmp->end = size - 1; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | void release_child_resources(struct resource *r) | ||
216 | { | ||
217 | write_lock(&resource_lock); | ||
218 | __release_child_resources(r); | ||
219 | write_unlock(&resource_lock); | ||
220 | } | ||
221 | |||
191 | /** | 222 | /** |
192 | * request_resource - request and reserve an I/O or memory resource | 223 | * request_resource_conflict - request and reserve an I/O or memory resource |
193 | * @root: root resource descriptor | 224 | * @root: root resource descriptor |
194 | * @new: resource descriptor desired by caller | 225 | * @new: resource descriptor desired by caller |
195 | * | 226 | * |
196 | * Returns 0 for success, negative error code on error. | 227 | * Returns 0 for success, conflict resource on error. |
197 | */ | 228 | */ |
198 | int request_resource(struct resource *root, struct resource *new) | 229 | struct resource *request_resource_conflict(struct resource *root, struct resource *new) |
199 | { | 230 | { |
200 | struct resource *conflict; | 231 | struct resource *conflict; |
201 | 232 | ||
202 | write_lock(&resource_lock); | 233 | write_lock(&resource_lock); |
203 | conflict = __request_resource(root, new); | 234 | conflict = __request_resource(root, new); |
204 | write_unlock(&resource_lock); | 235 | write_unlock(&resource_lock); |
236 | return conflict; | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * request_resource - request and reserve an I/O or memory resource | ||
241 | * @root: root resource descriptor | ||
242 | * @new: resource descriptor desired by caller | ||
243 | * | ||
244 | * Returns 0 for success, negative error code on error. | ||
245 | */ | ||
246 | int request_resource(struct resource *root, struct resource *new) | ||
247 | { | ||
248 | struct resource *conflict; | ||
249 | |||
250 | conflict = request_resource_conflict(root, new); | ||
205 | return conflict ? -EBUSY : 0; | 251 | return conflict ? -EBUSY : 0; |
206 | } | 252 | } |
207 | 253 | ||
@@ -274,7 +320,7 @@ int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, | |||
274 | void *arg, int (*func)(unsigned long, unsigned long, void *)) | 320 | void *arg, int (*func)(unsigned long, unsigned long, void *)) |
275 | { | 321 | { |
276 | struct resource res; | 322 | struct resource res; |
277 | unsigned long pfn, len; | 323 | unsigned long pfn, end_pfn; |
278 | u64 orig_end; | 324 | u64 orig_end; |
279 | int ret = -1; | 325 | int ret = -1; |
280 | 326 | ||
@@ -284,9 +330,10 @@ int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, | |||
284 | orig_end = res.end; | 330 | orig_end = res.end; |
285 | while ((res.start < res.end) && | 331 | while ((res.start < res.end) && |
286 | (find_next_system_ram(&res, "System RAM") >= 0)) { | 332 | (find_next_system_ram(&res, "System RAM") >= 0)) { |
287 | pfn = (unsigned long)(res.start >> PAGE_SHIFT); | 333 | pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT; |
288 | len = (unsigned long)((res.end + 1 - res.start) >> PAGE_SHIFT); | 334 | end_pfn = (res.end + 1) >> PAGE_SHIFT; |
289 | ret = (*func)(pfn, len, arg); | 335 | if (end_pfn > pfn) |
336 | ret = (*func)(pfn, end_pfn - pfn, arg); | ||
290 | if (ret) | 337 | if (ret) |
291 | break; | 338 | break; |
292 | res.start = res.end + 1; | 339 | res.start = res.end + 1; |
@@ -297,14 +344,29 @@ int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, | |||
297 | 344 | ||
298 | #endif | 345 | #endif |
299 | 346 | ||
347 | static int __is_ram(unsigned long pfn, unsigned long nr_pages, void *arg) | ||
348 | { | ||
349 | return 1; | ||
350 | } | ||
351 | /* | ||
352 | * This generic page_is_ram() returns true if specified address is | ||
353 | * registered as "System RAM" in iomem_resource list. | ||
354 | */ | ||
355 | int __weak page_is_ram(unsigned long pfn) | ||
356 | { | ||
357 | return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1; | ||
358 | } | ||
359 | |||
300 | /* | 360 | /* |
301 | * Find empty slot in the resource tree given range and alignment. | 361 | * Find empty slot in the resource tree given range and alignment. |
302 | */ | 362 | */ |
303 | static int find_resource(struct resource *root, struct resource *new, | 363 | static int find_resource(struct resource *root, struct resource *new, |
304 | resource_size_t size, resource_size_t min, | 364 | resource_size_t size, resource_size_t min, |
305 | resource_size_t max, resource_size_t align, | 365 | resource_size_t max, resource_size_t align, |
306 | void (*alignf)(void *, struct resource *, | 366 | resource_size_t (*alignf)(void *, |
307 | resource_size_t, resource_size_t), | 367 | const struct resource *, |
368 | resource_size_t, | ||
369 | resource_size_t), | ||
308 | void *alignf_data) | 370 | void *alignf_data) |
309 | { | 371 | { |
310 | struct resource *this = root->child; | 372 | struct resource *this = root->child; |
@@ -330,7 +392,7 @@ static int find_resource(struct resource *root, struct resource *new, | |||
330 | tmp.end = max; | 392 | tmp.end = max; |
331 | tmp.start = ALIGN(tmp.start, align); | 393 | tmp.start = ALIGN(tmp.start, align); |
332 | if (alignf) | 394 | if (alignf) |
333 | alignf(alignf_data, &tmp, size, align); | 395 | tmp.start = alignf(alignf_data, &tmp, size, align); |
334 | if (tmp.start < tmp.end && tmp.end - tmp.start >= size - 1) { | 396 | if (tmp.start < tmp.end && tmp.end - tmp.start >= size - 1) { |
335 | new->start = tmp.start; | 397 | new->start = tmp.start; |
336 | new->end = tmp.start + size - 1; | 398 | new->end = tmp.start + size - 1; |
@@ -358,8 +420,10 @@ static int find_resource(struct resource *root, struct resource *new, | |||
358 | int allocate_resource(struct resource *root, struct resource *new, | 420 | int allocate_resource(struct resource *root, struct resource *new, |
359 | resource_size_t size, resource_size_t min, | 421 | resource_size_t size, resource_size_t min, |
360 | resource_size_t max, resource_size_t align, | 422 | resource_size_t max, resource_size_t align, |
361 | void (*alignf)(void *, struct resource *, | 423 | resource_size_t (*alignf)(void *, |
362 | resource_size_t, resource_size_t), | 424 | const struct resource *, |
425 | resource_size_t, | ||
426 | resource_size_t), | ||
363 | void *alignf_data) | 427 | void *alignf_data) |
364 | { | 428 | { |
365 | int err; | 429 | int err; |
@@ -426,25 +490,40 @@ static struct resource * __insert_resource(struct resource *parent, struct resou | |||
426 | } | 490 | } |
427 | 491 | ||
428 | /** | 492 | /** |
429 | * insert_resource - Inserts a resource in the resource tree | 493 | * insert_resource_conflict - Inserts resource in the resource tree |
430 | * @parent: parent of the new resource | 494 | * @parent: parent of the new resource |
431 | * @new: new resource to insert | 495 | * @new: new resource to insert |
432 | * | 496 | * |
433 | * Returns 0 on success, -EBUSY if the resource can't be inserted. | 497 | * Returns 0 on success, conflict resource if the resource can't be inserted. |
434 | * | 498 | * |
435 | * This function is equivalent to request_resource when no conflict | 499 | * This function is equivalent to request_resource_conflict when no conflict |
436 | * happens. If a conflict happens, and the conflicting resources | 500 | * happens. If a conflict happens, and the conflicting resources |
437 | * entirely fit within the range of the new resource, then the new | 501 | * entirely fit within the range of the new resource, then the new |
438 | * resource is inserted and the conflicting resources become children of | 502 | * resource is inserted and the conflicting resources become children of |
439 | * the new resource. | 503 | * the new resource. |
440 | */ | 504 | */ |
441 | int insert_resource(struct resource *parent, struct resource *new) | 505 | struct resource *insert_resource_conflict(struct resource *parent, struct resource *new) |
442 | { | 506 | { |
443 | struct resource *conflict; | 507 | struct resource *conflict; |
444 | 508 | ||
445 | write_lock(&resource_lock); | 509 | write_lock(&resource_lock); |
446 | conflict = __insert_resource(parent, new); | 510 | conflict = __insert_resource(parent, new); |
447 | write_unlock(&resource_lock); | 511 | write_unlock(&resource_lock); |
512 | return conflict; | ||
513 | } | ||
514 | |||
515 | /** | ||
516 | * insert_resource - Inserts a resource in the resource tree | ||
517 | * @parent: parent of the new resource | ||
518 | * @new: new resource to insert | ||
519 | * | ||
520 | * Returns 0 on success, -EBUSY if the resource can't be inserted. | ||
521 | */ | ||
522 | int insert_resource(struct resource *parent, struct resource *new) | ||
523 | { | ||
524 | struct resource *conflict; | ||
525 | |||
526 | conflict = insert_resource_conflict(parent, new); | ||
448 | return conflict ? -EBUSY : 0; | 527 | return conflict ? -EBUSY : 0; |
449 | } | 528 | } |
450 | 529 | ||
@@ -603,6 +682,8 @@ resource_size_t resource_alignment(struct resource *res) | |||
603 | * release_region releases a matching busy region. | 682 | * release_region releases a matching busy region. |
604 | */ | 683 | */ |
605 | 684 | ||
685 | static DECLARE_WAIT_QUEUE_HEAD(muxed_resource_wait); | ||
686 | |||
606 | /** | 687 | /** |
607 | * __request_region - create a new busy resource region | 688 | * __request_region - create a new busy resource region |
608 | * @parent: parent resource descriptor | 689 | * @parent: parent resource descriptor |
@@ -615,6 +696,7 @@ struct resource * __request_region(struct resource *parent, | |||
615 | resource_size_t start, resource_size_t n, | 696 | resource_size_t start, resource_size_t n, |
616 | const char *name, int flags) | 697 | const char *name, int flags) |
617 | { | 698 | { |
699 | DECLARE_WAITQUEUE(wait, current); | ||
618 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); | 700 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); |
619 | 701 | ||
620 | if (!res) | 702 | if (!res) |
@@ -639,7 +721,15 @@ struct resource * __request_region(struct resource *parent, | |||
639 | if (!(conflict->flags & IORESOURCE_BUSY)) | 721 | if (!(conflict->flags & IORESOURCE_BUSY)) |
640 | continue; | 722 | continue; |
641 | } | 723 | } |
642 | 724 | if (conflict->flags & flags & IORESOURCE_MUXED) { | |
725 | add_wait_queue(&muxed_resource_wait, &wait); | ||
726 | write_unlock(&resource_lock); | ||
727 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
728 | schedule(); | ||
729 | remove_wait_queue(&muxed_resource_wait, &wait); | ||
730 | write_lock(&resource_lock); | ||
731 | continue; | ||
732 | } | ||
643 | /* Uhhuh, that didn't work out.. */ | 733 | /* Uhhuh, that didn't work out.. */ |
644 | kfree(res); | 734 | kfree(res); |
645 | res = NULL; | 735 | res = NULL; |
@@ -713,6 +803,8 @@ void __release_region(struct resource *parent, resource_size_t start, | |||
713 | break; | 803 | break; |
714 | *p = res->sibling; | 804 | *p = res->sibling; |
715 | write_unlock(&resource_lock); | 805 | write_unlock(&resource_lock); |
806 | if (res->flags & IORESOURCE_MUXED) | ||
807 | wake_up(&muxed_resource_wait); | ||
716 | kfree(res); | 808 | kfree(res); |
717 | return; | 809 | return; |
718 | } | 810 | } |