diff options
Diffstat (limited to 'kernel/resource.c')
| -rw-r--r-- | kernel/resource.c | 197 |
1 files changed, 168 insertions, 29 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index f5b518eabefe..7797dae85b50 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
| @@ -38,10 +38,6 @@ EXPORT_SYMBOL(iomem_resource); | |||
| 38 | 38 | ||
| 39 | static DEFINE_RWLOCK(resource_lock); | 39 | static DEFINE_RWLOCK(resource_lock); |
| 40 | 40 | ||
| 41 | #ifdef CONFIG_PROC_FS | ||
| 42 | |||
| 43 | enum { MAX_IORES_LEVEL = 5 }; | ||
| 44 | |||
| 45 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) | 41 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) |
| 46 | { | 42 | { |
| 47 | struct resource *p = v; | 43 | struct resource *p = v; |
| @@ -53,6 +49,10 @@ static void *r_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 53 | return p->sibling; | 49 | return p->sibling; |
| 54 | } | 50 | } |
| 55 | 51 | ||
| 52 | #ifdef CONFIG_PROC_FS | ||
| 53 | |||
| 54 | enum { MAX_IORES_LEVEL = 5 }; | ||
| 55 | |||
| 56 | static void *r_start(struct seq_file *m, loff_t *pos) | 56 | static void *r_start(struct seq_file *m, loff_t *pos) |
| 57 | __acquires(resource_lock) | 57 | __acquires(resource_lock) |
| 58 | { | 58 | { |
| @@ -362,35 +362,21 @@ int allocate_resource(struct resource *root, struct resource *new, | |||
| 362 | 362 | ||
| 363 | EXPORT_SYMBOL(allocate_resource); | 363 | EXPORT_SYMBOL(allocate_resource); |
| 364 | 364 | ||
| 365 | /** | 365 | /* |
| 366 | * insert_resource - Inserts a resource in the resource tree | 366 | * Insert a resource into the resource tree. If successful, return NULL, |
| 367 | * @parent: parent of the new resource | 367 | * otherwise return the conflicting resource (compare to __request_resource()) |
| 368 | * @new: new resource to insert | ||
| 369 | * | ||
| 370 | * Returns 0 on success, -EBUSY if the resource can't be inserted. | ||
| 371 | * | ||
| 372 | * This function is equivalent to request_resource when no conflict | ||
| 373 | * happens. If a conflict happens, and the conflicting resources | ||
| 374 | * entirely fit within the range of the new resource, then the new | ||
| 375 | * resource is inserted and the conflicting resources become children of | ||
| 376 | * the new resource. | ||
| 377 | */ | 368 | */ |
| 378 | int insert_resource(struct resource *parent, struct resource *new) | 369 | static struct resource * __insert_resource(struct resource *parent, struct resource *new) |
| 379 | { | 370 | { |
| 380 | int result; | ||
| 381 | struct resource *first, *next; | 371 | struct resource *first, *next; |
| 382 | 372 | ||
| 383 | write_lock(&resource_lock); | ||
| 384 | |||
| 385 | for (;; parent = first) { | 373 | for (;; parent = first) { |
| 386 | result = 0; | ||
| 387 | first = __request_resource(parent, new); | 374 | first = __request_resource(parent, new); |
| 388 | if (!first) | 375 | if (!first) |
| 389 | goto out; | 376 | return first; |
| 390 | 377 | ||
| 391 | result = -EBUSY; | ||
| 392 | if (first == parent) | 378 | if (first == parent) |
| 393 | goto out; | 379 | return first; |
| 394 | 380 | ||
| 395 | if ((first->start > new->start) || (first->end < new->end)) | 381 | if ((first->start > new->start) || (first->end < new->end)) |
| 396 | break; | 382 | break; |
| @@ -401,15 +387,13 @@ int insert_resource(struct resource *parent, struct resource *new) | |||
| 401 | for (next = first; ; next = next->sibling) { | 387 | for (next = first; ; next = next->sibling) { |
| 402 | /* Partial overlap? Bad, and unfixable */ | 388 | /* Partial overlap? Bad, and unfixable */ |
| 403 | if (next->start < new->start || next->end > new->end) | 389 | if (next->start < new->start || next->end > new->end) |
| 404 | goto out; | 390 | return next; |
| 405 | if (!next->sibling) | 391 | if (!next->sibling) |
| 406 | break; | 392 | break; |
| 407 | if (next->sibling->start > new->end) | 393 | if (next->sibling->start > new->end) |
| 408 | break; | 394 | break; |
| 409 | } | 395 | } |
| 410 | 396 | ||
| 411 | result = 0; | ||
| 412 | |||
| 413 | new->parent = parent; | 397 | new->parent = parent; |
| 414 | new->sibling = next->sibling; | 398 | new->sibling = next->sibling; |
| 415 | new->child = first; | 399 | new->child = first; |
| @@ -426,10 +410,64 @@ int insert_resource(struct resource *parent, struct resource *new) | |||
| 426 | next = next->sibling; | 410 | next = next->sibling; |
| 427 | next->sibling = new; | 411 | next->sibling = new; |
| 428 | } | 412 | } |
| 413 | return NULL; | ||
| 414 | } | ||
| 429 | 415 | ||
| 430 | out: | 416 | /** |
| 417 | * insert_resource - Inserts a resource in the resource tree | ||
| 418 | * @parent: parent of the new resource | ||
| 419 | * @new: new resource to insert | ||
| 420 | * | ||
| 421 | * Returns 0 on success, -EBUSY if the resource can't be inserted. | ||
| 422 | * | ||
| 423 | * This function is equivalent to request_resource when no conflict | ||
| 424 | * happens. If a conflict happens, and the conflicting resources | ||
| 425 | * entirely fit within the range of the new resource, then the new | ||
| 426 | * resource is inserted and the conflicting resources become children of | ||
| 427 | * the new resource. | ||
| 428 | */ | ||
| 429 | int insert_resource(struct resource *parent, struct resource *new) | ||
| 430 | { | ||
| 431 | struct resource *conflict; | ||
| 432 | |||
| 433 | write_lock(&resource_lock); | ||
| 434 | conflict = __insert_resource(parent, new); | ||
| 435 | write_unlock(&resource_lock); | ||
| 436 | return conflict ? -EBUSY : 0; | ||
| 437 | } | ||
| 438 | |||
| 439 | /** | ||
| 440 | * insert_resource_expand_to_fit - Insert a resource into the resource tree | ||
| 441 | * @root: root resource descriptor | ||
| 442 | * @new: new resource to insert | ||
| 443 | * | ||
| 444 | * Insert a resource into the resource tree, possibly expanding it in order | ||
| 445 | * to make it encompass any conflicting resources. | ||
| 446 | */ | ||
| 447 | void insert_resource_expand_to_fit(struct resource *root, struct resource *new) | ||
| 448 | { | ||
| 449 | if (new->parent) | ||
| 450 | return; | ||
| 451 | |||
| 452 | write_lock(&resource_lock); | ||
| 453 | for (;;) { | ||
| 454 | struct resource *conflict; | ||
| 455 | |||
| 456 | conflict = __insert_resource(root, new); | ||
| 457 | if (!conflict) | ||
| 458 | break; | ||
| 459 | if (conflict == root) | ||
| 460 | break; | ||
| 461 | |||
| 462 | /* Ok, expand resource to cover the conflict, then try again .. */ | ||
| 463 | if (conflict->start < new->start) | ||
| 464 | new->start = conflict->start; | ||
| 465 | if (conflict->end > new->end) | ||
| 466 | new->end = conflict->end; | ||
| 467 | |||
| 468 | printk("Expanded resource %s due to conflict with %s\n", new->name, conflict->name); | ||
| 469 | } | ||
| 431 | write_unlock(&resource_lock); | 470 | write_unlock(&resource_lock); |
| 432 | return result; | ||
| 433 | } | 471 | } |
| 434 | 472 | ||
| 435 | /** | 473 | /** |
| @@ -478,6 +516,70 @@ int adjust_resource(struct resource *res, resource_size_t start, resource_size_t | |||
| 478 | return result; | 516 | return result; |
| 479 | } | 517 | } |
| 480 | 518 | ||
| 519 | static void __init __reserve_region_with_split(struct resource *root, | ||
| 520 | resource_size_t start, resource_size_t end, | ||
| 521 | const char *name) | ||
| 522 | { | ||
| 523 | struct resource *parent = root; | ||
| 524 | struct resource *conflict; | ||
| 525 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); | ||
| 526 | |||
| 527 | if (!res) | ||
| 528 | return; | ||
| 529 | |||
| 530 | res->name = name; | ||
| 531 | res->start = start; | ||
| 532 | res->end = end; | ||
| 533 | res->flags = IORESOURCE_BUSY; | ||
| 534 | |||
| 535 | for (;;) { | ||
| 536 | conflict = __request_resource(parent, res); | ||
| 537 | if (!conflict) | ||
| 538 | break; | ||
| 539 | if (conflict != parent) { | ||
| 540 | parent = conflict; | ||
| 541 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
| 542 | continue; | ||
| 543 | } | ||
| 544 | |||
| 545 | /* Uhhuh, that didn't work out.. */ | ||
| 546 | kfree(res); | ||
| 547 | res = NULL; | ||
| 548 | break; | ||
| 549 | } | ||
| 550 | |||
| 551 | if (!res) { | ||
| 552 | /* failed, split and try again */ | ||
| 553 | |||
| 554 | /* conflict covered whole area */ | ||
| 555 | if (conflict->start <= start && conflict->end >= end) | ||
| 556 | return; | ||
| 557 | |||
| 558 | if (conflict->start > start) | ||
| 559 | __reserve_region_with_split(root, start, conflict->start-1, name); | ||
| 560 | if (!(conflict->flags & IORESOURCE_BUSY)) { | ||
| 561 | resource_size_t common_start, common_end; | ||
| 562 | |||
| 563 | common_start = max(conflict->start, start); | ||
| 564 | common_end = min(conflict->end, end); | ||
| 565 | if (common_start < common_end) | ||
| 566 | __reserve_region_with_split(root, common_start, common_end, name); | ||
| 567 | } | ||
| 568 | if (conflict->end < end) | ||
| 569 | __reserve_region_with_split(root, conflict->end+1, end, name); | ||
| 570 | } | ||
| 571 | |||
| 572 | } | ||
| 573 | |||
| 574 | void reserve_region_with_split(struct resource *root, | ||
| 575 | resource_size_t start, resource_size_t end, | ||
| 576 | const char *name) | ||
| 577 | { | ||
| 578 | write_lock(&resource_lock); | ||
| 579 | __reserve_region_with_split(root, start, end, name); | ||
| 580 | write_unlock(&resource_lock); | ||
| 581 | } | ||
| 582 | |||
| 481 | EXPORT_SYMBOL(adjust_resource); | 583 | EXPORT_SYMBOL(adjust_resource); |
| 482 | 584 | ||
| 483 | /** | 585 | /** |
| @@ -725,3 +827,40 @@ static int __init reserve_setup(char *str) | |||
| 725 | } | 827 | } |
| 726 | 828 | ||
| 727 | __setup("reserve=", reserve_setup); | 829 | __setup("reserve=", reserve_setup); |
| 830 | |||
| 831 | /* | ||
| 832 | * Check if the requested addr and size spans more than any slot in the | ||
| 833 | * iomem resource tree. | ||
| 834 | */ | ||
| 835 | int iomem_map_sanity_check(resource_size_t addr, unsigned long size) | ||
| 836 | { | ||
| 837 | struct resource *p = &iomem_resource; | ||
| 838 | int err = 0; | ||
| 839 | loff_t l; | ||
| 840 | |||
| 841 | read_lock(&resource_lock); | ||
| 842 | for (p = p->child; p ; p = r_next(NULL, p, &l)) { | ||
| 843 | /* | ||
| 844 | * We can probably skip the resources without | ||
| 845 | * IORESOURCE_IO attribute? | ||
| 846 | */ | ||
| 847 | if (p->start >= addr + size) | ||
| 848 | continue; | ||
| 849 | if (p->end < addr) | ||
| 850 | continue; | ||
| 851 | if (p->start <= addr && (p->end >= addr + size - 1)) | ||
| 852 | continue; | ||
| 853 | printk(KERN_WARNING "resource map sanity check conflict: " | ||
| 854 | "0x%llx 0x%llx 0x%llx 0x%llx %s\n", | ||
| 855 | (unsigned long long)addr, | ||
| 856 | (unsigned long long)(addr + size - 1), | ||
| 857 | (unsigned long long)p->start, | ||
| 858 | (unsigned long long)p->end, | ||
| 859 | p->name); | ||
| 860 | err = -1; | ||
| 861 | break; | ||
| 862 | } | ||
| 863 | read_unlock(&resource_lock); | ||
| 864 | |||
| 865 | return err; | ||
| 866 | } | ||
