diff mbox

[v4,00/11] PCI VPD access fixes

Message ID 20160223003444.10635.20204.stgit@bhelgaas-glaptop2.roam.corp.google.com
State Accepted
Headers show

Commit Message

Bjorn Helgaas Feb. 23, 2016, 12:46 a.m. UTC
Hi Hannes,

This is a revision of your v3 series:
http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de

Here's the description from your v3 posting:

  the current PCI VPD page access assumes that the entire possible VPD
  data is readable. However, the spec only guarantees a VPD data up to
  the 'end' marker, with everything beyond that being undefined.
  This causes a system lockup on certain devices.

  With this patch we always set the VPD sysfs attribute size to '0', and
  calculate the available VPD size on the first access.
  If no valid data can be read an I/O error is returned.

  I've also included the patch from Babu to blacklists devices which
  are known to lockup when accessing the VPD data.

I tweaked a few things, mostly whitespace and printk changes.  The
appended diff shows the changes I made.

I added some patches on top to clean up and simplify the VPD code.
These shouldn't make any functional difference unless I've made a
mistake.  I've built these, but I don't really have a way to test
them.

I am still waiting for bugzilla links from Babu for the blacklist
patch.

Bjorn

---

Babu Moger (1):
      FIXME need bugzilla link

Bjorn Helgaas (7):
      PCI: Use bitfield instead of bool for struct pci_vpd_pci22.busy
      PCI: Move pci_read_vpd() and pci_write_vpd() close to other VPD code
      PCI: Move pci_vpd_release() from header file to pci/access.c
      PCI: Remove struct pci_vpd_ops.release function pointer
      PCI: Rename VPD symbols to remove unnecessary "pci22"
      PCI: Fold struct pci_vpd_pci22 into struct pci_vpd
      PCI: Sleep rather than busy-wait for VPD access completion

Hannes Reinecke (3):
      PCI: Update VPD definitions
      PCI: Allow access to VPD attributes with size 0
      PCI: Determine actual VPD size on first access


 drivers/pci/access.c    |  240 ++++++++++++++++++++++++++++++-----------------
 drivers/pci/pci-sysfs.c |   22 +++-
 drivers/pci/pci.h       |   16 ++-
 drivers/pci/probe.c     |    2 
 drivers/pci/quirks.c    |   29 ++++++
 include/linux/pci.h     |   27 +++++
 6 files changed, 231 insertions(+), 105 deletions(-)


Below are the changes I made to your v3 series:

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Bjorn Helgaas Feb. 23, 2016, 2:07 p.m. UTC | #1
Hi Hannes,

Thanks for taking a look at the rest of these.

On Mon, Feb 22, 2016 at 06:46:23PM -0600, Bjorn Helgaas wrote:
> Hi Hannes,
> 
> This is a revision of your v3 series:
> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
> 
> Here's the description from your v3 posting:
> 
>   the current PCI VPD page access assumes that the entire possible VPD
>   data is readable. However, the spec only guarantees a VPD data up to
>   the 'end' marker, with everything beyond that being undefined.
>   This causes a system lockup on certain devices.
> 
>   With this patch we always set the VPD sysfs attribute size to '0', and
>   calculate the available VPD size on the first access.
>   If no valid data can be read an I/O error is returned.

