Subject: Re: [PATCH RFC v4 17/21] PCI: Calculate boundaries for bridge windows

From: Bjorn Helgaas <[email-masked]>
Date: Tue, 26 Mar 2019 16:01:28 -0500

On Mon, Mar 11, 2019 at 04:31:18PM +0300, Sergey Miroshnichenko wrote:
> If a bridge window contains fixed areas (there are PCIe devices with
> immovable BARs located on this bus), 

I think what you mean by "immovable BARs" is "drivers that don't
support moving BARs".  I want to keep the concept of legacy and EA
resources separate because those are immovable in principle, but
drivers can always be improved.

> this window must be allocated
> within the bound memory area, limited by windows size and by address
> range of fixed resources, calculated as follows:
> 
>            | <--     bus's fixed_range_hard   --> |
>   | <--  fixed_range_hard.end - window size   --> |
>            | <--  fixed_range_hard.start + window size   --> |
>   | <--                bus's fixed_range_soft            --> |
> 
> Signed-off-by: Sergey Miroshnichenko <[email-masked]>
> ---
>  drivers/pci/setup-bus.c | 56 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci.h     |  4 ++-
>  2 files changed, 59 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
> index a1fd7f3c5ea8..f4737339d5ec 100644
> --- a/drivers/pci/setup-bus.c
> +++ b/drivers/pci/setup-bus.c
> @@ -1809,6 +1809,61 @@ static enum enable_type pci_realloc_detect(struct pci_bus *bus,
>  }
>  #endif
>  
> +static void pci_bus_update_fixed_range_soft(struct pci_bus *bus)
> +{
> +	struct pci_dev *dev;
> +	struct pci_bus *parent = bus->parent;
> +	int idx;
> +
> +	list_for_each_entry(dev, &bus->devices, bus_list)
> +		if (dev->subordinate)
> +			pci_bus_update_fixed_range_soft(dev->subordinate);
> +
> +	if (!parent || !bus->self)
> +		return;
> +
> +	for (idx = 0; idx < ARRAY_SIZE(bus->fixed_range_hard); ++idx) {
> +		struct resource *r;
> +		resource_size_t soft_start, soft_end;
> +		resource_size_t hard_start = bus->fixed_range_hard[idx].start;
> +		resource_size_t hard_end = bus->fixed_range_hard[idx].end;
> +
> +		if (hard_start > hard_end)
> +			continue;
> +
> +		r = bus->resource[idx];
> +
> +		soft_start = hard_end - resource_size(r) + 1;
> +		soft_end = hard_start + resource_size(r) - 1;
> +
> +		if (soft_start > hard_start)
> +			soft_start = hard_start;
> +
> +		if (soft_end < hard_end)
> +			soft_end = hard_end;
> +
> +		list_for_each_entry(dev, &parent->devices, bus_list) {
> +			struct pci_bus *sibling = dev->subordinate;
> +			resource_size_t s_start, s_end;
> +
> +			if (!sibling || sibling == bus)
> +				continue;
> +
> +			s_start = sibling->fixed_range_hard[idx].start;
> +			s_end = sibling->fixed_range_hard[idx].end;
> +
> +			if (s_start > s_end)
> +				continue;
> +
> +			if (s_end < hard_start && s_end > soft_start)
> +				soft_start = s_end;
> +		}
> +
> +		bus->fixed_range_soft[idx].start = soft_start;
> +		bus->fixed_range_soft[idx].end = soft_end;
> +	}
> +}
> +
>  /*
>   * first try will not touch pci bridge res
>   * second and later try will clear small leaf bridge res
> @@ -1847,6 +1902,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
>  	/* Depth first, calculate sizes and alignments of all
>  	   subordinate buses. */
>  	__pci_bus_size_bridges(bus, add_list);
> +	pci_bus_update_fixed_range_soft(bus);
>  
>  	/* Depth last, allocate resources and update the hardware. */
>  	__pci_bus_assign_resources(bus, add_list, &fail_head);
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 7a4d62d84bc1..75a56db73ad4 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -574,9 +574,11 @@ struct pci_bus {
>  
>  	/*
>  	 * If there are fixed resources in the bridge window, the hard range
> -	 * contains the lowest and the highest addresses of them.
> +	 * contains the lowest and the highest addresses of them, and this
> +	 * bridge window must reside within the soft range.
>  	 */
>  	struct resource fixed_range_hard[PCI_BRIDGE_RESOURCE_NUM];
> +	struct resource fixed_range_soft[PCI_BRIDGE_RESOURCE_NUM];
>  
>  	struct pci_ops	*ops;		/* Configuration access functions */
>  	struct msi_controller *msi;	/* MSI controller */
> -- 
> 2.20.1
>