[PATCH 0/8] PCI/VPD: Extend PCI VPD API

STALE1810d

11 messages, 2 authors, 2021-08-20 · open the first message on its own page

[PATCH 0/8] PCI/VPD: Extend PCI VPD API

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 18:58:31

This series adds three functions to the PCI VPD API that help to
simplify driver code. First users are sfc and tg3 drivers because
I have test hw. The other users of the VPD API will benefit from a
migration as well.
I'd propose to apply this series via the PCI tree.

Added API calls:

pci_vpd_alloc()
Dynamically allocates a properly sized buffer and reads the VPD into it.

pci_vpd_find_ro_info_keyword()
Locates an info field keyword in the VPD RO section.
pci_vpd_find_info_keyword() can be removed once all
users have been migrated.

pci_vpd_check_csum()
Check VPD checksum based on algorithm defined in the PCI specification.

Tested on a SFN6122F and a BCM95719 card.

Heiner Kallweit (8):
  PCI/VPD: Add pci_vpd_alloc
  PCI/VPD: Add pci_vpd_find_ro_info_keyword and pci_vpd_check_csum
  PCI/VPD: Add missing VPD RO field keywords
  sfc: Use new function pci_vpd_alloc
  sfc: Use new VPD API function pci_vpd_find_ro_info_keyword
  tg3: Use new function pci_vpd_alloc
  tg3: Use new function pci_vpd_check_csum
  tg3: Use new function pci_vpd_find_ro_info_keyword

 drivers/net/ethernet/broadcom/tg3.c | 115 +++++++---------------------
 drivers/net/ethernet/broadcom/tg3.h |   1 -
 drivers/net/ethernet/sfc/efx.c      |  78 +++++--------------
 drivers/pci/vpd.c                   |  82 ++++++++++++++++++++
 include/linux/pci.h                 |  32 ++++++++
 5 files changed, 163 insertions(+), 145 deletions(-)

-- 
2.32.0

[PATCH 1/8] PCI/VPD: Add pci_vpd_alloc

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:04

Several users of the VPD API use a fixed-size buffer and read the VPD
into it for further usage. This requires special handling for the case
that the buffer isn't big enough to hold the full VPD data.
Also the buffer is often allocated on the stack what's not too nice.
So let's extend the VPD API with a function that dynamically allocates
a properly sized buffer.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/pci/vpd.c   | 26 ++++++++++++++++++++++++++
 include/linux/pci.h |  9 +++++++++
 2 files changed, 35 insertions(+)
diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
index 3b0425fb4..7c3a09737 100644
--- a/drivers/pci/vpd.c
+++ b/drivers/pci/vpd.c
@@ -270,6 +270,32 @@ const struct attribute_group pci_dev_vpd_attr_group = {
 	.is_bin_visible = vpd_attr_is_visible,
 };
 
+void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size)
+{
+	unsigned int len = dev->vpd.len;
+	void *buf;
+	int cnt;
+
+	if (!dev->vpd.cap)
+		return ERR_PTR(-ENODEV);
+
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	cnt = pci_read_vpd(dev, 0, len, buf);
+	if (cnt != len) {
+		kfree(buf);
+		return ERR_PTR(-EIO);
+	}
+
+	if (size)
+		*size = len;
+
+	return buf;
+}
+EXPORT_SYMBOL_GPL(pci_vpd_alloc);
+
 int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt)
 {
 	int i = 0;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index e752cc39a..3061cc943 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2330,6 +2330,15 @@ static inline u8 pci_vpd_info_field_size(const u8 *info_field)
 	return info_field[2];
 }
 
