diff options
| -rw-r--r-- | include/linux/ioport.h | 3 | ||||
| -rw-r--r-- | kernel/resource.c | 64 |
2 files changed, 67 insertions, 0 deletions
diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 8d3b7a9afd17..fded376b94e3 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h | |||
| @@ -108,6 +108,9 @@ extern struct resource iomem_resource; | |||
| 108 | 108 | ||
| 109 | extern int request_resource(struct resource *root, struct resource *new); | 109 | extern int request_resource(struct resource *root, struct resource *new); |
| 110 | extern int release_resource(struct resource *new); | 110 | extern int release_resource(struct resource *new); |
| 111 | extern void reserve_region_with_split(struct resource *root, | ||
| 112 | resource_size_t start, resource_size_t end, | ||
| 113 | const char *name); | ||
| 111 | extern int insert_resource(struct resource *parent, struct resource *new); | 114 | extern int insert_resource(struct resource *parent, struct resource *new); |
| 112 | extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new); | 115 | extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new); |
| 113 | extern int allocate_resource(struct resource *root, struct resource *new, | 116 | extern int allocate_resource(struct resource *root, struct resource *new, |
diff --git a/kernel/resource.c b/kernel/resource.c index 03d796c1b2e9..fc59dcc4795b 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
| @@ -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 | /** |
