On Thu, Dec 4, 2014 at 2:24 PM, Yinghai Lu <yinghai@xxxxxxxxxx> wrote: > On Thu, Dec 4, 2014 at 8:37 AM, Bjorn Helgaas <bhelgaas@xxxxxxxxxx> wrote: >>> pci=realloc should workaround the problem, as it would take control of >>> the bridge mmio BAR, and allocate more range for mmio pref under it. >> >> I don't think pci=realloc is the answer because >> >> 1) It's a poor user experience. We should be able to handle this >> automatically, without asking the user to do anything. > > we can detect that when bridge support 64bit mmio pref, and reject > child device pref that only > support mmio pref non-b64bit. > >> >> 2) It puts the radeon framebuffer in the non-prefetchable window, and we >> should be able to keep it in the prefetchable window. > > clear bridge 64bit flags if all children does not support that? > > even that slot supports hotplug. Please check if attached patch is good for v3.18. --- drivers/pci/setup-bus.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) Index: linux-2.6/drivers/pci/setup-bus.c =================================================================== --- linux-2.6.orig/drivers/pci/setup-bus.c +++ linux-2.6/drivers/pci/setup-bus.c @@ -1118,6 +1118,52 @@ handle_done: ; } +static void pbus_check_mem64(struct pci_bus *bus, unsigned long mask, + unsigned long type, struct list_head *realloc_head) +{ + struct pci_dev *dev; + resource_size_t align; + resource_size_t aligns[18]; /* Alignments from 1Mb to 128Gb */ + int order; + unsigned int mem64_mask = 0; + struct resource *b_res = find_free_bus_resource(bus, mask, type); + + if (!b_res || !(b_res->flags & IORESOURCE_MEM_64)) + return; + + memset(aligns, 0, sizeof(aligns)); + + mem64_mask = IORESOURCE_MEM_64; + b_res->flags &= ~IORESOURCE_MEM_64; + + list_for_each_entry(dev, &bus->devices, bus_list) { + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (r->parent || (r->flags & mask) != type) + continue; +#ifdef CONFIG_PCI_IOV + if (realloc_head && i >= PCI_IOV_RESOURCES && + i <= PCI_IOV_RESOURCE_END) + continue; +#endif + align = pci_resource_alignment(dev, r); + order = __ffs(align) - 20; + if (order < 0) + order = 0; + if (order >= ARRAY_SIZE(aligns)) + continue; + + if (i != PCI_ROM_RESOURCE) + mem64_mask &= r->flags & IORESOURCE_MEM_64; + } + } + + b_res->flags |= mem64_mask; +} + void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; @@ -1171,6 +1217,10 @@ void __pci_bus_size_bridges(struct pci_b b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES]; mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; + + if (b_res[2].flags & IORESOURCE_MEM_64) + pbus_check_mem64(bus, prefmask, prefmask, realloc_head); + if (b_res[2].flags & IORESOURCE_MEM_64) { prefmask |= IORESOURCE_MEM_64; ret = pbus_size_mem(bus, prefmask, prefmask,
--- drivers/pci/setup-bus.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) Index: linux-2.6/drivers/pci/setup-bus.c =================================================================== --- linux-2.6.orig/drivers/pci/setup-bus.c +++ linux-2.6/drivers/pci/setup-bus.c @@ -1118,6 +1118,52 @@ handle_done: ; } +static void pbus_check_mem64(struct pci_bus *bus, unsigned long mask, + unsigned long type, struct list_head *realloc_head) +{ + struct pci_dev *dev; + resource_size_t align; + resource_size_t aligns[18]; /* Alignments from 1Mb to 128Gb */ + int order; + unsigned int mem64_mask = 0; + struct resource *b_res = find_free_bus_resource(bus, mask, type); + + if (!b_res || !(b_res->flags & IORESOURCE_MEM_64)) + return; + + memset(aligns, 0, sizeof(aligns)); + + mem64_mask = IORESOURCE_MEM_64; + b_res->flags &= ~IORESOURCE_MEM_64; + + list_for_each_entry(dev, &bus->devices, bus_list) { + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (r->parent || (r->flags & mask) != type) + continue; +#ifdef CONFIG_PCI_IOV + if (realloc_head && i >= PCI_IOV_RESOURCES && + i <= PCI_IOV_RESOURCE_END) + continue; +#endif + align = pci_resource_alignment(dev, r); + order = __ffs(align) - 20; + if (order < 0) + order = 0; + if (order >= ARRAY_SIZE(aligns)) + continue; + + if (i != PCI_ROM_RESOURCE) + mem64_mask &= r->flags & IORESOURCE_MEM_64; + } + } + + b_res->flags |= mem64_mask; +} + void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; @@ -1171,6 +1217,10 @@ void __pci_bus_size_bridges(struct pci_b b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES]; mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; + + if (b_res[2].flags & IORESOURCE_MEM_64) + pbus_check_mem64(bus, prefmask, prefmask, realloc_head); + if (b_res[2].flags & IORESOURCE_MEM_64) { prefmask |= IORESOURCE_MEM_64; ret = pbus_size_mem(bus, prefmask, prefmask,