+/**
+ * pci_vpd_alloc - Allocates buffer and reads VPD into it
+ * @dev: PCI device
+ * @size: pointer to field where VPD length is returned
+ *
+ * Returns pointer to allocated buffer or an ERR_PTR in case of failure
+ */
+void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size);
+
 /**
  * pci_vpd_find_tag - Locates the Resource Data Type tag provided
  * @buf: Pointer to buffered vpd data
-- 
2.32.0

[PATCH 2/8] PCI/VPD: Add pci_vpd_find_ro_info_keyword and pci_vpd_check_csum

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:14

All Users of pci_vpd_find_info_keyword() are interested in the VPD RO
section only. In addition all calls are followed by the same
activities to calculate start of tag data area and size of the data
area. Therefore add an API function that combines these
functionalities. pci_vpd_find_info_keyword() can be phased out once
all users are converted.

VPD checksum information and checksum calculation are part of the PCI
standard. Therefore checksum handling can and should be moved into
PCI VPD core. Add an API function pci_vpd_check_csum() for that.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/pci/vpd.c   | 56 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h | 21 +++++++++++++++++
 2 files changed, 77 insertions(+)
diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
index 7c3a09737..01e575947 100644
--- a/drivers/pci/vpd.c
+++ b/drivers/pci/vpd.c
@@ -380,6 +380,62 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void
 }
 EXPORT_SYMBOL(pci_write_vpd);
 
+int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len,
+				 const char *kw, unsigned int *size)
+{
+	int ro_start, infokw_start;
+	unsigned int ro_len, infokw_size;
+
+	ro_start = pci_vpd_find_tag(buf, len, PCI_VPD_LRDT_RO_DATA);
+	if (ro_start < 0)
+		return ro_start;
+
+	ro_len = pci_vpd_lrdt_size(buf + ro_start);
+	ro_start += PCI_VPD_LRDT_TAG_SIZE;
+
+	if (ro_start + ro_len > len)
+		ro_len = len - ro_start;
+
+	infokw_start = pci_vpd_find_info_keyword(buf, ro_start, ro_len, kw);
+	if (infokw_start < 0)
+		return infokw_start;
+
+	infokw_size = pci_vpd_info_field_size(buf + infokw_start);
+	infokw_start += PCI_VPD_INFO_FLD_HDR_SIZE;
+
+	if (infokw_start + infokw_size > len)
+		return -EINVAL;
+
+	if (size)
+		*size = infokw_size;
+
+	return infokw_start;
+}
+EXPORT_SYMBOL_GPL(pci_vpd_find_ro_info_keyword);
+
+int pci_vpd_check_csum(const void *buf, unsigned int len)
+{
+	const u8 *vpd = buf;
+	unsigned int size;
+	u8 csum = 0;
+	int rv_start;
+
+	rv_start = pci_vpd_find_ro_info_keyword(buf, len, PCI_VPD_RO_KEYWORD_CHKSUM, &size);
+	if (rv_start == -ENOENT) /* no checksum in VPD */
+		return 1;
+	else if (rv_start < 0)
+		return rv_start;
+
+	if (!size)
+		return -EINVAL;
+
+	while (rv_start >= 0)
+		csum += vpd[rv_start--];
+
+	return csum ? -EILSEQ : 0;
+}
+EXPORT_SYMBOL_GPL(pci_vpd_check_csum);
+
 #ifdef CONFIG_PCI_QUIRKS
 /*
  * Quirk non-zero PCI functions to route VPD access through function 0 for
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 3061cc943..a82f5910f 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2363,6 +2363,27 @@ int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt);
 int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
 			      unsigned int len, const char *kw);
 
+/**
+ * pci_vpd_check_csum - Check VPD checksum
+ * @buf: Pointer to buffered vpd data
+ * @len: VPD size
+ *
+ * Returns 1 if VPD has no checksum, otherwise 0 or an errno
+ */
+int pci_vpd_check_csum(const void *buf, unsigned int len);
+
+/**
+ * pci_vpd_find_ro_info_keyword - Locates an info field keyword in VPD RO section
+ * @buf: Pointer to buffered vpd data
+ * @len: The length of the buffer area in which to search
+ * @kw: The keyword to search for
+ * @size: pointer to field where length of found keyword data is returned
+ *
+ * Returns the index of the information field keyword data or -ENOENT if not found.
+ */
+int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len,
+				 const char *kw, unsigned int *size);
+
 /* PCI <-> OF binding helpers */
 #ifdef CONFIG_OF
 struct device_node;
-- 
2.32.0

[PATCH 3/8] PCI/VPD: Add missing VPD RO field keywords

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:21

This patch adds further keywords that are defined in the PCI spec.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 include/linux/pci.h | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index a82f5910f..629c810ae 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2274,6 +2274,8 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask);
 #define PCI_VPD_RO_KEYWORD_MFR_ID	"MN"
 #define PCI_VPD_RO_KEYWORD_VENDOR0	"V0"
 #define PCI_VPD_RO_KEYWORD_CHKSUM	"RV"