Just to see if I have this right: the VPD file size in sysfs will
always appear as zero, regardless of whether it has been read or
written, right?  I don't think the user-visible size should change.

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjorn Helgaas Feb. 23, 2016, 5:11 p.m. UTC | #2
On Mon, Feb 22, 2016 at 06:46:23PM -0600, Bjorn Helgaas wrote:
> Hi Hannes,
> 
> This is a revision of your v3 series:
> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
> 
> Here's the description from your v3 posting:
> 
>   the current PCI VPD page access assumes that the entire possible VPD
>   data is readable. However, the spec only guarantees a VPD data up to
>   the 'end' marker, with everything beyond that being undefined.
>   This causes a system lockup on certain devices.
> 
>   With this patch we always set the VPD sysfs attribute size to '0', and
>   calculate the available VPD size on the first access.
>   If no valid data can be read an I/O error is returned.
> 
>   I've also included the patch from Babu to blacklists devices which
>   are known to lockup when accessing the VPD data.
> 
> I tweaked a few things, mostly whitespace and printk changes.  The
> appended diff shows the changes I made.
> 
> I added some patches on top to clean up and simplify the VPD code.
> These shouldn't make any functional difference unless I've made a
> mistake.  I've built these, but I don't really have a way to test
> them.
> 
> I am still waiting for bugzilla links from Babu for the blacklist
> patch.
> 
> Bjorn
> 
> ---
> 
> Babu Moger (1):
>       FIXME need bugzilla link
> 
> Bjorn Helgaas (7):
>       PCI: Use bitfield instead of bool for struct pci_vpd_pci22.busy
>       PCI: Move pci_read_vpd() and pci_write_vpd() close to other VPD code
>       PCI: Move pci_vpd_release() from header file to pci/access.c
>       PCI: Remove struct pci_vpd_ops.release function pointer
>       PCI: Rename VPD symbols to remove unnecessary "pci22"
>       PCI: Fold struct pci_vpd_pci22 into struct pci_vpd
>       PCI: Sleep rather than busy-wait for VPD access completion
> 
> Hannes Reinecke (3):
>       PCI: Update VPD definitions
>       PCI: Allow access to VPD attributes with size 0
>       PCI: Determine actual VPD size on first access
> 
> 
>  drivers/pci/access.c    |  240 ++++++++++++++++++++++++++++++-----------------
>  drivers/pci/pci-sysfs.c |   22 +++-
>  drivers/pci/pci.h       |   16 ++-
>  drivers/pci/probe.c     |    2 
>  drivers/pci/quirks.c    |   29 ++++++
>  include/linux/pci.h     |   27 +++++
>  6 files changed, 231 insertions(+), 105 deletions(-)

I added Hannes' reviewed-by and applied these, with the exception of
"PCI: Prevent VPD access for buggy devices" (I'm waiting for bugzilla
links for those quirks), to pci/vpd for v4.6.

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hannes Reinecke Feb. 23, 2016, 9:48 p.m. UTC | #3
On 02/23/2016 10:07 PM, Bjorn Helgaas wrote:
> Hi Hannes,
> 
> Thanks for taking a look at the rest of these.
> 
> On Mon, Feb 22, 2016 at 06:46:23PM -0600, Bjorn Helgaas wrote:
>> Hi Hannes,
>>
>> This is a revision of your v3 series:
>> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
>>
>> Here's the description from your v3 posting:
>>
>>   the current PCI VPD page access assumes that the entire possible VPD
>>   data is readable. However, the spec only guarantees a VPD data up to
>>   the 'end' marker, with everything beyond that being undefined.
>>   This causes a system lockup on certain devices.
>>
>>   With this patch we always set the VPD sysfs attribute size to '0', and
>>   calculate the available VPD size on the first access.
>>   If no valid data can be read an I/O error is returned.
> 
> Just to see if I have this right: the VPD file size in sysfs will
> always appear as zero, regardless of whether it has been read or
> written, right?  I don't think the user-visible size should change.
> 
That is correct.
As the actual size is evaluated on the first access, we don't have it
available when creating the sysfs attribute itself.
And when using the nominal size of 32k some bright program might try to
jump to somewhere in the middle of the data, which will make calculating
the validity of this horribly complex.
Setting it to '0' is an easy way of avoiding this kinda games.

So yes, there will be a user-visible change, but it shouldn't affect the
programs accessing this attribute.
lspci works happily with these changes

Cheers,

