diff mbox series

PCI: Try to find two continuous regions for child resource

Message ID 20210329084804.257526-1-kai.heng.feng@canonical.com
State New
Headers show
Series PCI: Try to find two continuous regions for child resource | expand

Commit Message

Kai-Heng Feng March 29, 2021, 8:47 a.m. UTC
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.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212013
Signed-off-by: Kai-Heng Feng <kai.heng.feng@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(-)

Comments

Bjorn Helgaas March 29, 2021, 4:22 p.m. UTC | #1
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.

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)

> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212013
> Signed-off-by: Kai-Heng Feng <kai.heng.feng@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
>
kernel test robot March 29, 2021, 4:56 p.m. UTC | #2
Hi Kai-Heng,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on pci/next]
[also build test ERROR on powerpc/next sparc/master linus/master sparc-next/master v5.12-rc5]
[cannot apply to next-20210329]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Kai-Heng-Feng/PCI-Try-to-find-two-continuous-regions-for-child-resource/20210329-165155
base:   https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next
config: microblaze-randconfig-p001-20210329 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/94d03d680678c966738f767c5fb91305a3af5c9d
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Kai-Heng-Feng/PCI-Try-to-find-two-continuous-regions-for-child-resource/20210329-165155
        git checkout 94d03d680678c966738f767c5fb91305a3af5c9d
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=microblaze 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   arch/microblaze/pci/pci-common.c: In function 'alloc_resource':
>> arch/microblaze/pci/pci-common.c:749:7: error: too few arguments to function 'pci_find_parent_resource'
     749 |  pr = pci_find_parent_resource(dev, r);
         |       ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/microblaze/pci/pci-common.c:16:
   include/linux/pci.h:1052:6: note: declared here
    1052 | void pci_find_parent_resource(const struct pci_dev *dev,
         |      ^~~~~~~~~~~~~~~~~~~~~~~~


vim +/pci_find_parent_resource +749 arch/microblaze/pci/pci-common.c

d3afa58c20b651 Michal Simek       2010-01-18  738  
b881bc469bdbdc Greg Kroah-Hartman 2012-12-21  739  static inline void alloc_resource(struct pci_dev *dev, int idx)
d3afa58c20b651 Michal Simek       2010-01-18  740  {
d3afa58c20b651 Michal Simek       2010-01-18  741  	struct resource *pr, *r = &dev->resource[idx];
d3afa58c20b651 Michal Simek       2010-01-18  742  
d3afa58c20b651 Michal Simek       2010-01-18  743  	pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n",
d3afa58c20b651 Michal Simek       2010-01-18  744  		 pci_name(dev), idx,
d3afa58c20b651 Michal Simek       2010-01-18  745  		 (unsigned long long)r->start,
d3afa58c20b651 Michal Simek       2010-01-18  746  		 (unsigned long long)r->end,
d3afa58c20b651 Michal Simek       2010-01-18  747  		 (unsigned int)r->flags);
d3afa58c20b651 Michal Simek       2010-01-18  748  
d3afa58c20b651 Michal Simek       2010-01-18 @749  	pr = pci_find_parent_resource(dev, r);
d3afa58c20b651 Michal Simek       2010-01-18  750  	if (!pr || (pr->flags & IORESOURCE_UNSET) ||
d3afa58c20b651 Michal Simek       2010-01-18  751  	    request_resource(pr, r) < 0) {
6bd55f0bbaebb7 Michal Simek       2012-12-27  752  		pr_warn("PCI: Cannot allocate resource region %d ", idx);
6bd55f0bbaebb7 Michal Simek       2012-12-27  753  		pr_cont("of device %s, will remap\n", pci_name(dev));
d3afa58c20b651 Michal Simek       2010-01-18  754  		if (pr)
d3afa58c20b651 Michal Simek       2010-01-18  755  			pr_debug("PCI:  parent is %p: %016llx-%016llx [%x]\n",
d3afa58c20b651 Michal Simek       2010-01-18  756  				 pr,
d3afa58c20b651 Michal Simek       2010-01-18  757  				 (unsigned long long)pr->start,
d3afa58c20b651 Michal Simek       2010-01-18  758  				 (unsigned long long)pr->end,
d3afa58c20b651 Michal Simek       2010-01-18  759  				 (unsigned int)pr->flags);
d3afa58c20b651 Michal Simek       2010-01-18  760  		/* We'll assign a new address later */
d3afa58c20b651 Michal Simek       2010-01-18  761  		r->flags |= IORESOURCE_UNSET;
d3afa58c20b651 Michal Simek       2010-01-18  762  		r->end -= r->start;
d3afa58c20b651 Michal Simek       2010-01-18  763  		r->start = 0;
d3afa58c20b651 Michal Simek       2010-01-18  764  	}
d3afa58c20b651 Michal Simek       2010-01-18  765  }
d3afa58c20b651 Michal Simek       2010-01-18  766  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Kai-Heng Feng March 31, 2021, 8:53 a.m. UTC | #3
On Tue, Mar 30, 2021 at 12:23 AM Bjorn Helgaas <helgaas@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@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
> >
diff mbox series

Patch

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);