+#define PCI_VPD_RO_KEYWORD_ECLEVEL	"EC"
+#define PCI_VPD_RO_KEYWORD_EXTCAP	"CP"
 
 /**
  * pci_vpd_lrdt_size - Extracts the Large Resource Data Type length
-- 
2.32.0

[PATCH 4/8] sfc: Use new function pci_vpd_alloc

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:30

Use new VPD API function pci_vpd_alloc() for dynamically allocating
a properly sized buffer and reading the full VPD data into it.
This avoids having to allocate a buffer on the stack, and we don't
have to make any assumptions on VPD size and location of information
in VPD any longer.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/net/ethernet/sfc/efx.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index a295e2621..e04a63109 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -900,21 +900,18 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
 
 /* NIC VPD information
  * Called during probe to display the part number of the
- * installed NIC.  VPD is potentially very large but this should
- * always appear within the first 512 bytes.
+ * installed NIC.
  */
-#define SFC_VPD_LEN 512
 static void efx_probe_vpd_strings(struct efx_nic *efx)
 {
 	struct pci_dev *dev = efx->pci_dev;
-	char vpd_data[SFC_VPD_LEN];
-	ssize_t vpd_size;
 	int ro_start, ro_size, i, j;
+	unsigned int vpd_size;
+	u8 *vpd_data;
 
-	/* Get the vpd data from the device */
-	vpd_size = pci_read_vpd(dev, 0, sizeof(vpd_data), vpd_data);
-	if (vpd_size <= 0) {
-		netif_err(efx, drv, efx->net_dev, "Unable to read VPD\n");
+	vpd_data = pci_vpd_alloc(dev, &vpd_size);
+	if (IS_ERR(vpd_data)) {
+		pci_warn(dev, "Unable to read VPD\n");
 		return;
 	}
 
@@ -922,7 +919,7 @@ static void efx_probe_vpd_strings(struct efx_nic *efx)
 	ro_start = pci_vpd_find_tag(vpd_data, vpd_size, PCI_VPD_LRDT_RO_DATA);
 	if (ro_start < 0) {
 		netif_err(efx, drv, efx->net_dev, "VPD Read-only not found\n");
-		return;
+		goto out;
 	}
 
 	ro_size = pci_vpd_lrdt_size(&vpd_data[ro_start]);
@@ -935,14 +932,14 @@ static void efx_probe_vpd_strings(struct efx_nic *efx)
 	i = pci_vpd_find_info_keyword(vpd_data, i, j, "PN");
 	if (i < 0) {
 		netif_err(efx, drv, efx->net_dev, "Part number not found\n");
-		return;
+		goto out;
 	}
 
 	j = pci_vpd_info_field_size(&vpd_data[i]);
 	i += PCI_VPD_INFO_FLD_HDR_SIZE;
 	if (i + j > vpd_size) {
 		netif_err(efx, drv, efx->net_dev, "Incomplete part number\n");
-		return;
+		goto out;
 	}
 
 	netif_info(efx, drv, efx->net_dev,
@@ -953,21 +950,23 @@ static void efx_probe_vpd_strings(struct efx_nic *efx)
 	i = pci_vpd_find_info_keyword(vpd_data, i, j, "SN");
 	if (i < 0) {
 		netif_err(efx, drv, efx->net_dev, "Serial number not found\n");
-		return;
+		goto out;
 	}
 
 	j = pci_vpd_info_field_size(&vpd_data[i]);
 	i += PCI_VPD_INFO_FLD_HDR_SIZE;
 	if (i + j > vpd_size) {
 		netif_err(efx, drv, efx->net_dev, "Incomplete serial number\n");
-		return;
+		goto out;
 	}
 
 	efx->vpd_sn = kmalloc(j + 1, GFP_KERNEL);
 	if (!efx->vpd_sn)
-		return;
+		goto out;
 
 	snprintf(efx->vpd_sn, j + 1, "%s", &vpd_data[i]);
+out:
+	kfree(vpd_data);
 }
 
 
-- 
2.32.0

[PATCH 5/8] sfc: Use new VPD API function pci_vpd_find_ro_info_keyword

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:36

Use new VPD API function pci_vpd_find_ro_info_keyword() to simplify
the code. In addition replace netif_err() with pci_err() because the
netdevice isn't registered yet what results in very ugly messages.
Use kmemdup_nul() instead of open-coding it.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/net/ethernet/sfc/efx.c | 65 ++++++++--------------------------
 1 file changed, 14 insertions(+), 51 deletions(-)
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index e04a63109..43ef4f529 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -905,9 +905,9 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
 static void efx_probe_vpd_strings(struct efx_nic *efx)
 {
 	struct pci_dev *dev = efx->pci_dev;
-	int ro_start, ro_size, i, j;
-	unsigned int vpd_size;
+	unsigned int vpd_size, kw_len;
 	u8 *vpd_data;
+	int start;
 
 	vpd_data = pci_vpd_alloc(dev, &vpd_size);
 	if (IS_ERR(vpd_data)) {
@@ -915,57 +915,20 @@ static void efx_probe_vpd_strings(struct efx_nic *efx)
 		return;
 	}
 
-	/* Get the Read only section */
-	ro_start = pci_vpd_find_tag(vpd_data, vpd_size, PCI_VPD_LRDT_RO_DATA);
-	if (ro_start < 0) {
-		netif_err(efx, drv, efx->net_dev, "VPD Read-only not found\n");
-		goto out;
-	}
-
-	ro_size = pci_vpd_lrdt_size(&vpd_data[ro_start]);
-	j = ro_size;
-	i = ro_start + PCI_VPD_LRDT_TAG_SIZE;
-	if (i + j > vpd_size)
-		j = vpd_size - i;
-
-	/* Get the Part number */
-	i = pci_vpd_find_info_keyword(vpd_data, i, j, "PN");
-	if (i < 0) {
-		netif_err(efx, drv, efx->net_dev, "Part number not found\n");
-		goto out;
-	}
-
-	j = pci_vpd_info_field_size(&vpd_data[i]);
-	i += PCI_VPD_INFO_FLD_HDR_SIZE;
-	if (i + j > vpd_size) {
-		netif_err(efx, drv, efx->net_dev, "Incomplete part number\n");
-		goto out;
-	}
-
-	netif_info(efx, drv, efx->net_dev,
-		   "Part Number : %.*s\n", j, &vpd_data[i]);
-
-	i = ro_start + PCI_VPD_LRDT_TAG_SIZE;
-	j = ro_size;
-	i = pci_vpd_find_info_keyword(vpd_data, i, j, "SN");
-	if (i < 0) {
-		netif_err(efx, drv, efx->net_dev, "Serial number not found\n");
-		goto out;
-	}
-
-	j = pci_vpd_info_field_size(&vpd_data[i]);
-	i += PCI_VPD_INFO_FLD_HDR_SIZE;
-	if (i + j > vpd_size) {
-		netif_err(efx, drv, efx->net_dev, "Incomplete serial number\n");
-		goto out;
-	}
+	start = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
+					     PCI_VPD_RO_KEYWORD_PARTNO, &kw_len);
+	if (start < 0)
+		pci_err(dev, "Part number not found or incomplete\n");
+	else
+		pci_info(dev, "Part Number : %.*s\n", kw_len, vpd_data + start);
 
-	efx->vpd_sn = kmalloc(j + 1, GFP_KERNEL);
-	if (!efx->vpd_sn)
-		goto out;
+	start = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
+					     PCI_VPD_RO_KEYWORD_SERIALNO, &kw_len);
+	if (start < 0)
+		pci_err(dev, "Serial number not found or incomplete\n");
+	else
+		efx->vpd_sn = kmemdup_nul(vpd_data + start, kw_len, GFP_KERNEL);
 
-	snprintf(efx->vpd_sn, j + 1, "%s", &vpd_data[i]);
-out:
 	kfree(vpd_data);
 }
 