Hannes
Bjorn Helgaas Feb. 23, 2016, 10:36 p.m. UTC | #4
On Wed, Feb 24, 2016 at 05:48:33AM +0800, Hannes Reinecke wrote:
> On 02/23/2016 10:07 PM, Bjorn Helgaas wrote:
> > Hi Hannes,
> > 
> > Thanks for taking a look at the rest of these.
> > 
> > On Mon, Feb 22, 2016 at 06:46:23PM -0600, Bjorn Helgaas wrote:
> >> Hi Hannes,
> >>
> >> This is a revision of your v3 series:
> >> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
> >>
> >> Here's the description from your v3 posting:
> >>
> >>   the current PCI VPD page access assumes that the entire possible VPD
> >>   data is readable. However, the spec only guarantees a VPD data up to
> >>   the 'end' marker, with everything beyond that being undefined.
> >>   This causes a system lockup on certain devices.
> >>
> >>   With this patch we always set the VPD sysfs attribute size to '0', and
> >>   calculate the available VPD size on the first access.
> >>   If no valid data can be read an I/O error is returned.
> > 
> > Just to see if I have this right: the VPD file size in sysfs will
> > always appear as zero, regardless of whether it has been read or
> > written, right?  I don't think the user-visible size should change.
> > 
> That is correct.
> As the actual size is evaluated on the first access, we don't have it
> available when creating the sysfs attribute itself.
> And when using the nominal size of 32k some bright program might try to
> jump to somewhere in the middle of the data, which will make calculating
> the validity of this horribly complex.
> Setting it to '0' is an easy way of avoiding this kinda games.
> 
> So yes, there will be a user-visible change, but it shouldn't affect the
> programs accessing this attribute.
> lspci works happily with these changes

What is the user-visible change?  Here's what I'm thinking.  If we do
this:

  ls -l /sys/.../vpd
  dd if=/sys/.../vpd bs=1 count=1
  ls -l /sys/.../vpd

Do we see different sizes from the two "ls" invocations?  My thought
is that we should see '0' both times, because I don't really think
that output should change depending on previous actions of this user
or other users.

I though you were confirming that we do always see '0', but then you
mentioned a user-visible change; is there a different change you're
thinking of?

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hannes Reinecke Feb. 24, 2016, 12:30 a.m. UTC | #5
On 02/24/2016 06:36 AM, Bjorn Helgaas wrote:
> On Wed, Feb 24, 2016 at 05:48:33AM +0800, Hannes Reinecke wrote:
>> On 02/23/2016 10:07 PM, Bjorn Helgaas wrote:
>>> Hi Hannes,
>>>
>>> Thanks for taking a look at the rest of these.
>>>
>>> On Mon, Feb 22, 2016 at 06:46:23PM -0600, Bjorn Helgaas wrote:
>>>> Hi Hannes,
>>>>
>>>> This is a revision of your v3 series:
>>>> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
>>>>
>>>> Here's the description from your v3 posting:
>>>>
>>>>   the current PCI VPD page access assumes that the entire possible VPD
>>>>   data is readable. However, the spec only guarantees a VPD data up to
>>>>   the 'end' marker, with everything beyond that being undefined.
>>>>   This causes a system lockup on certain devices.
>>>>
>>>>   With this patch we always set the VPD sysfs attribute size to '0', and
>>>>   calculate the available VPD size on the first access.
>>>>   If no valid data can be read an I/O error is returned.
>>>
>>> Just to see if I have this right: the VPD file size in sysfs will
>>> always appear as zero, regardless of whether it has been read or
>>> written, right?  I don't think the user-visible size should change.
>>>
>> That is correct.
>> As the actual size is evaluated on the first access, we don't have it
>> available when creating the sysfs attribute itself.
>> And when using the nominal size of 32k some bright program might try to
>> jump to somewhere in the middle of the data, which will make calculating
>> the validity of this horribly complex.
>> Setting it to '0' is an easy way of avoiding this kinda games.
>>
>> So yes, there will be a user-visible change, but it shouldn't affect the
>> programs accessing this attribute.
>> lspci works happily with these changes
> 
> What is the user-visible change?  Here's what I'm thinking.  If we do
> this:
> 
>   ls -l /sys/.../vpd
>   dd if=/sys/.../vpd bs=1 count=1
>   ls -l /sys/.../vpd
> 
> Do we see different sizes from the two "ls" invocations?  My thought
> is that we should see '0' both times, because I don't really think
> that output should change depending on previous actions of this user
> or other users.
> 
Originally we have:

# ls -l 0000:07:00.0/vpd
-rw------- 1 root root 32768 Feb 24 01:29 0000:07:00.0/vpd

and with this patchset we have:

