diff options
Diffstat (limited to 'kernel/resource.c')
-rw-r--r-- | kernel/resource.c | 150 |
1 files changed, 126 insertions, 24 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index 03d796c1b2e9..4089d12af6e0 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 | { |
@@ -516,6 +516,70 @@ int adjust_resource(struct resource *res, resource_size_t start, resource_size_t | |||
516 | return result; | 516 | return result; |
517 | } | 517 | } |
518 | 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 | |||
519 | EXPORT_SYMBOL(adjust_resource); | 583 | EXPORT_SYMBOL(adjust_resource); |
520 | 584 | ||
521 | /** | 585 | /** |
@@ -562,33 +626,34 @@ struct resource * __request_region(struct resource *parent, | |||
562 | { | 626 | { |
563 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); | 627 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); |
564 | 628 | ||
565 | if (res) { | 629 | if (!res) |
566 | res->name = name; | 630 | return NULL; |
567 | res->start = start; | ||
568 | res->end = start + n - 1; | ||
569 | res->flags = IORESOURCE_BUSY; | ||
570 | 631 | ||
571 | write_lock(&resource_lock); | 632 | res->name = name; |
633 | res->start = start; | ||
634 | res->end = start + n - 1; | ||
635 | res->flags = IORESOURCE_BUSY; | ||
572 | 636 | ||
573 | for (;;) { | 637 | write_lock(&resource_lock); |
574 | struct resource *conflict; | ||
575 | 638 | ||
576 | conflict = __request_resource(parent, res); | 639 | for (;;) { |
577 | if (!conflict) | 640 | struct resource *conflict; |
578 | break; | ||
579 | if (conflict != parent) { | ||
580 | parent = conflict; | ||
581 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
582 | continue; | ||
583 | } | ||
584 | 641 | ||
585 | /* Uhhuh, that didn't work out.. */ | 642 | conflict = __request_resource(parent, res); |
586 | kfree(res); | 643 | if (!conflict) |
587 | res = NULL; | ||
588 | break; | 644 | break; |
645 | if (conflict != parent) { | ||
646 | parent = conflict; | ||
647 | if (!(conflict->flags & IORESOURCE_BUSY)) | ||
648 | continue; | ||
589 | } | 649 | } |
590 | write_unlock(&resource_lock); | 650 | |
651 | /* Uhhuh, that didn't work out.. */ | ||
652 | kfree(res); | ||
653 | res = NULL; | ||
654 | break; | ||
591 | } | 655 | } |
656 | write_unlock(&resource_lock); | ||
592 | return res; | 657 | return res; |
593 | } | 658 | } |
594 | EXPORT_SYMBOL(__request_region); | 659 | EXPORT_SYMBOL(__request_region); |
@@ -763,3 +828,40 @@ static int __init reserve_setup(char *str) | |||
763 | } | 828 | } |
764 | 829 | ||
765 | __setup("reserve=", reserve_setup); | 830 | __setup("reserve=", reserve_setup); |
831 | |||
832 | /* | ||
833 | * Check if the requested addr and size spans more than any slot in the | ||
834 | * iomem resource tree. | ||
835 | */ | ||
836 | int iomem_map_sanity_check(resource_size_t addr, unsigned long size) | ||
837 | { | ||
838 | struct resource *p = &iomem_resource; | ||
839 | int err = 0; | ||
840 | loff_t l; | ||
841 | |||
842 | read_lock(&resource_lock); | ||
843 | for (p = p->child; p ; p = r_next(NULL, p, &l)) { | ||
844 | /* | ||
845 | * We can probably skip the resources without | ||
846 | * IORESOURCE_IO attribute? | ||
847 | */ | ||
848 | if (p->start >= addr + size) | ||
849 | continue; | ||
850 | if (p->end < addr) | ||
851 | continue; | ||
852 | if (p->start <= addr && (p->end >= addr + size - 1)) | ||
853 | continue; | ||
854 | printk(KERN_WARNING "resource map sanity check conflict: " | ||
855 | "0x%llx 0x%llx 0x%llx 0x%llx %s\n", | ||
856 | (unsigned long long)addr, | ||
857 | (unsigned long long)(addr + size - 1), | ||
858 | (unsigned long long)p->start, | ||
859 | (unsigned long long)p->end, | ||
860 | p->name); | ||
861 | err = -1; | ||
862 | break; | ||
863 | } | ||
864 | read_unlock(&resource_lock); | ||
865 | |||
866 | return err; | ||
867 | } | ||