diff mbox series

[SRU,F,G,v3,1/1] s390/pci: Fix zpci_alloc_domain() over allocation

Message ID 20200506072102.11107-2-frank.heimes@canonical.com
State New
Headers show
Series s390x/pci: do not allow to create more pci functions than configured via CONFIG_PCI_NR_FUNCTIONS | expand

Commit Message

Frank Heimes May 6, 2020, 7:21 a.m. UTC
From: Niklas Schnelle <schnelle@linux.ibm.com>

BugLink: https://bugs.launchpad.net/bugs/1874057

Until now zpci_alloc_domain() only prevented more than
CONFIG_PCI_NR_FUNCTIONS from being added when using automatic domain
allocation. When explicit UIDs were defined UIDs above
CONFIG_PCI_NR_FUNCTIONS were not counted at all.
When more PCI functions are added this could lead to various errors
including under sized IRQ vectors and similar issues.

Fix this by explicitly tracking the number of allocated domains.

Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Reviewed-by: Pierre Morel <pmorel at linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor at linux.ibm.com>
(backported from commit 969ae01bab2fe938b4c8324836038b5ac1c78fac)
Signed-off-by: Frank Heimes <frank.heimes@canonical.com>
---
 arch/s390/include/asm/pci.h |  1 +
 arch/s390/pci/pci.c         | 34 ++++++++++++++++++++--------------
 2 files changed, 21 insertions(+), 14 deletions(-)

Comments

Kleber Sacilotto de Souza May 13, 2020, 9:54 a.m. UTC | #1
On 06.05.20 09:21, frank.heimes@canonical.com wrote:
> From: Niklas Schnelle <schnelle@linux.ibm.com>
> 
> BugLink: https://bugs.launchpad.net/bugs/1874057
> 
> Until now zpci_alloc_domain() only prevented more than
> CONFIG_PCI_NR_FUNCTIONS from being added when using automatic domain
> allocation. When explicit UIDs were defined UIDs above
> CONFIG_PCI_NR_FUNCTIONS were not counted at all.
> When more PCI functions are added this could lead to various errors
> including under sized IRQ vectors and similar issues.
> 
> Fix this by explicitly tracking the number of allocated domains.
> 
> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> Reviewed-by: Pierre Morel <pmorel at linux.ibm.com>
> Signed-off-by: Vasily Gorbik <gor at linux.ibm.com>
> (backported from commit 969ae01bab2fe938b4c8324836038b5ac1c78fac)
> Signed-off-by: Frank Heimes <frank.heimes@canonical.com>


Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>