# ls -l 0000:07:00.0/vpd
-rw------- 1 root root 0 Feb 24 01:29 0000:07:00.0/vpd

So only programs doing a 'stat' on the device node will see a difference.

Cheers,

Hannes
Bjorn Helgaas Feb. 24, 2016, 12:45 a.m. UTC | #6
On Wed, Feb 24, 2016 at 08:30:38AM +0800, Hannes Reinecke wrote:
> On 02/24/2016 06:36 AM, Bjorn Helgaas wrote:
> > On Wed, Feb 24, 2016 at 05:48:33AM +0800, Hannes Reinecke wrote:
> >> On 02/23/2016 10:07 PM, Bjorn Helgaas wrote:
> >>> Hi Hannes,
> >>>
> >>> Thanks for taking a look at the rest of these.
> >>>
> >>> On Mon, Feb 22, 2016 at 06:46:23PM -0600, Bjorn Helgaas wrote:
> >>>> Hi Hannes,
> >>>>
> >>>> This is a revision of your v3 series:
> >>>> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
> >>>>
> >>>> Here's the description from your v3 posting:
> >>>>
> >>>>   the current PCI VPD page access assumes that the entire possible VPD
> >>>>   data is readable. However, the spec only guarantees a VPD data up to
> >>>>   the 'end' marker, with everything beyond that being undefined.
> >>>>   This causes a system lockup on certain devices.
> >>>>
> >>>>   With this patch we always set the VPD sysfs attribute size to '0', and
> >>>>   calculate the available VPD size on the first access.
> >>>>   If no valid data can be read an I/O error is returned.
> >>>
> >>> Just to see if I have this right: the VPD file size in sysfs will
> >>> always appear as zero, regardless of whether it has been read or
> >>> written, right?  I don't think the user-visible size should change.
> >>>
> >> That is correct.
> >> As the actual size is evaluated on the first access, we don't have it
> >> available when creating the sysfs attribute itself.
> >> And when using the nominal size of 32k some bright program might try to
> >> jump to somewhere in the middle of the data, which will make calculating
> >> the validity of this horribly complex.
> >> Setting it to '0' is an easy way of avoiding this kinda games.
> >>
> >> So yes, there will be a user-visible change, but it shouldn't affect the
> >> programs accessing this attribute.
> >> lspci works happily with these changes
> > 
> > What is the user-visible change?  Here's what I'm thinking.  If we do
> > this:
> > 
> >   ls -l /sys/.../vpd
> >   dd if=/sys/.../vpd bs=1 count=1
> >   ls -l /sys/.../vpd
> > 
> > Do we see different sizes from the two "ls" invocations?  My thought
> > is that we should see '0' both times, because I don't really think
> > that output should change depending on previous actions of this user
> > or other users.
> > 
> Originally we have:
> 
> # ls -l 0000:07:00.0/vpd
> -rw------- 1 root root 32768 Feb 24 01:29 0000:07:00.0/vpd
> 
> and with this patchset we have:
> 
> # ls -l 0000:07:00.0/vpd
> -rw------- 1 root root 0 Feb 24 01:29 0000:07:00.0/vpd
> 
> So only programs doing a 'stat' on the device node will see a difference.

Oh, I think I see: you mean there's a user-visible difference between
the current tree and the tree with your patches applied.

I was hoping that on a single kernel, the "vpd" attribute size was
always the same, regardless of whether anybody had read or written it.

If we always report zero size for all "vpd" files, I think that's OK.

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Seymour, Shane M Feb. 24, 2016, 4:52 a.m. UTC | #7
Retested v4. For the series tested two PCIe cards that support VPD:

0d:00.0 Serial Attached SCSI controller: LSI Logic / Symbios Logic SAS2308 PCI-Express Fusion-MPT SAS-2 (rev 05)
1e:00.0 Fibre Channel: QLogic Corp. ISP2432-based 4Gb Fibre Channel to PCI Express HBA (rev 03)

The first card exposes 32KiB of NUL bytes as VPD and the second 154 bytes of non-NUL data repeating every 4KiB for 32KiB before the change. After the change the first  card returns no data for VPD (since it contains only NUL bytes) and the second the expected 154 bytes of data.

