diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 95 |
1 files changed, 79 insertions, 16 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index e3e17f3c0f0f..6603d401bb7c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c | |||
| @@ -99,8 +99,8 @@ static void remove_from_list(struct list_head *head, | |||
| 99 | } | 99 | } |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | static resource_size_t get_res_add_size(struct list_head *head, | 102 | static struct pci_dev_resource *res_to_dev_res(struct list_head *head, |
| 103 | struct resource *res) | 103 | struct resource *res) |
| 104 | { | 104 | { |
| 105 | struct pci_dev_resource *dev_res; | 105 | struct pci_dev_resource *dev_res; |
| 106 | 106 | ||
| @@ -109,17 +109,37 @@ static resource_size_t get_res_add_size(struct list_head *head, | |||
| 109 | int idx = res - &dev_res->dev->resource[0]; | 109 | int idx = res - &dev_res->dev->resource[0]; |
| 110 | 110 | ||
| 111 | dev_printk(KERN_DEBUG, &dev_res->dev->dev, | 111 | dev_printk(KERN_DEBUG, &dev_res->dev->dev, |
| 112 | "res[%d]=%pR get_res_add_size add_size %llx\n", | 112 | "res[%d]=%pR res_to_dev_res add_size %llx min_align %llx\n", |
| 113 | idx, dev_res->res, | 113 | idx, dev_res->res, |
| 114 | (unsigned long long)dev_res->add_size); | 114 | (unsigned long long)dev_res->add_size, |
| 115 | (unsigned long long)dev_res->min_align); | ||
| 115 | 116 | ||
| 116 | return dev_res->add_size; | 117 | return dev_res; |
| 117 | } | 118 | } |
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | return 0; | 121 | return NULL; |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 124 | static resource_size_t get_res_add_size(struct list_head *head, | ||
| 125 | struct resource *res) | ||
| 126 | { | ||
| 127 | struct pci_dev_resource *dev_res; | ||
| 128 | |||
| 129 | dev_res = res_to_dev_res(head, res); | ||
| 130 | return dev_res ? dev_res->add_size : 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | static resource_size_t get_res_add_align(struct list_head *head, | ||
| 134 | struct resource *res) | ||
| 135 | { | ||
| 136 | struct pci_dev_resource *dev_res; | ||
| 137 | |||
| 138 | dev_res = res_to_dev_res(head, res); | ||
| 139 | return dev_res ? dev_res->min_align : 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | |||
| 123 | /* Sort resources by alignment */ | 143 | /* Sort resources by alignment */ |
| 124 | static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) | 144 | static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) |
| 125 | { | 145 | { |
| @@ -215,7 +235,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, | |||
| 215 | struct resource *res; | 235 | struct resource *res; |
| 216 | struct pci_dev_resource *add_res, *tmp; | 236 | struct pci_dev_resource *add_res, *tmp; |
| 217 | struct pci_dev_resource *dev_res; | 237 | struct pci_dev_resource *dev_res; |
| 218 | resource_size_t add_size; | 238 | resource_size_t add_size, align; |
| 219 | int idx; | 239 | int idx; |
| 220 | 240 | ||
| 221 | list_for_each_entry_safe(add_res, tmp, realloc_head, list) { | 241 | list_for_each_entry_safe(add_res, tmp, realloc_head, list) { |
| @@ -238,13 +258,13 @@ static void reassign_resources_sorted(struct list_head *realloc_head, | |||
| 238 | 258 | ||
| 239 | idx = res - &add_res->dev->resource[0]; | 259 | idx = res - &add_res->dev->resource[0]; |
| 240 | add_size = add_res->add_size; | 260 | add_size = add_res->add_size; |
| 261 | align = add_res->min_align; | ||
| 241 | if (!resource_size(res)) { | 262 | if (!resource_size(res)) { |
| 242 | res->start = add_res->start; | 263 | res->start = align; |
| 243 | res->end = res->start + add_size - 1; | 264 | res->end = res->start + add_size - 1; |
| 244 | if (pci_assign_resource(add_res->dev, idx)) | 265 | if (pci_assign_resource(add_res->dev, idx)) |
| 245 | reset_resource(res); | 266 | reset_resource(res); |
| 246 | } else { | 267 | } else { |
| 247 | resource_size_t align = add_res->min_align; | ||
| 248 | res->flags |= add_res->flags & | 268 | res->flags |= add_res->flags & |
| 249 | (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); | 269 | (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); |
| 250 | if (pci_reassign_resource(add_res->dev, idx, | 270 | if (pci_reassign_resource(add_res->dev, idx, |
| @@ -368,8 +388,9 @@ static void __assign_resources_sorted(struct list_head *head, | |||
| 368 | LIST_HEAD(save_head); | 388 | LIST_HEAD(save_head); |
| 369 | LIST_HEAD(local_fail_head); | 389 | LIST_HEAD(local_fail_head); |
| 370 | struct pci_dev_resource *save_res; | 390 | struct pci_dev_resource *save_res; |
| 371 | struct pci_dev_resource *dev_res, *tmp_res; | 391 | struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; |
| 372 | unsigned long fail_type; | 392 | unsigned long fail_type; |
| 393 | resource_size_t add_align, align; | ||
| 373 | 394 | ||
| 374 | /* Check if optional add_size is there */ | 395 | /* Check if optional add_size is there */ |
| 375 | if (!realloc_head || list_empty(realloc_head)) | 396 | if (!realloc_head || list_empty(realloc_head)) |
| @@ -384,10 +405,44 @@ static void __assign_resources_sorted(struct list_head *head, | |||
| 384 | } | 405 | } |
| 385 | 406 | ||
| 386 | /* Update res in head list with add_size in realloc_head list */ | 407 | /* Update res in head list with add_size in realloc_head list */ |
| 387 | list_for_each_entry(dev_res, head, list) | 408 | list_for_each_entry_safe(dev_res, tmp_res, head, list) { |
| 388 | dev_res->res->end += get_res_add_size(realloc_head, | 409 | dev_res->res->end += get_res_add_size(realloc_head, |
| 389 | dev_res->res); | 410 | dev_res->res); |
| 390 | 411 | ||
| 412 | /* | ||
| 413 | * There are two kinds of additional resources in the list: | ||
| 414 | * 1. bridge resource -- IORESOURCE_STARTALIGN | ||
| 415 | * 2. SR-IOV resource -- IORESOURCE_SIZEALIGN | ||
| 416 | * Here just fix the additional alignment for bridge | ||
| 417 | */ | ||
| 418 | if (!(dev_res->res->flags & IORESOURCE_STARTALIGN)) | ||
| 419 | continue; | ||
| 420 | |||
| 421 | add_align = get_res_add_align(realloc_head, dev_res->res); | ||
| 422 | |||
| 423 | /* | ||
| 424 | * The "head" list is sorted by the alignment to make sure | ||
| 425 | * resources with bigger alignment will be assigned first. | ||
| 426 | * After we change the alignment of a dev_res in "head" list, | ||
| 427 | * we need to reorder the list by alignment to make it | ||
| 428 | * consistent. | ||
| 429 | */ | ||
| 430 | if (add_align > dev_res->res->start) { | ||
| 431 | dev_res->res->start = add_align; | ||
| 432 | dev_res->res->end = add_align + | ||
| 433 | resource_size(dev_res->res); | ||
| 434 | |||
| 435 | list_for_each_entry(dev_res2, head, list) { | ||
| 436 | align = pci_resource_alignment(dev_res2->dev, | ||
| 437 | dev_res2->res); | ||
| 438 | if (add_align > align) | ||
| 439 | list_move_tail(&dev_res->list, | ||
| 440 | &dev_res2->list); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | } | ||
| 445 | |||
| 391 | /* Try updated head list with add_size added */ | 446 | /* Try updated head list with add_size added */ |
| 392 | assign_requested_resources_sorted(head, &local_fail_head); | 447 | assign_requested_resources_sorted(head, &local_fail_head); |
| 393 | 448 | ||
| @@ -962,6 +1017,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, | |||
| 962 | struct resource *b_res = find_free_bus_resource(bus, | 1017 | struct resource *b_res = find_free_bus_resource(bus, |
| 963 | mask | IORESOURCE_PREFETCH, type); | 1018 | mask | IORESOURCE_PREFETCH, type); |
| 964 | resource_size_t children_add_size = 0; | 1019 | resource_size_t children_add_size = 0; |
| 1020 | resource_size_t children_add_align = 0; | ||
| 1021 | resource_size_t add_align = 0; | ||
| 965 | 1022 | ||
| 966 | if (!b_res) | 1023 | if (!b_res) |
| 967 | return -ENOSPC; | 1024 | return -ENOSPC; |
| @@ -986,6 +1043,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, | |||
| 986 | /* put SRIOV requested res to the optional list */ | 1043 | /* put SRIOV requested res to the optional list */ |
| 987 | if (realloc_head && i >= PCI_IOV_RESOURCES && | 1044 | if (realloc_head && i >= PCI_IOV_RESOURCES && |
| 988 | i <= PCI_IOV_RESOURCE_END) { | 1045 | i <= PCI_IOV_RESOURCE_END) { |
| 1046 | add_align = max(pci_resource_alignment(dev, r), add_align); | ||
| 989 | r->end = r->start - 1; | 1047 | r->end = r->start - 1; |
| 990 | add_to_list(realloc_head, dev, r, r_size, 0/* don't care */); | 1048 | add_to_list(realloc_head, dev, r, r_size, 0/* don't care */); |
| 991 | children_add_size += r_size; | 1049 | children_add_size += r_size; |
| @@ -1016,19 +1074,23 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, | |||
| 1016 | if (order > max_order) | 1074 | if (order > max_order) |
| 1017 | max_order = order; | 1075 | max_order = order; |
| 1018 | 1076 | ||
| 1019 | if (realloc_head) | 1077 | if (realloc_head) { |
| 1020 | children_add_size += get_res_add_size(realloc_head, r); | 1078 | children_add_size += get_res_add_size(realloc_head, r); |
| 1079 | children_add_align = get_res_add_align(realloc_head, r); | ||
| 1080 | add_align = max(add_align, children_add_align); | ||
| 1081 | } | ||
| 1021 | } | 1082 | } |
| 1022 | } | 1083 | } |
| 1023 | 1084 | ||
| 1024 | min_align = calculate_mem_align(aligns, max_order); | 1085 | min_align = calculate_mem_align(aligns, max_order); |
| 1025 | min_align = max(min_align, window_alignment(bus, b_res->flags)); | 1086 | min_align = max(min_align, window_alignment(bus, b_res->flags)); |
| 1026 | size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); | 1087 | size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); |
| 1088 | add_align = max(min_align, add_align); | ||
| 1027 | if (children_add_size > add_size) | 1089 | if (children_add_size > add_size) |
| 1028 | add_size = children_add_size; | 1090 | add_size = children_add_size; |
| 1029 | size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : | 1091 | size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : |
| 1030 | calculate_memsize(size, min_size, add_size, | 1092 | calculate_memsize(size, min_size, add_size, |
| 1031 | resource_size(b_res), min_align); | 1093 | resource_size(b_res), add_align); |
| 1032 | if (!size0 && !size1) { | 1094 | if (!size0 && !size1) { |
| 1033 | if (b_res->start || b_res->end) | 1095 | if (b_res->start || b_res->end) |
| 1034 | dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n", | 1096 | dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n", |
| @@ -1040,10 +1102,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, | |||
| 1040 | b_res->end = size0 + min_align - 1; | 1102 | b_res->end = size0 + min_align - 1; |
| 1041 | b_res->flags |= IORESOURCE_STARTALIGN; | 1103 | b_res->flags |= IORESOURCE_STARTALIGN; |
| 1042 | if (size1 > size0 && realloc_head) { | 1104 | if (size1 > size0 && realloc_head) { |
| 1043 | add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); | 1105 | add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); |
| 1044 | dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n", | 1106 | dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx add_align %llx\n", |
| 1045 | b_res, &bus->busn_res, | 1107 | b_res, &bus->busn_res, |
| 1046 | (unsigned long long)size1-size0); | 1108 | (unsigned long long) (size1 - size0), |
| 1109 | (unsigned long long) add_align); | ||
| 1047 | } | 1110 | } |
| 1048 | return 0; | 1111 | return 0; |
| 1049 | } | 1112 | } |
