diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
-rw-r--r-- | drivers/pci/setup-bus.c | 83 |
1 files changed, 82 insertions, 1 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 75d43eb3784..7757c002690 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -99,6 +99,24 @@ static void add_to_failed_list(struct resource_list_x *head, 0 /* dont care */); } +static void remove_from_list(struct resource_list_x *realloc_head, + struct resource *res) +{ + struct resource_list_x *prev, *tmp, *list; + + prev = realloc_head; + for (list = realloc_head->next; list;) { + if (list->res != res) { + prev = list; + list = list->next; + continue; + } + tmp = list; + prev->next = list = list->next; + kfree(tmp); + } +} + static resource_size_t get_res_add_size(struct resource_list_x *realloc_head, struct resource *res) { @@ -108,8 +126,13 @@ static resource_size_t get_res_add_size(struct resource_list_x *realloc_head, for (list = realloc_head->next; list && list->res != res; list = list->next) ; - if (list) + + if (list) { + dev_printk(KERN_DEBUG, &list->dev->dev, + "%pR get_res_add_size add_size %llx\n", + list->res, (unsigned long long)list->add_size); return list->add_size; + } return 0; } @@ -238,6 +261,64 @@ static void __assign_resources_sorted(struct resource_list *head, struct resource_list_x *realloc_head, struct resource_list_x *fail_head) { + /* + * Should not assign requested resources at first. + * they could be adjacent, so later reassign can not reallocate + * them one by one in parent resource window. + * Try to assign requested + add_size at begining + * if could do that, could get out early. + * if could not do that, we still try to assign requested at first, + * then try to reassign add_size for some resources. + */ + struct resource_list_x save_head, local_fail_head, *list; + struct resource_list *l; + + /* Check if optional add_size is there */ + if (!realloc_head || !realloc_head->next) + goto requested_and_reassign; + + /* Save original start, end, flags etc at first */ + save_head.next = NULL; + for (l = head->next; l; l = l->next) + if (add_to_list(&save_head, l->dev, l->res, 0, 0)) { + free_list(resource_list_x, &save_head); + goto requested_and_reassign; + } + + /* Update res in head list with add_size in realloc_head list */ + for (l = head->next; l; l = l->next) + l->res->end += get_res_add_size(realloc_head, l->res); + + /* Try updated head list with add_size added */ + local_fail_head.next = NULL; + assign_requested_resources_sorted(head, &local_fail_head); + + /* all assigned with add_size ? */ + if (!local_fail_head.next) { + /* Remove head list from realloc_head list */ + for (l = head->next; l; l = l->next) + remove_from_list(realloc_head, l->res); + free_list(resource_list_x, &save_head); + free_list(resource_list, head); + return; + } + + free_list(resource_list_x, &local_fail_head); + /* Release assigned resource */ + for (l = head->next; l; l = l->next) + if (l->res->parent) + release_resource(l->res); + /* Restore start/end/flags from saved list */ + for (list = save_head.next; list; list = list->next) { + struct resource *res = list->res; + + res->start = list->start; + res->end = list->end; + res->flags = list->flags; + } + free_list(resource_list_x, &save_head); + +requested_and_reassign: /* Satisfy the must-have resource requests */ assign_requested_resources_sorted(head, fail_head); |