The following warning message comes out as expected for the LSI card (changed from dyn debug controlled message):

[  614.475984] mpt3sas 0000:0d:00.0: invalid short VPD tag 00 at offset 1

lspci works correctly with the patch in the kernel. The only change on the system (apart from the size of the vpd file) was that with the LSI card lspci with -vvvv as root it now says "Not readable"  instead of "Unknown small resource type 00, will not decode more." in the VPD section.
---
Tested-by: Shane Seymour <shane.seymour@hpe.com>
Babu Moger Feb. 29, 2016, 5:27 p.m. UTC | #8
Hi Bjorn,

On 2/22/2016 6:46 PM, Bjorn Helgaas wrote:
> Hi Hannes,
> 
> This is a revision of your v3 series:
> http://lkml.kernel.org/r/1455525722-122040-1-git-send-email-hare@suse.de
> 
> Here's the description from your v3 posting:
> 
>   the current PCI VPD page access assumes that the entire possible VPD
>   data is readable. However, the spec only guarantees a VPD data up to
>   the 'end' marker, with everything beyond that being undefined.
>   This causes a system lockup on certain devices.
> 
>   With this patch we always set the VPD sysfs attribute size to '0', and
>   calculate the available VPD size on the first access.
>   If no valid data can be read an I/O error is returned.
> 
>   I've also included the patch from Babu to blacklists devices which
>   are known to lockup when accessing the VPD data.
> 
> I tweaked a few things, mostly whitespace and printk changes.  The
> appended diff shows the changes I made.
> 
> I added some patches on top to clean up and simplify the VPD code.
> These shouldn't make any functional difference unless I've made a
> mistake.  I've built these, but I don't really have a way to test
> them.
> 
> I am still waiting for bugzilla links from Babu for the blacklist
> patch.

 Sorry. I was on vacation. Just back in office today. Here is the 
 Bugzilla link.  https://bugzilla.kernel.org/show_bug.cgi?id=110681

     