-- 
2.32.0

[PATCH 6/8] tg3: Use new function pci_vpd_alloc

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:39

Use new VPD API function pci_vpd_alloc() for dynamically allocating
a properly sized buffer and reading the full VPD data into it.
This allows to simplify the code, and we don't have to make
assumptions on VPD size any longer.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/net/ethernet/broadcom/tg3.c | 27 ++++++++++-----------------
 drivers/net/ethernet/broadcom/tg3.h |  1 -
 2 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 6f82eeaa4..fd4522c81 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12791,7 +12791,7 @@ static void tg3_get_ethtool_stats(struct net_device *dev,
 		memset(tmp_stats, 0, sizeof(struct tg3_ethtool_stats));
 }
 
-static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
+static __be32 *tg3_vpd_readblock(struct tg3 *tp, unsigned int *vpdlen)
 {
 	int i;
 	__be32 *buf;
@@ -12825,15 +12825,11 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
 			offset = TG3_NVM_VPD_OFF;
 			len = TG3_NVM_VPD_LEN;
 		}
-	} else {
-		len = TG3_NVM_PCI_VPD_MAX_LEN;
-	}
 
-	buf = kmalloc(len, GFP_KERNEL);
-	if (buf == NULL)
-		return NULL;
+		buf = kmalloc(len, GFP_KERNEL);
+		if (!buf)
+			return NULL;
 
