diff options
Diffstat (limited to 'kernel/resource.c')
| -rw-r--r-- | kernel/resource.c | 152 |
1 files changed, 128 insertions, 24 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index 03d796c1b2e9..4337063663ef 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/proc_fs.h> | 17 | #include <linux/proc_fs.h> |
| 18 | #include <linux/seq_file.h> | 18 | #include <linux/seq_file.h> |
| 19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
| 20 | #include <linux/pfn.h> | ||
| 20 | #include <asm/io.h> | 21 | #include <asm/io.h> |
| 21 | 22 | ||
| 22 | 23 | ||
| @@ -38,10 +39,6 @@ EXPORT_SYMBOL(iomem_resource); | |||
| 38 | 39 | ||
| 39 | static DEFINE_RWLOCK(resource_lock); | 40 | static DEFINE_RWLOCK(resource_lock); |
| 40 | 41 | ||
| 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) | 42 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) |
| 46 | { | 43 | { |
| 47 | struct resource *p = v; | 44 | struct resource *p = v; |
| @@ -53,6 +50,10 @@ static void *r_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 53 | return p->sibling; | 50 | return p->sibling; |
| 54 | } | 51 | } |
| 55 | 52 | ||
| 53 | #ifdef CONFIG_PROC_FS | ||
| 54 | |||
| 55 | enum { MAX_IORES_LEVEL = 5 }; | ||
| 56 | |||
| 56 | static void *r_start(struct seq_file *m, loff_t *pos) | 57 | static void *r_start(struct seq_file *m, loff_t *pos) |
| 57 | __acquires(resource_lock) | 58 | __acquires(resource_lock) |
| 58 | { | 59 | { |
| @@ -516,6 +517,70 @@ int adjust_resource(struct resource *res, resource_size_t start, resource_size_t | |||
| 516 | return result; | 517 | return result; |
| 517 | } | 518 | } |
| 518 | 519 | ||
| 520 | static void __init __reserve_region_with_split(struct resource *root, | ||
| 521 | resource_size_t start, resource_size_t end, | ||
| 522 | const char *name) | ||
| 523 | { | ||
| 524 | struct resource *parent = root; | ||
| 525 | struct resource *conflict; | ||
| 526 | struct resource *res = kzalloc(sizeof(*res), GFP_ATOMIC); | ||
| 527 | |||
| 528 | if (!res) | ||
| 529 | return; | ||
| 530 | |||
| 531 | res->name = name; | ||
| 532 | res->start = start; | ||
| 533 | res->end = end; | ||
| 534 | res->flags = IORESOURCE_BUSY; | ||
| 535 | |||
| 536 | for (;;) { | ||
| 537 | conflict = __request_resource(parent, res); | ||
| 538 | if (!conflict) | ||
| 539 | break; | ||
| 540 | if (conflict != parent) { | ||
| 541 | parent = conflict; | ||
| 542 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
| 543 | continue; | ||
| 544 | } | ||
| 545 | |||
| 546 | /* Uhhuh, that didn't work out.. */ | ||
| 547 | kfree(res); | ||
| 548 | res = NULL; | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | |||
| 552 | if (!res) { | ||
| 553 | /* failed, split and try again */ | ||
| 554 | |||
| 555 | /* conflict covered whole area */ | ||
| 556 | if (conflict->start <= start && conflict->end >= end) | ||
| 557 | return; | ||
| 558 | |||
| 559 | if (conflict->start > start) | ||
| 560 | __reserve_region_with_split(root, start, conflict->start-1, name); | ||
| 561 | if (!(conflict->flags & IORESOURCE_BUSY)) { | ||
| 562 | resource_size_t common_start, common_end; | ||
| 563 | |||
| 564 | common_start = max(conflict->start, start); | ||
| 565 | common_end = min(conflict->end, end); | ||
| 566 | if (common_start < common_end) | ||
| 567 | __reserve_region_with_split(root, common_start, common_end, name); | ||
| 568 | } | ||
| 569 | if (conflict->end < end) | ||
| 570 | __reserve_region_with_split(root, conflict->end+1, end, name); | ||
| 571 | } | ||
| 572 | |||
| 573 | } | ||
| 574 | |||
| 575 | void __init reserve_region_with_split(struct resource *root, | ||
| 576 | resource_size_t start, resource_size_t end, | ||
| 577 | const char *name) | ||
| 578 | { | ||
| 579 | write_lock(&resource_lock); | ||
| 580 | __reserve_region_with_split(root, start, end, name); | ||
| 581 | write_unlock(&resource_lock); | ||
| 582 | } | ||
| 583 | |||
| 519 | EXPORT_SYMBOL(adjust_resource); | 584 | EXPORT_SYMBOL(adjust_resource); |
| 520 | 585 | ||
| 521 | /** | 586 | /** |
| @@ -562,33 +627,34 @@ struct resource * __request_region(struct resource *parent, | |||
| 562 | { | 627 | { |
| 563 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); | 628 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); |
| 564 | 629 | ||
| 565 | if (res) { | 630 | if (!res) |
| 566 | res->name = name; | 631 | return NULL; |
| 567 | res->start = start; | ||
| 568 | res->end = start + n - 1; | ||
| 569 | res->flags = IORESOURCE_BUSY; | ||
| 570 | 632 | ||
| 571 | write_lock(&resource_lock); | 633 | res->name = name; |
| 634 | res->start = start; | ||
| 635 | res->end = start + n - 1; | ||
| 636 | res->flags = IORESOURCE_BUSY; | ||
| 572 | 637 | ||
| 573 | for (;;) { | 638 | write_lock(&resource_lock); |
| 574 | struct resource *conflict; | ||
| 575 | 639 | ||
| 576 | conflict = __request_resource(parent, res); | 640 | for (;;) { |
| 577 | if (!conflict) | 641 | struct resource *conflict; |
| 578 | break; | ||
| 579 | if (conflict != parent) { | ||
| 580 | parent = conflict; | ||
| 581 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
| 582 | continue; | ||
| 583 | } | ||
| 584 | 642 | ||
| 585 | /* Uhhuh, that didn't work out.. */ | 643 | conflict = __request_resource(parent, res); |
| 586 | kfree(res); | 644 | if (!conflict) |
| 587 | res = NULL; | ||
| 588 | break; | 645 | break; |
| 646 | if (conflict != parent) { | ||
| 647 | parent = conflict; | ||
| 648 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
| 649 | continue; | ||
| 589 | } | 650 | } |
| 590 | write_unlock(&resource_lock); | 651 | |
| 652 | /* Uhhuh, that didn't work out.. */ | ||
| 653 | kfree(res); | ||
| 654 | res = NULL; | ||
| 655 | break; | ||
| 591 | } | 656 | } |
| 657 | write_unlock(&resource_lock); | ||
| 592 | return res; | 658 | return res; |
| 593 | } | 659 | } |
| 594 | EXPORT_SYMBOL(__request_region); | 660 | EXPORT_SYMBOL(__request_region); |
| @@ -763,3 +829,41 @@ static int __init reserve_setup(char *str) | |||
| 763 | } | 829 | } |
| 764 | 830 | ||
| 765 | __setup("reserve=", reserve_setup); | 831 | __setup("reserve=", reserve_setup); |
| 832 | |||
| 833 | /* | ||
| 834 | * Check if the requested addr and size spans more than any slot in the | ||
| 835 | * iomem resource tree. | ||
| 836 | */ | ||
| 837 | int iomem_map_sanity_check(resource_size_t addr, unsigned long size) | ||
| 838 | { | ||
| 839 | struct resource *p = &iomem_resource; | ||
| 840 | int err = 0; | ||
| 841 | loff_t l; | ||
| 842 | |||
| 843 | read_lock(&resource_lock); | ||
| 844 | for (p = p->child; p ; p = r_next(NULL, p, &l)) { | ||
| 845 | /* | ||
| 846 | * We can probably skip the resources without | ||
| 847 | * IORESOURCE_IO attribute? | ||
| 848 | */ | ||
| 849 | if (p->start >= addr + size) | ||
| 850 | continue; | ||
| 851 | if (p->end < addr) | ||
| 852 | continue; | ||
| 853 | if (PFN_DOWN(p->start) <= PFN_DOWN(addr) && | ||
| 854 | PFN_DOWN(p->end) >= PFN_DOWN(addr + size - 1)) | ||
| 855 | continue; | ||
| 856 | printk(KERN_WARNING "resource map sanity check conflict: " | ||
| 857 | "0x%llx 0x%llx 0x%llx 0x%llx %s\n", | ||
| 858 | (unsigned long long)addr, | ||
| 859 | (unsigned long long)(addr + size - 1), | ||
| 860 | (unsigned long long)p->start, | ||
| 861 | (unsigned long long)p->end, | ||
| 862 | p->name); | ||
| 863 | err = -1; | ||
| 864 | break; | ||
| 865 | } | ||
| 866 | read_unlock(&resource_lock); | ||
| 867 | |||
| 868 | return err; | ||
| 869 | } | ||
