[PATCH] PCI: Try to find two continuous regions for child resource

Kai-Heng Feng kai.heng.feng at canonical.com
Wed Mar 31 19:53:33 AEDT 2021


On Tue, Mar 30, 2021 at 12:23 AM Bjorn Helgaas <helgaas at kernel.org> wrote:
>
> On Mon, Mar 29, 2021 at 04:47:59PM +0800, Kai-Heng Feng wrote:
> > Built-in grahpics on HP EliteDesk 805 G6 doesn't work because graphics
> > can't get the BAR it needs:
> > [    0.611504] pci_bus 0000:00: root bus resource [mem 0x10020200000-0x100303fffff window]
> > [    0.611505] pci_bus 0000:00: root bus resource [mem 0x10030400000-0x100401fffff window]
> > ...
> > [    0.638083] pci 0000:00:08.1:   bridge window [mem 0xd2000000-0xd23fffff]
> > [    0.638086] pci 0000:00:08.1:   bridge window [mem 0x10030000000-0x100401fffff 64bit pref]
> > [    0.962086] pci 0000:00:08.1: can't claim BAR 15 [mem 0x10030000000-0x100401fffff 64bit pref]: no compatible bridge window
> > [    0.962086] pci 0000:00:08.1: [mem 0x10030000000-0x100401fffff 64bit pref] clipped to [mem 0x10030000000-0x100303fffff 64bit pref]
> > [    0.962086] pci 0000:00:08.1:   bridge window [mem 0x10030000000-0x100303fffff 64bit pref]
> > [    0.962086] pci 0000:07:00.0: can't claim BAR 0 [mem 0x10030000000-0x1003fffffff 64bit pref]: no compatible bridge window
> > [    0.962086] pci 0000:07:00.0: can't claim BAR 2 [mem 0x10040000000-0x100401fffff 64bit pref]: no compatible bridge window
> >
> > However, the root bus has two continuous regions that can contain the
> > child resource requested.
> >
> > So try to find another parent region if two regions are continuous and
> > can contain child resource. This change makes the grahpics works on the
> > system in question.
>
> The BIOS description of PCI0 is interesting:
>
>   pci_bus 0000:00: root bus resource [mem 0x10000000000-0x100201fffff window]
>   pci_bus 0000:00: root bus resource [mem 0x10020200000-0x100303fffff window]
>   pci_bus 0000:00: root bus resource [mem 0x10030400000-0x100401fffff window]
>
> So the PCI0 _CRS apparently gave us:
>
>   [mem 0x10000000000-0x100201fffff] size 0x20200000 (512MB + 2MB)
>   [mem 0x10020200000-0x100303fffff] size 0x10200000 (256MB + 2MB)
>   [mem 0x10030400000-0x100401fffff] size 0x0fe00000 (254MB)
>
> These are all contiguous, so we'd have no problem if we coalesced them
> into a single window:
>
>   [mem 0x10000000000-0x100401fffff window] size 0x40200000 (1GB + 2MB)
>
> I think we currently keep these root bus resources separate because if
> we ever support _SRS for host bridges, the argument we give to _SRS
> must be exactly the same format as what we got from _CRS (see ACPI
> v6.3, sec 6.2.16, and pnpacpi_set_resources()).
>
> pnpacpi_encode_resources() is currently very simple-minded and copies
> each device resource back into a single _SRS entry.  But (1) we don't
> support _SRS for host bridges, and (2) if we ever do, we can make
> pnpacpi_encode_resources() smarter so it breaks things back up.
>
> So I think we should try to fix this by coalescing these adjacent
> resources from _CRS so we end up with a single root bus resource that
> covers all contiguous regions.

Thanks for the tip! Working on v2 patch.

>
> Typos, etc:
>   - No need for the timestamps; they're not relevant to the problem.
>   - s/grahpics/graphics/ (two occurrences above)
>   - s/continuous/contiguous/ (three occurrences above)

Will also update those in v2.

Kai-Heng

