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 8169597e47cb..4fd0cacf7ca0 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 | } |