> 
> Bjorn
> 
> ---
> 
> Babu Moger (1):
>       FIXME need bugzilla link
> 
> Bjorn Helgaas (7):
>       PCI: Use bitfield instead of bool for struct pci_vpd_pci22.busy
>       PCI: Move pci_read_vpd() and pci_write_vpd() close to other VPD code
>       PCI: Move pci_vpd_release() from header file to pci/access.c
>       PCI: Remove struct pci_vpd_ops.release function pointer
>       PCI: Rename VPD symbols to remove unnecessary "pci22"
>       PCI: Fold struct pci_vpd_pci22 into struct pci_vpd
>       PCI: Sleep rather than busy-wait for VPD access completion
> 
> Hannes Reinecke (3):
>       PCI: Update VPD definitions
>       PCI: Allow access to VPD attributes with size 0
>       PCI: Determine actual VPD size on first access
> 
> 
>  drivers/pci/access.c    |  240 ++++++++++++++++++++++++++++++-----------------
>  drivers/pci/pci-sysfs.c |   22 +++-
>  drivers/pci/pci.h       |   16 ++-
>  drivers/pci/probe.c     |    2 
>  drivers/pci/quirks.c    |   29 ++++++
>  include/linux/pci.h     |   27 +++++
>  6 files changed, 231 insertions(+), 105 deletions(-)
> 
> 
> Below are the changes I made to your v3 series:
> 
> diff --git a/drivers/pci/access.c b/drivers/pci/access.c
> index 8b6f5a2..4850f06 100644
> --- a/drivers/pci/access.c
> +++ b/drivers/pci/access.c
> @@ -283,9 +283,9 @@ struct pci_vpd_pci22 {
>  	struct pci_vpd base;
>  	struct mutex lock;
>  	u16	flag;
> +	u8	cap;
>  	u8	busy:1;
>  	u8	valid:1;
> -	u8	cap;
>  };
>  
>  /**
> @@ -311,9 +311,9 @@ static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size)
>  			    (tag == PCI_VPD_LTIN_RW_DATA)) {
>  				if (pci_read_vpd(dev, off+1, 2,
>  						 &header[1]) != 2) {
> -					dev_dbg(&dev->dev,
> -						"invalid large VPD tag %02x size at offset %zu",
> -						tag, off + 1);
> +					dev_warn(&dev->dev,
> +						 "invalid large VPD tag %02x size at offset %zu",
> +						 tag, off + 1);
>  					return 0;
>  				}
>  				off += PCI_VPD_LRDT_TAG_SIZE +
> @@ -325,15 +325,17 @@ static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size)
>  				pci_vpd_srdt_size(header);
>  			tag = pci_vpd_srdt_tag(header);
>  		}
> +
>  		if (tag == PCI_VPD_STIN_END)	/* End tag descriptor */
>  			return off;
> +
>  		if ((tag != PCI_VPD_LTIN_ID_STRING) &&
>  		    (tag != PCI_VPD_LTIN_RO_DATA) &&
>  		    (tag != PCI_VPD_LTIN_RW_DATA)) {
> -			dev_dbg(&dev->dev,
> -				"invalid %s VPD tag %02x at offset %zu",
> -				(header[0] & PCI_VPD_LRDT) ? "large" : "short",
> -				tag, off);
> +			dev_warn(&dev->dev,
> +				 "invalid %s VPD tag %02x at offset %zu",
> +				 (header[0] & PCI_VPD_LRDT) ? "large" : "short",
> +				 tag, off);
>  			return 0;
>  		}
>  	}
> @@ -366,7 +368,7 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev)
>  			return ret;
>  
>  		if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
> -			vpd->busy = false;
> +			vpd->busy = 0;
>  			return 0;
>  		}
>  
> @@ -393,16 +395,18 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
>  	if (pos < 0)
>  		return -EINVAL;
>  
> -	if (!vpd->valid && vpd->base.len > 0) {
> -		vpd->valid = true;
> +	if (!vpd->valid) {
> +		vpd->valid = 1;
>  		vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len);
>  	}
> +
>  	if (vpd->base.len == 0)
>  		return -EIO;
>  
> +	if (pos >= vpd->base.len)
> +		return 0;
> +
>  	if (end > vpd->base.len) {
> -		if (pos > vpd->base.len)
> -			return 0;
>  		end = vpd->base.len;
>  		count = end - pos;
>  	}
> @@ -422,7 +426,7 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
>  						 pos & ~3);
>  		if (ret < 0)
>  			break;
> -		vpd->busy = true;
> +		vpd->busy = 1;
>  		vpd->flag = PCI_VPD_ADDR_F;
>  		ret = pci_vpd_pci22_wait(dev);
>  		if (ret < 0)
> @@ -459,10 +463,11 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count
>  	if (pos < 0 || (pos & 3) || (count & 3))
>  		return -EINVAL;
>  
> -	if (!vpd->valid && vpd->base.len > 0) {
> -		vpd->valid = true;
> +	if (!vpd->valid) {
> +		vpd->valid = 1;
>  		vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len);
>  	}
> +
>  	if (vpd->base.len == 0)
>  		return -EIO;
>  
> @@ -492,7 +497,7 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count
>  		if (ret < 0)
>  			break;
>  
> -		vpd->busy = true;
> +		vpd->busy = 1;
>  		vpd->flag = 0;
>  		ret = pci_vpd_pci22_wait(dev);
>  		if (ret < 0)
> @@ -572,8 +577,8 @@ int pci_vpd_pci22_init(struct pci_dev *dev)
>  		vpd->base.ops = &pci_vpd_pci22_ops;
>  	mutex_init(&vpd->lock);
>  	vpd->cap = cap;
> -	vpd->busy = false;
> -	vpd->valid = false;
> +	vpd->busy = 0;
> +	vpd->valid = 0;
>  	dev->vpd = &vpd->base;
>  	return 0;
>  }
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index df1178f..626c3b2 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -2135,45 +2135,31 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
>  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
>  
>  /*
> - * A read/write to sysfs entry ('/sys/bus/pci/devices/<id>/vpd')
> - * will dump 32k of data. The default length is set as 32768.
> - * Reading a full 32k will cause an access beyond the VPD end tag.
> - * The system behaviour at that point is mostly unpredictable.
> - * Apparently, some vendors have not implemented this VPD headers properly.
> - * Adding a generic function disable vpd data for these buggy adapters
> - * Add the DECLARE_PCI_FIXUP_FINAL line below with the specific with
> - * vendor and device of interest to use this quirk.
> + * If a device follows the VPD format spec, the PCI core will not read or
> + * write past the VPD End Tag.  But some vendors do not follow the VPD
> + * format spec, so we can't tell how much data is safe to access.  Devices
> + * may behave unpredictably if we access too much.  Blacklist these devices
> + * so we don't touch VPD at all.
>   */
>  static void quirk_blacklist_vpd(struct pci_dev *dev)
>  {
>  	if (dev->vpd) {
>  		dev->vpd->len = 0;
> -		dev_warn(&dev->dev, "PCI vpd access has been disabled due to firmware bug\n");
> +		dev_warn(&dev->dev, FW_BUG "VPD access disabled\n");
>  	}
>  }
>  
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d,
> -		quirk_blacklist_vpd);
> -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f,
> -		quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
>  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
>  		quirk_blacklist_vpd);
>  
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Babu Moger Feb. 29, 2016, 10:36 p.m. UTC | #9
On 2/23/2016 10:52 PM, Seymour, Shane M wrote:
> Retested v4. For the series tested two PCIe cards that support VPD:
> 
> 0d:00.0 Serial Attached SCSI controller: LSI Logic / Symbios Logic SAS2308 PCI-Express Fusion-MPT SAS-2 (rev 05)
> 1e:00.0 Fibre Channel: QLogic Corp. ISP2432-based 4Gb Fibre Channel to PCI Express HBA (rev 03)
> 
> The first card exposes 32KiB of NUL bytes as VPD and the second 154 bytes of non-NUL data repeating every 4KiB for 32KiB before the change. After the change the first  card returns no data for VPD (since it contains only NUL bytes) and the second the expected 154 bytes of data.
> 
> The following warning message comes out as expected for the LSI card (changed from dyn debug controlled message):
> 
> [  614.475984] mpt3sas 0000:0d:00.0: invalid short VPD tag 00 at offset 1
> 
> lspci works correctly with the patch in the kernel. The only change on the system (apart from the size of the vpd file) was that with the LSI card lspci with -vvvv as root it now says "Not readable"  instead of "Unknown small resource type 00, will not decode more." in the VPD section.
> ---
> Tested-by: Shane Seymour <shane.seymour@hpe.com>
> 