>
> > Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212013
> > Signed-off-by: Kai-Heng Feng <kai.heng.feng at canonical.com>
> > ---
> >  arch/microblaze/pci/pci-common.c |  4 +--
> >  arch/powerpc/kernel/pci-common.c |  8 ++---
> >  arch/sparc/kernel/pci.c          |  4 +--
> >  drivers/pci/pci.c                | 60 +++++++++++++++++++++++++++-----
> >  drivers/pci/setup-res.c          | 21 +++++++----
> >  drivers/pcmcia/rsrc_nonstatic.c  |  4 +--
> >  include/linux/pci.h              |  6 ++--
> >  7 files changed, 80 insertions(+), 27 deletions(-)
> >
> > diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c
> > index 557585f1be41..8e65832fb510 100644
> > --- a/arch/microblaze/pci/pci-common.c
> > +++ b/arch/microblaze/pci/pci-common.c
> > @@ -669,7 +669,7 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus)
> >  {
> >       struct pci_bus *b;
> >       int i;
> > -     struct resource *res, *pr;
> > +     struct resource *res, *pr = NULL;
> >
> >       pr_debug("PCI: Allocating bus resources for %04x:%02x...\n",
> >                pci_domain_nr(bus), bus->number);
> > @@ -688,7 +688,7 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus)
> >                        * and as such ensure proper re-allocation
> >                        * later.
> >                        */
> > -                     pr = pci_find_parent_resource(bus->self, res);
> > +                     pci_find_parent_resource(bus->self, res, &pr, NULL);
> >                       if (pr == res) {
> >                               /* this happens when the generic PCI
> >                                * code (wrongly) decides that this
> > diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
> > index 001e90cd8948..f865354b746d 100644
> > --- a/arch/powerpc/kernel/pci-common.c
> > +++ b/arch/powerpc/kernel/pci-common.c
> > @@ -1196,7 +1196,7 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus)
> >  {
> >       struct pci_bus *b;
> >       int i;
> > -     struct resource *res, *pr;
> > +     struct resource *res, *pr = NULL;
> >
> >       pr_debug("PCI: Allocating bus resources for %04x:%02x...\n",
> >                pci_domain_nr(bus), bus->number);
> > @@ -1213,7 +1213,7 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus)
> >                       pr = (res->flags & IORESOURCE_IO) ?
> >                               &ioport_resource : &iomem_resource;
> >               else {
> > -                     pr = pci_find_parent_resource(bus->self, res);
> > +                     pci_find_parent_resource(bus->self, res, &pr, NULL);
> >                       if (pr == res) {
> >                               /* this happens when the generic PCI
> >                                * code (wrongly) decides that this
> > @@ -1265,12 +1265,12 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus)
> >
> >  static inline void alloc_resource(struct pci_dev *dev, int idx)
> >  {
> > -     struct resource *pr, *r = &dev->resource[idx];
> > +     struct resource *pr = NULL, *r = &dev->resource[idx];
> >
> >       pr_debug("PCI: Allocating %s: Resource %d: %pR\n",
> >                pci_name(dev), idx, r);
> >
> > -     pr = pci_find_parent_resource(dev, r);
> > +     pci_find_parent_resource(dev, r, &pr, NULL);
> >       if (!pr || (pr->flags & IORESOURCE_UNSET) ||
> >           request_resource(pr, r) < 0) {
> >               printk(KERN_WARNING "PCI: Cannot allocate resource region %d"
> > diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
> > index 9c2b720bfd20..b4006798e4e1 100644
> > --- a/arch/sparc/kernel/pci.c
> > +++ b/arch/sparc/kernel/pci.c
> > @@ -621,7 +621,7 @@ static void pci_bus_register_of_sysfs(struct pci_bus *bus)
> >  static void pci_claim_legacy_resources(struct pci_dev *dev)
> >  {
> >       struct pci_bus_region region;
> > -     struct resource *p, *root, *conflict;
> > +     struct resource *p, *root = NULL, *conflict;
> >
> >       if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
> >               return;
> > @@ -637,7 +637,7 @@ static void pci_claim_legacy_resources(struct pci_dev *dev)
> >       region.end = region.start + 0x1ffffUL;
> >       pcibios_bus_to_resource(dev->bus, p, &region);
> >
> > -     root = pci_find_parent_resource(dev, p);
> > +     pci_find_parent_resource(dev, p, &root, NULL);
> >       if (!root) {
> >               pci_info(dev, "can't claim VGA legacy %pR: no compatible bridge window\n", p);
> >               goto err;
> > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > index 16a17215f633..abbcd2dcdc02 100644
> > --- a/drivers/pci/pci.c
> > +++ b/drivers/pci/pci.c
> > @@ -693,20 +693,25 @@ u8 pci_find_ht_capability(struct pci_dev *dev, int ht_cap)
> >  EXPORT_SYMBOL_GPL(pci_find_ht_capability);
> >
> >  /**
> > - * pci_find_parent_resource - return resource region of parent bus of given
> > + * pci_find_parent_resource - find resource region of parent bus of given
> >   *                         region
> >   * @dev: PCI device structure contains resources to be searched
> >   * @res: child resource record for which parent is sought
> > + * @first: the first region that contains the child resource
> > + * @second: the second region that combines with the first region to fully
> > + * contains the child resource
> >   *
> >   * For given resource region of given device, return the resource region of
> >   * parent bus the given region is contained in.
> >   */
> > -struct resource *pci_find_parent_resource(const struct pci_dev *dev,
> > -                                       struct resource *res)
> > +void pci_find_parent_resource(const struct pci_dev *dev,
> > +                                       struct resource *res,
> > +                                       struct resource **first,
> > +                                       struct resource **second)
> >  {
> >       const struct pci_bus *bus = dev->bus;
> >       struct resource *r;
> > -     int i;
> > +     int i, overlaps = 0;
> >
> >       pci_bus_for_each_resource(bus, r, i) {
> >               if (!r)
> > @@ -718,8 +723,10 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
> >                        * not, the allocator made a mistake.
> >                        */
> >                       if (r->flags & IORESOURCE_PREFETCH &&
> > -                         !(res->flags & IORESOURCE_PREFETCH))
> > -                             return NULL;
> > +                         !(res->flags & IORESOURCE_PREFETCH)) {
> > +                             *first = NULL;
> > +                             return;
> > +                     }
> >
> >                       /*
> >                        * If we're below a transparent bridge, there may
> > @@ -729,10 +736,47 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
> >                        * on pci_bus_for_each_resource() giving us those
> >                        * first.
> >                        */
> > -                     return r;
> > +                     *first = r;
> > +                     return;
> >               }
> >       }
> > -     return NULL;
> > +
> > +     if (!second)
> > +             return;
> > +
> > +     pci_bus_for_each_resource(bus, r, i) {
> > +             if (!r)
> > +                     continue;
> > +             if (resource_overlaps(r, res)) {
> > +                     if (r->flags & IORESOURCE_PREFETCH &&
> > +                         !(res->flags & IORESOURCE_PREFETCH))
> > +                             continue;
> > +
> > +                     if (!overlaps++)
> > +                             *first = r;
> > +                     else {
> > +                             *second = r;
> > +                             break;
> > +                     }
> > +             }
> > +     }
> > +
> > +     if (overlaps != 2)
> > +             goto out;
> > +
> > +     if ((*first)->start > (*second)->start)
> > +             swap(*first, *second);
> > +
> > +     if ((*first)->end + 1 != (*second)->start)
> > +             goto out;
> > +
> > +     if ((*first)->start <= res->start && (*second)->end >= res->end)
> > +             return;
> > +out:
> > +
> > +     *first = NULL;
> > +     *second = NULL;
> > +     return;
> >  }
> >  EXPORT_SYMBOL(pci_find_parent_resource);
> >
> > diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
> > index 7f1acb3918d0..e39615321d81 100644
> > --- a/drivers/pci/setup-res.c
> > +++ b/drivers/pci/setup-res.c
> > @@ -131,7 +131,7 @@ void pci_update_resource(struct pci_dev *dev, int resno)
> >  int pci_claim_resource(struct pci_dev *dev, int resource)
> >  {
> >       struct resource *res = &dev->resource[resource];
> > -     struct resource *root, *conflict;
> > +     struct resource *first = NULL, *second = NULL, *conflict;
> >
> >       if (res->flags & IORESOURCE_UNSET) {
> >               pci_info(dev, "can't claim BAR %d %pR: no address assigned\n",
> > @@ -147,21 +147,28 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
> >       if (res->flags & IORESOURCE_ROM_SHADOW)
> >               return 0;
> >
> > -     root = pci_find_parent_resource(dev, res);
> > -     if (!root) {
> > +     pci_find_parent_resource(dev, res, &first, &second);
> > +     if (!first) {
> >               pci_info(dev, "can't claim BAR %d %pR: no compatible bridge window\n",
> >                        resource, res);
> >               res->flags |= IORESOURCE_UNSET;
> >               return -EINVAL;
> >       }
> >
> > -     conflict = request_resource_conflict(root, res);
> > +     if (second)
> > +             first->end = second->end;
> > +
> > +     conflict = request_resource_conflict(first, res);
> >       if (conflict) {
> > +             if (second)
> > +                     first->end = second->start - 1;
> > +
> >               pci_info(dev, "can't claim BAR %d %pR: address conflict with %s %pR\n",
> >                        resource, res, conflict->name, conflict);
> >               res->flags |= IORESOURCE_UNSET;
> >               return -EBUSY;
> > -     }
> > +     } else if (second)
> > +             second->start = second->end = 0;
> >
> >       return 0;
> >  }
> > @@ -195,7 +202,7 @@ resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
> >  static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
> >               int resno, resource_size_t size)
> >  {
> > -     struct resource *root, *conflict;
> > +     struct resource *root = NULL, *conflict;
> >       resource_size_t fw_addr, start, end;
> >
> >       fw_addr = pcibios_retrieve_fw_addr(dev, resno);
> > @@ -208,7 +215,7 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
> >       res->end = res->start + size - 1;
> >       res->flags &= ~IORESOURCE_UNSET;
> >
> > -     root = pci_find_parent_resource(dev, res);
> > +     pci_find_parent_resource(dev, res, &root, NULL);
> >       if (!root) {
> >               if (res->flags & IORESOURCE_IO)
> >                       root = &ioport_resource;
> > diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
> > index 3b05760e69d6..2fba42d7486e 100644
> > --- a/drivers/pcmcia/rsrc_nonstatic.c
> > +++ b/drivers/pcmcia/rsrc_nonstatic.c
> > @@ -73,7 +73,7 @@ static struct resource *
> >  claim_region(struct pcmcia_socket *s, resource_size_t base,
> >               resource_size_t size, int type, char *name)
> >  {
> > -     struct resource *res, *parent;
> > +     struct resource *res, *parent = NULL;
> >
> >       parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
> >       res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name);
> > @@ -81,7 +81,7 @@ claim_region(struct pcmcia_socket *s, resource_size_t base,
> >       if (res) {
> >  #ifdef CONFIG_PCI
> >               if (s && s->cb_dev)
> > -                     parent = pci_find_parent_resource(s->cb_dev, res);
> > +                     pci_find_parent_resource(s->cb_dev, res, &parent, NULL);
> >  #endif
> >               if (!parent || request_resource(parent, res)) {
> >                       kfree(res);
> > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > index 86c799c97b77..dd1455be5247 100644
> > --- a/include/linux/pci.h
> > +++ b/include/linux/pci.h
> > @@ -1049,8 +1049,10 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
> >  unsigned int pci_scan_child_bus(struct pci_bus *bus);
> >  void pci_bus_add_device(struct pci_dev *dev);
> >  void pci_read_bridge_bases(struct pci_bus *child);
> > -struct resource *pci_find_parent_resource(const struct pci_dev *dev,
> > -                                       struct resource *res);
> > +void pci_find_parent_resource(const struct pci_dev *dev,
> > +                                       struct resource *res,
> > +                                       struct resource **first,
> > +                                       struct resource **second);
> >  u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin);
> >  int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge);
> >  u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp);
> > --
> > 2.30.2
> >


More information about the Linuxppc-dev mailing list