> ---
>  arch/s390/include/asm/pci.h |  1 +
>  arch/s390/pci/pci.c         | 34 ++++++++++++++++++++--------------
>  2 files changed, 21 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
> index 6087a4e9b2bf..7850e8c8c79a 100644
> --- a/arch/s390/include/asm/pci.h
> +++ b/arch/s390/include/asm/pci.h
> @@ -28,6 +28,7 @@ int pci_proc_domain(struct pci_bus *);
>  
>  #define ZPCI_NR_DMA_SPACES		1
>  #define ZPCI_NR_DEVICES			CONFIG_PCI_NR_FUNCTIONS
> +#define ZPCI_DOMAIN_BITMAP_SIZE		(1 << 16)
>  
>  /* PCI Function Controls */
>  #define ZPCI_FC_FN_ENABLED		0x80
> diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
> index 6105b1b6e49b..0af46683dd66 100644
> --- a/arch/s390/pci/pci.c
> +++ b/arch/s390/pci/pci.c
> @@ -39,8 +39,9 @@
>  static LIST_HEAD(zpci_list);
>  static DEFINE_SPINLOCK(zpci_list_lock);
>  
> -static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
> +static DECLARE_BITMAP(zpci_domain, ZPCI_DOMAIN_BITMAP_SIZE);
>  static DEFINE_SPINLOCK(zpci_domain_lock);
> +static unsigned int zpci_num_domains_allocated;
>  
>  #define ZPCI_IOMAP_ENTRIES						\
>  	min(((unsigned long) ZPCI_NR_DEVICES * PCI_BAR_COUNT / 2),	\
> @@ -651,39 +652,44 @@ struct dev_pm_ops pcibios_pm_ops = {
>  
>  static int zpci_alloc_domain(struct zpci_dev *zdev)
>  {
> +	spin_lock(&zpci_domain_lock);
> +	if (zpci_num_domains_allocated > (ZPCI_NR_DEVICES - 1)) {
> +		spin_unlock(&zpci_domain_lock);
> +		pr_err("Adding PCI function %08x failed because the configured limit of %d is reached\n",
> +			zdev->fid, ZPCI_NR_DEVICES);
> +		return -ENOSPC;
> +	}
> +
>  	if (zpci_unique_uid) {
>  		zdev->domain = (u16) zdev->uid;
> -		if (zdev->domain >= ZPCI_NR_DEVICES)
> -			return 0;
> -
> -		spin_lock(&zpci_domain_lock);
>  		if (test_bit(zdev->domain, zpci_domain)) {
>  			spin_unlock(&zpci_domain_lock);
> +			pr_err("Adding PCI function %08x failed because domain %04x is already assigned\n",
> +				zdev->fid, zdev->domain);
>  			return -EEXIST;
>  		}
>  		set_bit(zdev->domain, zpci_domain);
> +		zpci_num_domains_allocated++;
>  		spin_unlock(&zpci_domain_lock);
>  		return 0;
>  	}
> -
> -	spin_lock(&zpci_domain_lock);
> +	/*
> +	 * We can always auto allocate domains below ZPCI_NR_DEVICES.
> +	 * There is either a free domain or we have reached the maximum in
> +	 * which case we would have bailed earlier.
> +	 */
>  	zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
> -	if (zdev->domain == ZPCI_NR_DEVICES) {
> -		spin_unlock(&zpci_domain_lock);
> -		return -ENOSPC;
> -	}
>  	set_bit(zdev->domain, zpci_domain);
> +	zpci_num_domains_allocated++;
>  	spin_unlock(&zpci_domain_lock);
>  	return 0;
>  }
>  
>  static void zpci_free_domain(struct zpci_dev *zdev)
>  {
> -	if (zdev->domain >= ZPCI_NR_DEVICES)
> -		return;
> -
>  	spin_lock(&zpci_domain_lock);
>  	clear_bit(zdev->domain, zpci_domain);
> +	zpci_num_domains_allocated--;
>  	spin_unlock(&zpci_domain_lock);
>  }
>  
>
Kleber Sacilotto de Souza May 13, 2020, 9:56 a.m. UTC | #2
On 13.05.20 11:54, Kleber Souza wrote:
> On 06.05.20 09:21, frank.heimes@canonical.com wrote:
>> From: Niklas Schnelle <schnelle@linux.ibm.com>
>>
>> BugLink: https://bugs.launchpad.net/bugs/1874057

Hi Frank,

I forgot to mention, can you please fix the nominations on the bug report?


Thanks,
Kleber

>>
>> Until now zpci_alloc_domain() only prevented more than
>> CONFIG_PCI_NR_FUNCTIONS from being added when using automatic domain
>> allocation. When explicit UIDs were defined UIDs above
>> CONFIG_PCI_NR_FUNCTIONS were not counted at all.
>> When more PCI functions are added this could lead to various errors
>> including under sized IRQ vectors and similar issues.
>>
>> Fix this by explicitly tracking the number of allocated domains.
>>
>> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
>> Reviewed-by: Pierre Morel <pmorel at linux.ibm.com>
>> Signed-off-by: Vasily Gorbik <gor at linux.ibm.com>
>> (backported from commit 969ae01bab2fe938b4c8324836038b5ac1c78fac)
>> Signed-off-by: Frank Heimes <frank.heimes@canonical.com>
> 
> 
> Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
> 
>> ---
>>  arch/s390/include/asm/pci.h |  1 +
>>  arch/s390/pci/pci.c         | 34 ++++++++++++++++++++--------------
>>  2 files changed, 21 insertions(+), 14 deletions(-)
>>
>> diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
>> index 6087a4e9b2bf..7850e8c8c79a 100644
>> --- a/arch/s390/include/asm/pci.h
>> +++ b/arch/s390/include/asm/pci.h
>> @@ -28,6 +28,7 @@ int pci_proc_domain(struct pci_bus *);
>>  
>>  #define ZPCI_NR_DMA_SPACES		1
>>  #define ZPCI_NR_DEVICES			CONFIG_PCI_NR_FUNCTIONS
>> +#define ZPCI_DOMAIN_BITMAP_SIZE		(1 << 16)
>>  
>>  /* PCI Function Controls */
>>  #define ZPCI_FC_FN_ENABLED		0x80
>> diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
>> index 6105b1b6e49b..0af46683dd66 100644
>> --- a/arch/s390/pci/pci.c
>> +++ b/arch/s390/pci/pci.c
>> @@ -39,8 +39,9 @@
>>  static LIST_HEAD(zpci_list);
>>  static DEFINE_SPINLOCK(zpci_list_lock);
>>  
>> -static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
>> +static DECLARE_BITMAP(zpci_domain, ZPCI_DOMAIN_BITMAP_SIZE);
>>  static DEFINE_SPINLOCK(zpci_domain_lock);
>> +static unsigned int zpci_num_domains_allocated;
>>  
>>  #define ZPCI_IOMAP_ENTRIES						\
>>  	min(((unsigned long) ZPCI_NR_DEVICES * PCI_BAR_COUNT / 2),	\
>> @@ -651,39 +652,44 @@ struct dev_pm_ops pcibios_pm_ops = {
>>  
>>  static int zpci_alloc_domain(struct zpci_dev *zdev)
>>  {
>> +	spin_lock(&zpci_domain_lock);
>> +	if (zpci_num_domains_allocated > (ZPCI_NR_DEVICES - 1)) {
>> +		spin_unlock(&zpci_domain_lock);
>> +		pr_err("Adding PCI function %08x failed because the configured limit of %d is reached\n",
>> +			zdev->fid, ZPCI_NR_DEVICES);
>> +		return -ENOSPC;
>> +	}
>> +
>>  	if (zpci_unique_uid) {
>>  		zdev->domain = (u16) zdev->uid;
>> -		if (zdev->domain >= ZPCI_NR_DEVICES)
>> -			return 0;
>> -
>> -		spin_lock(&zpci_domain_lock);
>>  		if (test_bit(zdev->domain, zpci_domain)) {
>>  			spin_unlock(&zpci_domain_lock);
>> +			pr_err("Adding PCI function %08x failed because domain %04x is already assigned\n",
>> +				zdev->fid, zdev->domain);
>>  			return -EEXIST;
>>  		}
>>  		set_bit(zdev->domain, zpci_domain);
>> +		zpci_num_domains_allocated++;
>>  		spin_unlock(&zpci_domain_lock);
>>  		return 0;
>>  	}
>> -
>> -	spin_lock(&zpci_domain_lock);
>> +	/*
>> +	 * We can always auto allocate domains below ZPCI_NR_DEVICES.
>> +	 * There is either a free domain or we have reached the maximum in
>> +	 * which case we would have bailed earlier.
>> +	 */
>>  	zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
>> -	if (zdev->domain == ZPCI_NR_DEVICES) {
>> -		spin_unlock(&zpci_domain_lock);
>> -		return -ENOSPC;
>> -	}
>>  	set_bit(zdev->domain, zpci_domain);
>> +	zpci_num_domains_allocated++;
>>  	spin_unlock(&zpci_domain_lock);
>>  	return 0;
>>  }
>>  
>>  static void zpci_free_domain(struct zpci_dev *zdev)
>>  {
>> -	if (zdev->domain >= ZPCI_NR_DEVICES)
>> -		return;
>> -
>>  	spin_lock(&zpci_domain_lock);
>>  	clear_bit(zdev->domain, zpci_domain);
>> +	zpci_num_domains_allocated--;
>>  	spin_unlock(&zpci_domain_lock);
>>  }
>>  
>>
>
Frank Heimes May 13, 2020, 10:02 a.m. UTC | #3
Yes of course - done.
(Sorry, some of the tickets were opened when the dev. of 'groovy' was not
opened, and I forgot to adjust the nominations later ...)

BR, Frank


On Wed, May 13, 2020 at 11:56 AM Kleber Souza <kleber.souza@canonical.com>
wrote:

> On 13.05.20 11:54, Kleber Souza wrote:
> > On 06.05.20 09:21, frank.heimes@canonical.com wrote:
> >> From: Niklas Schnelle <schnelle@linux.ibm.com>
> >>
> >> BugLink: https://bugs.launchpad.net/bugs/1874057
>
> Hi Frank,
>
> I forgot to mention, can you please fix the nominations on the bug report?
>
>
> Thanks,
> Kleber
>
> >>
> >> Until now zpci_alloc_domain() only prevented more than
> >> CONFIG_PCI_NR_FUNCTIONS from being added when using automatic domain
> >> allocation. When explicit UIDs were defined UIDs above
> >> CONFIG_PCI_NR_FUNCTIONS were not counted at all.
> >> When more PCI functions are added this could lead to various errors
> >> including under sized IRQ vectors and similar issues.
> >>
> >> Fix this by explicitly tracking the number of allocated domains.
> >>
> >> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> >> Reviewed-by: Pierre Morel <pmorel at linux.ibm.com>
> >> Signed-off-by: Vasily Gorbik <gor at linux.ibm.com>
> >> (backported from commit 969ae01bab2fe938b4c8324836038b5ac1c78fac)
> >> Signed-off-by: Frank Heimes <frank.heimes@canonical.com>
> >
> >
> > Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
> >
> >> ---
> >>  arch/s390/include/asm/pci.h |  1 +
> >>  arch/s390/pci/pci.c         | 34 ++++++++++++++++++++--------------
> >>  2 files changed, 21 insertions(+), 14 deletions(-)
> >>
> >> diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
> >> index 6087a4e9b2bf..7850e8c8c79a 100644
> >> --- a/arch/s390/include/asm/pci.h
> >> +++ b/arch/s390/include/asm/pci.h
> >> @@ -28,6 +28,7 @@ int pci_proc_domain(struct pci_bus *);
> >>
> >>  #define ZPCI_NR_DMA_SPACES          1
> >>  #define ZPCI_NR_DEVICES                     CONFIG_PCI_NR_FUNCTIONS
> >> +#define ZPCI_DOMAIN_BITMAP_SIZE             (1 << 16)
> >>
> >>  /* PCI Function Controls */
> >>  #define ZPCI_FC_FN_ENABLED          0x80
> >> diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
> >> index 6105b1b6e49b..0af46683dd66 100644
> >> --- a/arch/s390/pci/pci.c
> >> +++ b/arch/s390/pci/pci.c
> >> @@ -39,8 +39,9 @@
> >>  static LIST_HEAD(zpci_list);
> >>  static DEFINE_SPINLOCK(zpci_list_lock);
> >>
> >> -static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
> >> +static DECLARE_BITMAP(zpci_domain, ZPCI_DOMAIN_BITMAP_SIZE);
> >>  static DEFINE_SPINLOCK(zpci_domain_lock);
> >> +static unsigned int zpci_num_domains_allocated;
> >>
> >>  #define ZPCI_IOMAP_ENTRIES                                          \
> >>      min(((unsigned long) ZPCI_NR_DEVICES * PCI_BAR_COUNT / 2),      \
> >> @@ -651,39 +652,44 @@ struct dev_pm_ops pcibios_pm_ops = {
> >>
> >>  static int zpci_alloc_domain(struct zpci_dev *zdev)
> >>  {
> >> +    spin_lock(&zpci_domain_lock);
> >> +    if (zpci_num_domains_allocated > (ZPCI_NR_DEVICES - 1)) {
> >> +            spin_unlock(&zpci_domain_lock);
> >> +            pr_err("Adding PCI function %08x failed because the
> configured limit of %d is reached\n",
> >> +                    zdev->fid, ZPCI_NR_DEVICES);
> >> +            return -ENOSPC;
> >> +    }
> >> +
> >>      if (zpci_unique_uid) {
> >>              zdev->domain = (u16) zdev->uid;
> >> -            if (zdev->domain >= ZPCI_NR_DEVICES)
> >> -                    return 0;
> >> -
> >> -            spin_lock(&zpci_domain_lock);
> >>              if (test_bit(zdev->domain, zpci_domain)) {
> >>                      spin_unlock(&zpci_domain_lock);
> >> +                    pr_err("Adding PCI function %08x failed because
> domain %04x is already assigned\n",
> >> +                            zdev->fid, zdev->domain);
> >>                      return -EEXIST;
> >>              }
> >>              set_bit(zdev->domain, zpci_domain);
> >> +            zpci_num_domains_allocated++;
> >>              spin_unlock(&zpci_domain_lock);
> >>              return 0;
> >>      }
> >> -
> >> -    spin_lock(&zpci_domain_lock);
> >> +    /*
> >> +     * We can always auto allocate domains below ZPCI_NR_DEVICES.
> >> +     * There is either a free domain or we have reached the maximum in
> >> +     * which case we would have bailed earlier.
> >> +     */
> >>      zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
> >> -    if (zdev->domain == ZPCI_NR_DEVICES) {
> >> -            spin_unlock(&zpci_domain_lock);
> >> -            return -ENOSPC;
> >> -    }
> >>      set_bit(zdev->domain, zpci_domain);
> >> +    zpci_num_domains_allocated++;
> >>      spin_unlock(&zpci_domain_lock);
> >>      return 0;
> >>  }
> >>
> >>  static void zpci_free_domain(struct zpci_dev *zdev)
> >>  {
> >> -    if (zdev->domain >= ZPCI_NR_DEVICES)
> >> -            return;
> >> -
> >>      spin_lock(&zpci_domain_lock);
> >>      clear_bit(zdev->domain, zpci_domain);
> >> +    zpci_num_domains_allocated--;
> >>      spin_unlock(&zpci_domain_lock);
> >>  }
> >>
> >>
> >
>
>
Stefan Bader May 13, 2020, 12:14 p.m. UTC | #4
On 06.05.20 09:21, frank.heimes@canonical.com wrote:
> From: Niklas Schnelle <schnelle@linux.ibm.com>
> 
> BugLink: https://bugs.launchpad.net/bugs/1874057
> 
> Until now zpci_alloc_domain() only prevented more than
> CONFIG_PCI_NR_FUNCTIONS from being added when using automatic domain
> allocation. When explicit UIDs were defined UIDs above
> CONFIG_PCI_NR_FUNCTIONS were not counted at all.
> When more PCI functions are added this could lead to various errors
> including under sized IRQ vectors and similar issues.
> 
> Fix this by explicitly tracking the number of allocated domains.
> 
> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> Reviewed-by: Pierre Morel <pmorel at linux.ibm.com>
> Signed-off-by: Vasily Gorbik <gor at linux.ibm.com>
> (backported from commit 969ae01bab2fe938b4c8324836038b5ac1c78fac)
> Signed-off-by: Frank Heimes <frank.heimes@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
> ---
>  arch/s390/include/asm/pci.h |  1 +
>  arch/s390/pci/pci.c         | 34 ++++++++++++++++++++--------------
>  2 files changed, 21 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
> index 6087a4e9b2bf..7850e8c8c79a 100644
> --- a/arch/s390/include/asm/pci.h
> +++ b/arch/s390/include/asm/pci.h
> @@ -28,6 +28,7 @@ int pci_proc_domain(struct pci_bus *);
>  
>  #define ZPCI_NR_DMA_SPACES		1
>  #define ZPCI_NR_DEVICES			CONFIG_PCI_NR_FUNCTIONS
> +#define ZPCI_DOMAIN_BITMAP_SIZE		(1 << 16)
>  
>  /* PCI Function Controls */
>  #define ZPCI_FC_FN_ENABLED		0x80
> diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
> index 6105b1b6e49b..0af46683dd66 100644
> --- a/arch/s390/pci/pci.c
> +++ b/arch/s390/pci/pci.c
> @@ -39,8 +39,9 @@
>  static LIST_HEAD(zpci_list);
>  static DEFINE_SPINLOCK(zpci_list_lock);
>  
> -static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
> +static DECLARE_BITMAP(zpci_domain, ZPCI_DOMAIN_BITMAP_SIZE);
>  static DEFINE_SPINLOCK(zpci_domain_lock);
> +static unsigned int zpci_num_domains_allocated;
>  
>  #define ZPCI_IOMAP_ENTRIES						\
>  	min(((unsigned long) ZPCI_NR_DEVICES * PCI_BAR_COUNT / 2),	\
> @@ -651,39 +652,44 @@ struct dev_pm_ops pcibios_pm_ops = {
>  
>  static int zpci_alloc_domain(struct zpci_dev *zdev)
>  {
> +	spin_lock(&zpci_domain_lock);
> +	if (zpci_num_domains_allocated > (ZPCI_NR_DEVICES - 1)) {
> +		spin_unlock(&zpci_domain_lock);
> +		pr_err("Adding PCI function %08x failed because the configured limit of %d is reached\n",
> +			zdev->fid, ZPCI_NR_DEVICES);
> +		return -ENOSPC;
> +	}
> +
>  	if (zpci_unique_uid) {
>  		zdev->domain = (u16) zdev->uid;
> -		if (zdev->domain >= ZPCI_NR_DEVICES)
> -			return 0;
> -
> -		spin_lock(&zpci_domain_lock);
>  		if (test_bit(zdev->domain, zpci_domain)) {
>  			spin_unlock(&zpci_domain_lock);
> +			pr_err("Adding PCI function %08x failed because domain %04x is already assigned\n",
> +				zdev->fid, zdev->domain);
>  			return -EEXIST;
>  		}
>  		set_bit(zdev->domain, zpci_domain);
> +		zpci_num_domains_allocated++;
>  		spin_unlock(&zpci_domain_lock);
>  		return 0;
>  	}
> -
> -	spin_lock(&zpci_domain_lock);
> +	/*
> +	 * We can always auto allocate domains below ZPCI_NR_DEVICES.
> +	 * There is either a free domain or we have reached the maximum in
> +	 * which case we would have bailed earlier.
> +	 */
>  	zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
> -	if (zdev->domain == ZPCI_NR_DEVICES) {
> -		spin_unlock(&zpci_domain_lock);
> -		return -ENOSPC;
> -	}
>  	set_bit(zdev->domain, zpci_domain);
> +	zpci_num_domains_allocated++;
>  	spin_unlock(&zpci_domain_lock);
>  	return 0;
>  }
>  
>  static void zpci_free_domain(struct zpci_dev *zdev)
>  {
> -	if (zdev->domain >= ZPCI_NR_DEVICES)
> -		return;
> -
>  	spin_lock(&zpci_domain_lock);
>  	clear_bit(zdev->domain, zpci_domain);
> +	zpci_num_domains_allocated--;
>  	spin_unlock(&zpci_domain_lock);
>  }
>  
>
diff mbox series

Patch

diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index 6087a4e9b2bf..7850e8c8c79a 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -28,6 +28,7 @@  int pci_proc_domain(struct pci_bus *);
 
 #define ZPCI_NR_DMA_SPACES		1
 #define ZPCI_NR_DEVICES			CONFIG_PCI_NR_FUNCTIONS
+#define ZPCI_DOMAIN_BITMAP_SIZE		(1 << 16)
 
 /* PCI Function Controls */
 #define ZPCI_FC_FN_ENABLED		0x80
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 6105b1b6e49b..0af46683dd66 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -39,8 +39,9 @@ 
 static LIST_HEAD(zpci_list);
 static DEFINE_SPINLOCK(zpci_list_lock);
 
-static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
+static DECLARE_BITMAP(zpci_domain, ZPCI_DOMAIN_BITMAP_SIZE);
 static DEFINE_SPINLOCK(zpci_domain_lock);
+static unsigned int zpci_num_domains_allocated;
 
 #define ZPCI_IOMAP_ENTRIES						\
 	min(((unsigned long) ZPCI_NR_DEVICES * PCI_BAR_COUNT / 2),	\
@@ -651,39 +652,44 @@  struct dev_pm_ops pcibios_pm_ops = {
 
 static int zpci_alloc_domain(struct zpci_dev *zdev)
 {
+	spin_lock(&zpci_domain_lock);
+	if (zpci_num_domains_allocated > (ZPCI_NR_DEVICES - 1)) {
+		spin_unlock(&zpci_domain_lock);
+		pr_err("Adding PCI function %08x failed because the configured limit of %d is reached\n",
+			zdev->fid, ZPCI_NR_DEVICES);
+		return -ENOSPC;
+	}
+
 	if (zpci_unique_uid) {
 		zdev->domain = (u16) zdev->uid;
-		if (zdev->domain >= ZPCI_NR_DEVICES)
-			return 0;
-
-		spin_lock(&zpci_domain_lock);
 		if (test_bit(zdev->domain, zpci_domain)) {
 			spin_unlock(&zpci_domain_lock);
+			pr_err("Adding PCI function %08x failed because domain %04x is already assigned\n",
+				zdev->fid, zdev->domain);
 			return -EEXIST;
 		}
 		set_bit(zdev->domain, zpci_domain);
+		zpci_num_domains_allocated++;
 		spin_unlock(&zpci_domain_lock);
 		return 0;
 	}
-
-	spin_lock(&zpci_domain_lock);
+	/*
+	 * We can always auto allocate domains below ZPCI_NR_DEVICES.
+	 * There is either a free domain or we have reached the maximum in
+	 * which case we would have bailed earlier.
+	 */
 	zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
-	if (zdev->domain == ZPCI_NR_DEVICES) {
-		spin_unlock(&zpci_domain_lock);
-		return -ENOSPC;
-	}
 	set_bit(zdev->domain, zpci_domain);
+	zpci_num_domains_allocated++;
 	spin_unlock(&zpci_domain_lock);
 	return 0;
 }
 
 static void zpci_free_domain(struct zpci_dev *zdev)
 {
-	if (zdev->domain >= ZPCI_NR_DEVICES)
-		return;
-
 	spin_lock(&zpci_domain_lock);
 	clear_bit(zdev->domain, zpci_domain);
+	zpci_num_domains_allocated--;
 	spin_unlock(&zpci_domain_lock);
 }