I have also retested the V4 series. Did not see any side effects.
Once again here is the bug I opened for this issue.
https://bugzilla.kernel.org/show_bug.cgi?id=110681
---
Tested-by: Babu Moger <babu.moger@oracle.com>
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 8b6f5a2..4850f06 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -283,9 +283,9 @@  struct pci_vpd_pci22 {
 	struct pci_vpd base;
 	struct mutex lock;
 	u16	flag;
+	u8	cap;
 	u8	busy:1;
 	u8	valid:1;
-	u8	cap;
 };
 
 /**
@@ -311,9 +311,9 @@  static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size)
 			    (tag == PCI_VPD_LTIN_RW_DATA)) {
 				if (pci_read_vpd(dev, off+1, 2,
 						 &header[1]) != 2) {
-					dev_dbg(&dev->dev,
-						"invalid large VPD tag %02x size at offset %zu",
-						tag, off + 1);
+					dev_warn(&dev->dev,
+						 "invalid large VPD tag %02x size at offset %zu",
+						 tag, off + 1);
 					return 0;
 				}
 				off += PCI_VPD_LRDT_TAG_SIZE +
@@ -325,15 +325,17 @@  static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size)
 				pci_vpd_srdt_size(header);
 			tag = pci_vpd_srdt_tag(header);
 		}
+
 		if (tag == PCI_VPD_STIN_END)	/* End tag descriptor */
 			return off;