-	if (magic == TG3_EEPROM_MAGIC) {
 		for (i = 0; i < len; i += 4) {
 			/* The data is in little-endian format in NVRAM.
 			 * Use the big-endian read routines to preserve
@@ -12844,12 +12840,9 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
 		}
 		*vpdlen = len;
 	} else {
-		ssize_t cnt;
-
-		cnt = pci_read_vpd(tp->pdev, 0, len, (u8 *)buf);
-		if (cnt < 0)
-			goto error;
-		*vpdlen = cnt;
+		buf = pci_vpd_alloc(tp->pdev, vpdlen);
+		if (IS_ERR(buf))
+			return NULL;
 	}
 
 	return buf;
@@ -12871,9 +12864,10 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
 
 static int tg3_test_nvram(struct tg3 *tp)
 {
-	u32 csum, magic, len;
+	u32 csum, magic;
 	__be32 *buf;
 	int i, j, k, err = 0, size;
+	unsigned int len;
 
 	if (tg3_flag(tp, NO_NVRAM))
 		return 0;
@@ -15621,8 +15615,7 @@ static int tg3_phy_probe(struct tg3 *tp)
 static void tg3_read_vpd(struct tg3 *tp)
 {
 	u8 *vpd_data;
-	unsigned int block_end, rosize, len;
-	u32 vpdlen;
+	unsigned int block_end, rosize, len, vpdlen;
 	int j, i = 0;
 
 	vpd_data = (u8 *)tg3_vpd_readblock(tp, &vpdlen);
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 46ec4fdfd..1000c8940 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -2101,7 +2101,6 @@
 /* Hardware Legacy NVRAM layout */
 #define TG3_NVM_VPD_OFF			0x100
 #define TG3_NVM_VPD_LEN			256
-#define TG3_NVM_PCI_VPD_MAX_LEN		512
 
 /* Hardware Selfboot NVRAM layout */
 #define TG3_NVM_HWSB_CFG1		0x00000004
-- 
2.32.0

[PATCH 7/8] tg3: Use new function pci_vpd_check_csum

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:07:49

Use new VPD API function pci_vpd_check_csum() to simplify the code.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/net/ethernet/broadcom/tg3.c | 31 ++++-------------------------
 1 file changed, 4 insertions(+), 27 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index fd4522c81..309aec742 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -13010,33 +13010,10 @@ static int tg3_test_nvram(struct tg3 *tp)
 	if (!buf)
 		return -ENOMEM;
 
-	i = pci_vpd_find_tag((u8 *)buf, len, PCI_VPD_LRDT_RO_DATA);
-	if (i > 0) {
-		j = pci_vpd_lrdt_size(&((u8 *)buf)[i]);
-		if (j < 0)
-			goto out;
-
-		if (i + PCI_VPD_LRDT_TAG_SIZE + j > len)
-			goto out;
-
-		i += PCI_VPD_LRDT_TAG_SIZE;
-		j = pci_vpd_find_info_keyword((u8 *)buf, i, j,
-					      PCI_VPD_RO_KEYWORD_CHKSUM);
-		if (j > 0) {
-			u8 csum8 = 0;
-
-			j += PCI_VPD_INFO_FLD_HDR_SIZE;
-
-			for (i = 0; i <= j; i++)
-				csum8 += ((u8 *)buf)[i];
-
-			if (csum8)
-				goto out;
-		}
-	}
-
-	err = 0;
-
+	err = pci_vpd_check_csum(buf, len);
+	/* go on if no checksum found */
+	if (err == 1)
+		err = 0;
 out:
 	kfree(buf);
 	return err;
-- 
2.32.0

[PATCH 8/8] tg3: Use new function pci_vpd_find_ro_info_keyword

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-18 19:08:01

Use new VPD API function pci_vpd_find_ro_info_keyword() to simplify
the code.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/net/ethernet/broadcom/tg3.c | 59 ++++++++---------------------
 1 file changed, 16 insertions(+), 43 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 309aec742..6637a97d7 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -15592,63 +15592,36 @@ static int tg3_phy_probe(struct tg3 *tp)
 static void tg3_read_vpd(struct tg3 *tp)
 {
 	u8 *vpd_data;
-	unsigned int block_end, rosize, len, vpdlen;
-	int j, i = 0;
+	unsigned int len, vpdlen;
+	int i;
 
 	vpd_data = (u8 *)tg3_vpd_readblock(tp, &vpdlen);
 	if (!vpd_data)
 		goto out_no_vpd;
 
-	i = pci_vpd_find_tag(vpd_data, vpdlen, PCI_VPD_LRDT_RO_DATA);
+	i = pci_vpd_find_ro_info_keyword(vpd_data, vpdlen,
+					 PCI_VPD_RO_KEYWORD_MFR_ID, &len);
 	if (i < 0)
-		goto out_not_found;
-
-	rosize = pci_vpd_lrdt_size(&vpd_data[i]);
-	block_end = i + PCI_VPD_LRDT_TAG_SIZE + rosize;
-	i += PCI_VPD_LRDT_TAG_SIZE;
-
-	if (block_end > vpdlen)
-		goto out_not_found;
-
-	j = pci_vpd_find_info_keyword(vpd_data, i, rosize,
-				      PCI_VPD_RO_KEYWORD_MFR_ID);
-	if (j > 0) {
-		len = pci_vpd_info_field_size(&vpd_data[j]);
+		goto partno;
 
-		j += PCI_VPD_INFO_FLD_HDR_SIZE;
-		if (j + len > block_end || len != 4 ||
-		    memcmp(&vpd_data[j], "1028", 4))
-			goto partno;
+	if (len != 4 || memcmp(vpd_data + i, "1028", 4))
+		goto partno;
 
-		j = pci_vpd_find_info_keyword(vpd_data, i, rosize,
-					      PCI_VPD_RO_KEYWORD_VENDOR0);
-		if (j < 0)
-			goto partno;
-
-		len = pci_vpd_info_field_size(&vpd_data[j]);
+	i = pci_vpd_find_ro_info_keyword(vpd_data, vpdlen,
+					 PCI_VPD_RO_KEYWORD_VENDOR0, &len);
+	if (i < 0)
+		goto partno;
 
-		j += PCI_VPD_INFO_FLD_HDR_SIZE;
-		if (j + len > block_end)
-			goto partno;
-
-		if (len >= sizeof(tp->fw_ver))
-			len = sizeof(tp->fw_ver) - 1;
-		memset(tp->fw_ver, 0, sizeof(tp->fw_ver));
-		snprintf(tp->fw_ver, sizeof(tp->fw_ver), "%.*s bc ", len,
-			 &vpd_data[j]);
-	}
+	memset(tp->fw_ver, 0, sizeof(tp->fw_ver));
+	snprintf(tp->fw_ver, sizeof(tp->fw_ver), "%.*s bc ", len, vpd_data + i);
 
 partno:
-	i = pci_vpd_find_info_keyword(vpd_data, i, rosize,
-				      PCI_VPD_RO_KEYWORD_PARTNO);
+	i = pci_vpd_find_ro_info_keyword(vpd_data, vpdlen,
+					 PCI_VPD_RO_KEYWORD_PARTNO, &len);
 	if (i < 0)
 		goto out_not_found;
 
-	len = pci_vpd_info_field_size(&vpd_data[i]);
-
-	i += PCI_VPD_INFO_FLD_HDR_SIZE;
-	if (len > TG3_BPN_SIZE ||
-	    (len + i) > vpdlen)
+	if (len > TG3_BPN_SIZE)
 		goto out_not_found;
 
 	memcpy(tp->board_part_number, &vpd_data[i], len);
-- 
2.32.0

Re: [PATCH 0/8] PCI/VPD: Extend PCI VPD API

From: Bjorn Helgaas <helgaas@kernel.org>
Date: 2021-08-20 20:50:46

On Wed, Aug 18, 2021 at 08:58:18PM +0200, Heiner Kallweit wrote:
This series adds three functions to the PCI VPD API that help to
simplify driver code. First users are sfc and tg3 drivers because
I have test hw. The other users of the VPD API will benefit from a
migration as well.
I'd propose to apply this series via the PCI tree.

Added API calls:

pci_vpd_alloc()
Dynamically allocates a properly sized buffer and reads the VPD into it.

pci_vpd_find_ro_info_keyword()
Locates an info field keyword in the VPD RO section.
pci_vpd_find_info_keyword() can be removed once all
users have been migrated.

pci_vpd_check_csum()
Check VPD checksum based on algorithm defined in the PCI specification.

Tested on a SFN6122F and a BCM95719 card.

Heiner Kallweit (8):
  PCI/VPD: Add pci_vpd_alloc
  PCI/VPD: Add pci_vpd_find_ro_info_keyword and pci_vpd_check_csum
  PCI/VPD: Add missing VPD RO field keywords
  sfc: Use new function pci_vpd_alloc
  sfc: Use new VPD API function pci_vpd_find_ro_info_keyword
  tg3: Use new function pci_vpd_alloc
  tg3: Use new function pci_vpd_check_csum
  tg3: Use new function pci_vpd_find_ro_info_keyword

 drivers/net/ethernet/broadcom/tg3.c | 115 +++++++---------------------
 drivers/net/ethernet/broadcom/tg3.h |   1 -
 drivers/net/ethernet/sfc/efx.c      |  78 +++++--------------
 drivers/pci/vpd.c                   |  82 ++++++++++++++++++++
 include/linux/pci.h                 |  32 ++++++++
 5 files changed, 163 insertions(+), 145 deletions(-)
Beautiful!  I applied this with minor tweaks to pci/vpd for v5.15.

I dropped the "add missing keywords" patch because there are no users
of the missing keywords yet.

I would have removed pci_vpd_find_info_keyword() as well, but it looks
like there are stilla few users of it.

Re: [PATCH 0/8] PCI/VPD: Extend PCI VPD API

From: Heiner Kallweit <hkallweit1@gmail.com>
Date: 2021-08-20 21:36:25

On 20.08.2021 22:50, Bjorn Helgaas wrote:
On Wed, Aug 18, 2021 at 08:58:18PM +0200, Heiner Kallweit wrote:
quoted
This series adds three functions to the PCI VPD API that help to
simplify driver code. First users are sfc and tg3 drivers because
I have test hw. The other users of the VPD API will benefit from a
migration as well.
I'd propose to apply this series via the PCI tree.

Added API calls:

pci_vpd_alloc()
Dynamically allocates a properly sized buffer and reads the VPD into it.

pci_vpd_find_ro_info_keyword()
Locates an info field keyword in the VPD RO section.
pci_vpd_find_info_keyword() can be removed once all
users have been migrated.

pci_vpd_check_csum()
Check VPD checksum based on algorithm defined in the PCI specification.

Tested on a SFN6122F and a BCM95719 card.

Heiner Kallweit (8):
  PCI/VPD: Add pci_vpd_alloc
  PCI/VPD: Add pci_vpd_find_ro_info_keyword and pci_vpd_check_csum
  PCI/VPD: Add missing VPD RO field keywords
  sfc: Use new function pci_vpd_alloc
  sfc: Use new VPD API function pci_vpd_find_ro_info_keyword
  tg3: Use new function pci_vpd_alloc
  tg3: Use new function pci_vpd_check_csum
  tg3: Use new function pci_vpd_find_ro_info_keyword

 drivers/net/ethernet/broadcom/tg3.c | 115 +++++++---------------------
 drivers/net/ethernet/broadcom/tg3.h |   1 -
 drivers/net/ethernet/sfc/efx.c      |  78 +++++--------------
 drivers/pci/vpd.c                   |  82 ++++++++++++++++++++
 include/linux/pci.h                 |  32 ++++++++
 5 files changed, 163 insertions(+), 145 deletions(-)
Beautiful!  I applied this with minor tweaks to pci/vpd for v5.15.

I dropped the "add missing keywords" patch because there are no users
of the missing keywords yet.
Chelsio T4 driver is using this keyword. Then I'll add this keyword
when migrating the driver.
I would have removed pci_vpd_find_info_keyword() as well, but it looks
like there are stilla few users of it.
Right, there are few more users. In this initial series I only changed
users for which I have test hw.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help