|
@@ -99,8 +99,8 @@ static void remove_from_list(struct list_head *head,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static resource_size_t get_res_add_size(struct list_head *head,
|
|
|
- struct resource *res)
|
|
|
+static struct pci_dev_resource *res_to_dev_res(struct list_head *head,
|
|
|
+ struct resource *res)
|
|
|
{
|
|
|
struct pci_dev_resource *dev_res;
|
|
|
|
|
@@ -109,17 +109,37 @@ static resource_size_t get_res_add_size(struct list_head *head,
|
|
|
int idx = res - &dev_res->dev->resource[0];
|
|
|
|
|
|
dev_printk(KERN_DEBUG, &dev_res->dev->dev,
|
|
|
- "res[%d]=%pR get_res_add_size add_size %llx\n",
|
|
|
+ "res[%d]=%pR res_to_dev_res add_size %llx min_align %llx\n",
|
|
|
idx, dev_res->res,
|
|
|
- (unsigned long long)dev_res->add_size);
|
|
|
+ (unsigned long long)dev_res->add_size,
|
|
|
+ (unsigned long long)dev_res->min_align);
|
|
|
|
|
|
- return dev_res->add_size;
|
|
|
+ return dev_res;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
+static resource_size_t get_res_add_size(struct list_head *head,
|
|
|
+ struct resource *res)
|
|
|
+{
|
|
|
+ struct pci_dev_resource *dev_res;
|
|
|
+
|
|
|
+ dev_res = res_to_dev_res(head, res);
|
|
|
+ return dev_res ? dev_res->add_size : 0;
|
|
|
+}
|
|
|
+
|
|
|
+static resource_size_t get_res_add_align(struct list_head *head,
|
|
|
+ struct resource *res)
|
|
|
+{
|
|
|
+ struct pci_dev_resource *dev_res;
|
|
|
+
|
|
|
+ dev_res = res_to_dev_res(head, res);
|
|
|
+ return dev_res ? dev_res->min_align : 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* Sort resources by alignment */
|
|
|
static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
|
|
|
{
|
|
@@ -215,7 +235,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
|
|
|
struct resource *res;
|
|
|
struct pci_dev_resource *add_res, *tmp;
|
|
|
struct pci_dev_resource *dev_res;
|
|
|
- resource_size_t add_size;
|
|
|
+ resource_size_t add_size, align;
|
|
|
int idx;
|
|
|
|
|
|
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,
|
|
|
|
|
|
idx = res - &add_res->dev->resource[0];
|
|
|
add_size = add_res->add_size;
|
|
|
+ align = add_res->min_align;
|
|
|
if (!resource_size(res)) {
|
|
|
- res->start = add_res->start;
|
|
|
+ res->start = align;
|
|
|
res->end = res->start + add_size - 1;
|
|
|
if (pci_assign_resource(add_res->dev, idx))
|
|
|
reset_resource(res);
|
|
|
} else {
|
|
|
- resource_size_t align = add_res->min_align;
|
|
|
res->flags |= add_res->flags &
|
|
|
(IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
|
|
|
if (pci_reassign_resource(add_res->dev, idx,
|
|
@@ -368,8 +388,9 @@ static void __assign_resources_sorted(struct list_head *head,
|
|
|
LIST_HEAD(save_head);
|
|
|
LIST_HEAD(local_fail_head);
|
|
|
struct pci_dev_resource *save_res;
|
|
|
- struct pci_dev_resource *dev_res, *tmp_res;
|
|
|
+ struct pci_dev_resource *dev_res, *tmp_res, *dev_res2;
|
|
|
unsigned long fail_type;
|
|
|
+ resource_size_t add_align, align;
|
|
|
|
|
|
/* Check if optional add_size is there */
|
|
|
if (!realloc_head || list_empty(realloc_head))
|
|
@@ -384,10 +405,44 @@ static void __assign_resources_sorted(struct list_head *head,
|
|
|
}
|
|
|
|
|
|
/* Update res in head list with add_size in realloc_head list */
|
|
|
- list_for_each_entry(dev_res, head, list)
|
|
|
+ list_for_each_entry_safe(dev_res, tmp_res, head, list) {
|
|
|
dev_res->res->end += get_res_add_size(realloc_head,
|
|
|
dev_res->res);
|
|
|
|
|
|
+ /*
|
|
|
+ * There are two kinds of additional resources in the list:
|
|
|
+ * 1. bridge resource -- IORESOURCE_STARTALIGN
|
|
|
+ * 2. SR-IOV resource -- IORESOURCE_SIZEALIGN
|
|
|
+ * Here just fix the additional alignment for bridge
|
|
|
+ */
|
|
|
+ if (!(dev_res->res->flags & IORESOURCE_STARTALIGN))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ add_align = get_res_add_align(realloc_head, dev_res->res);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The "head" list is sorted by the alignment to make sure
|
|
|
+ * resources with bigger alignment will be assigned first.
|
|
|
+ * After we change the alignment of a dev_res in "head" list,
|
|
|
+ * we need to reorder the list by alignment to make it
|
|
|
+ * consistent.
|
|
|
+ */
|
|
|
+ if (add_align > dev_res->res->start) {
|
|
|
+ dev_res->res->start = add_align;
|
|
|
+ dev_res->res->end = add_align +
|
|
|
+ resource_size(dev_res->res);
|
|
|
+
|
|
|
+ list_for_each_entry(dev_res2, head, list) {
|
|
|
+ align = pci_resource_alignment(dev_res2->dev,
|
|
|
+ dev_res2->res);
|
|
|
+ if (add_align > align)
|
|
|
+ list_move_tail(&dev_res->list,
|
|
|
+ &dev_res2->list);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
/* Try updated head list with add_size added */
|
|
|
assign_requested_resources_sorted(head, &local_fail_head);
|
|
|
|
|
@@ -962,6 +1017,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
struct resource *b_res = find_free_bus_resource(bus,
|
|
|
mask | IORESOURCE_PREFETCH, type);
|
|
|
resource_size_t children_add_size = 0;
|
|
|
+ resource_size_t children_add_align = 0;
|
|
|
+ resource_size_t add_align = 0;
|
|
|
|
|
|
if (!b_res)
|
|
|
return -ENOSPC;
|
|
@@ -986,6 +1043,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
/* put SRIOV requested res to the optional list */
|
|
|
if (realloc_head && i >= PCI_IOV_RESOURCES &&
|
|
|
i <= PCI_IOV_RESOURCE_END) {
|
|
|
+ add_align = max(pci_resource_alignment(dev, r), add_align);
|
|
|
r->end = r->start - 1;
|
|
|
add_to_list(realloc_head, dev, r, r_size, 0/* don't care */);
|
|
|
children_add_size += r_size;
|
|
@@ -1016,19 +1074,23 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
if (order > max_order)
|
|
|
max_order = order;
|
|
|
|
|
|
- if (realloc_head)
|
|
|
+ if (realloc_head) {
|
|
|
children_add_size += get_res_add_size(realloc_head, r);
|
|
|
+ children_add_align = get_res_add_align(realloc_head, r);
|
|
|
+ add_align = max(add_align, children_add_align);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
min_align = calculate_mem_align(aligns, max_order);
|
|
|
min_align = max(min_align, window_alignment(bus, b_res->flags));
|
|
|
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
|
|
|
+ add_align = max(min_align, add_align);
|
|
|
if (children_add_size > add_size)
|
|
|
add_size = children_add_size;
|
|
|
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
|
|
|
calculate_memsize(size, min_size, add_size,
|
|
|
- resource_size(b_res), min_align);
|
|
|
+ resource_size(b_res), add_align);
|
|
|
if (!size0 && !size1) {
|
|
|
if (b_res->start || b_res->end)
|
|
|
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,
|
|
|
b_res->end = size0 + min_align - 1;
|
|
|
b_res->flags |= IORESOURCE_STARTALIGN;
|
|
|
if (size1 > size0 && realloc_head) {
|
|
|
- add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
|
|
|
- dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n",
|
|
|
+ add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align);
|
|
|
+ dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx add_align %llx\n",
|
|
|
b_res, &bus->busn_res,
|
|
|
- (unsigned long long)size1-size0);
|
|
|
+ (unsigned long long) (size1 - size0),
|
|
|
+ (unsigned long long) add_align);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|