[PATCH] PCI: mvebu: Handle changes to the bridge windows while enabled

Bjorn Helgaas helgaas at kernel.org
Sat Jan 28 13:17:28 PST 2017


On Wed, Jan 11, 2017 at 12:30:55PM -0600, Bjorn Helgaas wrote:
> On Mon, Dec 12, 2016 at 11:30:20AM -0700, Jason Gunthorpe wrote:
> > The PCI core will write to the bridge window config multiple times
> > while they are enabled. This can lead to mbus failures like:
> > 
> >  mvebu_mbus: cannot add window '4:e8', conflicts with another window
> >  mvebu-pcie mbus:pex at e0000000: Could not create MBus window at [mem 0xe0000000-0xe00fffff]: -22
> > 
> > For me this is happening during a hotplug cycle. The PCI core is
> > not changing the values, just writing them twice while active.
> > 
> > The patch addresses the general case of any change to an active window,
> > but not atomically. The code is slightly refactored so io and mem
> > can share more of the window logic.
> 
> Looks good to me, but I'm waiting for an ack from Thomas or Jason (listed
> as maintainers and already cc'd).

Ping, Thomas, Jason C?

> > Signed-off-by: Jason Gunthorpe <jgunthorpe at obsidianresearch.com>
> > ---
> >  drivers/pci/host/pci-mvebu.c | 101 +++++++++++++++++++++++++------------------
> >  1 file changed, 60 insertions(+), 41 deletions(-)
> > 
> > diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
> > index 307f81d6b479af..af724731b22f53 100644
> > --- a/drivers/pci/host/pci-mvebu.c
> > +++ b/drivers/pci/host/pci-mvebu.c
> > @@ -133,6 +133,12 @@ struct mvebu_pcie {
> >  	int nports;
> >  };
> >  
> > +struct mvebu_pcie_window {
> > +	phys_addr_t base;
> > +	phys_addr_t remap;
> > +	size_t size;
> > +};
> > +
> >  /* Structure representing one PCIe interface */
> >  struct mvebu_pcie_port {
> >  	char *name;
> > @@ -150,10 +156,8 @@ struct mvebu_pcie_port {
> >  	struct mvebu_sw_pci_bridge bridge;
> >  	struct device_node *dn;
> >  	struct mvebu_pcie *pcie;
> > -	phys_addr_t memwin_base;
> > -	size_t memwin_size;
> > -	phys_addr_t iowin_base;
> > -	size_t iowin_size;
> > +	struct mvebu_pcie_window memwin;
> > +	struct mvebu_pcie_window iowin;
> >  	u32 saved_pcie_stat;
> >  };
> >  
> > @@ -379,23 +383,45 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
> >  	}
> >  }
> >  
> > +static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
> > +				  unsigned int target, unsigned int attribute,
> > +				  const struct mvebu_pcie_window *desired,
> > +				  struct mvebu_pcie_window *cur)
> > +{
> > +	if (desired->base == cur->base && desired->remap == cur->remap &&
> > +	    desired->size == cur->size)
> > +		return;
> > +
> > +	if (cur->size != 0) {
> > +		mvebu_pcie_del_windows(port, cur->base, cur->size);
> > +		cur->size = 0;
> > +		cur->base = 0;
> > +
> > +		/*
> > +		 * If something tries to change the window while it is enabled
> > +		 * the change will not be done atomically. That would be
> > +		 * difficult to do in the general case.
> > +		 */
> > +	}
> > +
> > +	if (desired->size == 0)
> > +		return;
> > +
> > +	mvebu_pcie_add_windows(port, target, attribute, desired->base,
> > +			       desired->size, desired->remap);
> > +	*cur = *desired;
> > +}
> > +
> >  static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  {
> > -	phys_addr_t iobase;
> > +	struct mvebu_pcie_window desired = {};
> >  
> >  	/* Are the new iobase/iolimit values invalid? */
> >  	if (port->bridge.iolimit < port->bridge.iobase ||
> >  	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
> >  	    !(port->bridge.command & PCI_COMMAND_IO)) {
> > -
> > -		/* If a window was configured, remove it */
> > -		if (port->iowin_base) {
> > -			mvebu_pcie_del_windows(port, port->iowin_base,
> > -					       port->iowin_size);
> > -			port->iowin_base = 0;
> > -			port->iowin_size = 0;
> > -		}
> > -
> > +		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
> > +				      &desired, &port->iowin);
> >  		return;
> >  	}
> >  
> > @@ -412,32 +438,27 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  	 * specifications. iobase is the bus address, port->iowin_base
> >  	 * is the CPU address.
> >  	 */
> > -	iobase = ((port->bridge.iobase & 0xF0) << 8) |
> > -		(port->bridge.iobaseupper << 16);
> > -	port->iowin_base = port->pcie->io.start + iobase;
> > -	port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> > -			    (port->bridge.iolimitupper << 16)) -
> > -			    iobase) + 1;
> > -
> > -	mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
> > -			       port->iowin_base, port->iowin_size,
> > -			       iobase);
> > +	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
> > +			(port->bridge.iobaseupper << 16);
> > +	desired.base = port->pcie->io.start + desired.remap;
> > +	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> > +			 (port->bridge.iolimitupper << 16)) -
> > +			desired.remap) +
> > +		       1;
> > +
> > +	mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired,
> > +			      &port->iowin);
> >  }
> >  
> >  static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
> >  {
> > +	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
> > +
> >  	/* Are the new membase/memlimit values invalid? */
> >  	if (port->bridge.memlimit < port->bridge.membase ||
> >  	    !(port->bridge.command & PCI_COMMAND_MEMORY)) {
> > -
> > -		/* If a window was configured, remove it */
> > -		if (port->memwin_base) {
> > -			mvebu_pcie_del_windows(port, port->memwin_base,
> > -					       port->memwin_size);
> > -			port->memwin_base = 0;
> > -			port->memwin_size = 0;
> > -		}
> > -
> > +		mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
> > +				      &desired, &port->memwin);
> >  		return;
> >  	}
> >  
> > @@ -447,14 +468,12 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
> >  	 * window to setup, according to the PCI-to-PCI bridge
> >  	 * specifications.
> >  	 */
> > -	port->memwin_base  = ((port->bridge.membase & 0xFFF0) << 16);
> > -	port->memwin_size  =
> > -		(((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
> > -		port->memwin_base + 1;
> > -
> > -	mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr,
> > -			       port->memwin_base, port->memwin_size,
> > -			       MVEBU_MBUS_NO_REMAP);
> > +	desired.base = ((port->bridge.membase & 0xFFF0) << 16);
> > +	desired.size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
> > +		       desired.base + 1;
> > +
> > +	mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
> > +			      &port->memwin);
> >  }
> >  
> >  /*
> > -- 
> > 2.7.4
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> > the body of a message to majordomo at vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list