+
 		if ((tag != PCI_VPD_LTIN_ID_STRING) &&
 		    (tag != PCI_VPD_LTIN_RO_DATA) &&
 		    (tag != PCI_VPD_LTIN_RW_DATA)) {
-			dev_dbg(&dev->dev,
-				"invalid %s VPD tag %02x at offset %zu",
-				(header[0] & PCI_VPD_LRDT) ? "large" : "short",
-				tag, off);
+			dev_warn(&dev->dev,
+				 "invalid %s VPD tag %02x at offset %zu",
+				 (header[0] & PCI_VPD_LRDT) ? "large" : "short",
+				 tag, off);
 			return 0;
 		}
 	}
@@ -366,7 +368,7 @@  static int pci_vpd_pci22_wait(struct pci_dev *dev)
 			return ret;
 
 		if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
-			vpd->busy = false;
+			vpd->busy = 0;
 			return 0;
 		}
 
@@ -393,16 +395,18 @@  static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
 	if (pos < 0)
 		return -EINVAL;
 
-	if (!vpd->valid && vpd->base.len > 0) {
-		vpd->valid = true;
+	if (!vpd->valid) {
+		vpd->valid = 1;
 		vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len);
 	}
+
 	if (vpd->base.len == 0)
 		return -EIO;
 
+	if (pos >= vpd->base.len)
+		return 0;
+
 	if (end > vpd->base.len) {
-		if (pos > vpd->base.len)
-			return 0;
 		end = vpd->base.len;
 		count = end - pos;
 	}
@@ -422,7 +426,7 @@  static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
 						 pos & ~3);
 		if (ret < 0)
 			break;
-		vpd->busy = true;
+		vpd->busy = 1;
 		vpd->flag = PCI_VPD_ADDR_F;
 		ret = pci_vpd_pci22_wait(dev);
 		if (ret < 0)
@@ -459,10 +463,11 @@  static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count
 	if (pos < 0 || (pos & 3) || (count & 3))
 		return -EINVAL;
 
-	if (!vpd->valid && vpd->base.len > 0) {
-		vpd->valid = true;
+	if (!vpd->valid) {
+		vpd->valid = 1;
 		vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len);
 	}
+
 	if (vpd->base.len == 0)
 		return -EIO;
 
@@ -492,7 +497,7 @@  static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count
 		if (ret < 0)
 			break;
 
-		vpd->busy = true;
+		vpd->busy = 1;
 		vpd->flag = 0;
 		ret = pci_vpd_pci22_wait(dev);
 		if (ret < 0)
@@ -572,8 +577,8 @@  int pci_vpd_pci22_init(struct pci_dev *dev)
 		vpd->base.ops = &pci_vpd_pci22_ops;
 	mutex_init(&vpd->lock);
 	vpd->cap = cap;
-	vpd->busy = false;
-	vpd->valid = false;
+	vpd->busy = 0;
+	vpd->valid = 0;
 	dev->vpd = &vpd->base;
 	return 0;
 }
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index df1178f..626c3b2 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2135,45 +2135,31 @@  static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
 
 /*
- * A read/write to sysfs entry ('/sys/bus/pci/devices/<id>/vpd')
- * will dump 32k of data. The default length is set as 32768.
- * Reading a full 32k will cause an access beyond the VPD end tag.
- * The system behaviour at that point is mostly unpredictable.
- * Apparently, some vendors have not implemented this VPD headers properly.
- * Adding a generic function disable vpd data for these buggy adapters
- * Add the DECLARE_PCI_FIXUP_FINAL line below with the specific with
- * vendor and device of interest to use this quirk.
+ * If a device follows the VPD format spec, the PCI core will not read or
+ * write past the VPD End Tag.  But some vendors do not follow the VPD
+ * format spec, so we can't tell how much data is safe to access.  Devices
+ * may behave unpredictably if we access too much.  Blacklist these devices
+ * so we don't touch VPD at all.
  */
 static void quirk_blacklist_vpd(struct pci_dev *dev)
 {
 	if (dev->vpd) {
 		dev->vpd->len = 0;
-		dev_warn(&dev->dev, "PCI vpd access has been disabled due to firmware bug\n");
+		dev_warn(&dev->dev, FW_BUG "VPD access disabled\n");
 	}
 }
 
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d,
-		quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f,
-		quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
 		quirk_blacklist_vpd);