[PATCH net-next v5 4/7] ptp: ocp: Track EEPROM fields independently

Ahmad Byagowi <[email protected]>
Newsgroups org.kernel.vger.linux-i2c,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-hardening,org.kernel.vger.linux-kernel,org.kernel.vger.linux-leds,org.kernel.vger.netdev
Message-ID <3186cc1f1c51b548439fda558e9e4052a481b787.1786747084.git.ahmadexp@gmail.com>
The board ID and serial number can reside in separate EEPROM devices.
Requiring both reads to succeed before publishing either field makes a
missing serial EEPROM block board profile selection and hides an otherwise
valid board ID.

Serialize EEPROM discovery, read each field into temporary storage, and
publish its readiness only after a successful read. Let devlink report
whichever fields are available and make the serial-number sysfs attribute
propagate read failures.

Signed-off-by: Ahmad Byagowi <[email protected]>
---
 drivers/ptp/ptp_ocp.c | 129 ++++++++++++++++++++++++++++++------------
 1 file changed, 93 insertions(+), 36 deletions(-)

diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 3d26ec1f7b9e..31a4c9dfe391 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -21,6 +21,7 @@
 #include <net/devlink.h>
 #include <linux/i2c.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mutex.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
 #include <linux/dpll.h>
@@ -343,6 +344,10 @@ struct ptp_ocp_serial_port {
 
 #define OCP_BOARD_ID_LEN		13
 #define OCP_SERIAL_LEN			6
+#define OCP_EEPROM_BOARD_ID		BIT(0)
+#define OCP_EEPROM_SERIAL		BIT(1)
+#define OCP_EEPROM_ALL			(OCP_EEPROM_BOARD_ID | \
+					 OCP_EEPROM_SERIAL)
 #define OCP_SMA_NUM			4
 #define OCP_SIGNAL_NUM			4
 #define OCP_FREQ_NUM			4
@@ -403,7 +408,10 @@ struct ptp_ocp {
 	u16			fw_version;
 	u8			board_id[OCP_BOARD_ID_LEN];
 	u8			serial[OCP_SERIAL_LEN];
-	bool			has_eeprom_data;
+	struct mutex		eeprom_lock; /* Serializes EEPROM discovery. */
+	bool			has_board_id;
+	bool			has_serial;
+	bool			eeprom_error_reported;
 	u32			pps_req_map;
 	int			flash_start;
 	u32			utc_tai_offset;
@@ -1969,21 +1977,50 @@ ptp_ocp_nvmem_device_put(struct nvmem_device **nvmemp)
 	*nvmemp = NULL;
 }
 
-static void
-ptp_ocp_read_eeprom(struct ptp_ocp *bp)
+static int
+ptp_ocp_read_eeprom(struct ptp_ocp *bp, unsigned int fields)
 {
 	const struct ptp_ocp_eeprom_map *map;
 	struct nvmem_device *nvmem;
+	u8 data[OCP_BOARD_ID_LEN];
+	const char *field_name = "data";
 	const void *tag;
-	int ret;
-
-	if (!bp->i2c_ctrl)
-		return;
+	unsigned int field;
+	int ret = 0;
 
 	tag = NULL;
 	nvmem = NULL;
+	mutex_lock(&bp->eeprom_lock);
+	fields &= OCP_EEPROM_ALL;
+	if (bp->has_board_id)
+		fields &= ~OCP_EEPROM_BOARD_ID;
+	if (bp->has_serial)
+		fields &= ~OCP_EEPROM_SERIAL;
+	if (!fields)
+		goto out;
+	if (!bp->i2c_ctrl || !bp->eeprom_map) {
+		ret = -ENODEV;
+		goto out;
+	}
 
 	for (map = bp->eeprom_map; map->len; map++) {
+		if (map->bp_offset == offsetof(struct ptp_ocp, board_id)) {
+			field = OCP_EEPROM_BOARD_ID;
+			field_name = "board ID";
+			if (bp->has_board_id)
+				continue;
+		} else if (map->bp_offset == offsetof(struct ptp_ocp, serial)) {
+			field = OCP_EEPROM_SERIAL;
+			field_name = "serial number";
+			if (bp->has_serial)
+				continue;
+		} else {
+			continue;
+		}
+
+		if (!(fields & field))
+			continue;
+
 		if (map->tag != tag) {
 			tag = map->tag;
 			ptp_ocp_nvmem_device_put(&nvmem);
@@ -1992,24 +2029,34 @@ ptp_ocp_read_eeprom(struct ptp_ocp *bp)
 			nvmem = ptp_ocp_nvmem_device_get(bp, tag);
 			if (IS_ERR(nvmem)) {
 				ret = PTR_ERR(nvmem);
-				goto fail;
+				goto out;
 			}
 		}
-		ret = nvmem_device_read(nvmem, map->off, map->len,
-					BP_MAP_ENTRY_ADDR(bp, map));
-		if (ret != map->len)
-			goto fail;
+		ret = nvmem_device_read(nvmem, map->off, map->len, data);
+		if (ret < 0)
+			goto out;
+
+		memcpy(BP_MAP_ENTRY_ADDR(bp, map), data, map->len);
+		if (field == OCP_EEPROM_BOARD_ID) {
+			/* Publish the field before marking it ready. */
+			smp_store_release(&bp->has_board_id, true);
+		} else {
+			/* Publish the field before marking it ready. */
+			smp_store_release(&bp->has_serial, true);
+		}
 	}
 
-	bp->has_eeprom_data = true;
+	ret = 0;
 
 out:
+	if (ret && ret != -EPROBE_DEFER && !bp->eeprom_error_reported) {
+		dev_err(&bp->pdev->dev, "failed to read EEPROM %s: %pe\n",
+			field_name, ERR_PTR(ret));
+		bp->eeprom_error_reported = true;
+	}
 	ptp_ocp_nvmem_device_put(&nvmem);
-	return;
-
-fail:
-	dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", ret);
-	goto out;
+	mutex_unlock(&bp->eeprom_lock);
+	return ret;
 }
 
 static struct device *
@@ -2156,6 +2203,7 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 			 struct netlink_ext_ack *extack)
 {
 	struct ptp_ocp *bp = devlink_priv(devlink);
+	const char *board_id_key = DEVLINK_INFO_VERSION_GENERIC_BOARD_ID;
 	const char *fw_image;
 	char buf[32];
 	int err;
@@ -2166,24 +2214,24 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 	if (err)
 		return err;
 
-	if (!bp->has_eeprom_data) {
-		ptp_ocp_read_eeprom(bp);
-		if (!bp->has_eeprom_data)
-			return 0;
-	}
+	ptp_ocp_read_eeprom(bp, OCP_EEPROM_ALL);
 
-	sprintf(buf, "%pM", bp->serial);
-	err = devlink_info_serial_number_put(req, buf);
-	if (err)
-		return err;
+	/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+	if (smp_load_acquire(&bp->has_serial)) {
+		sprintf(buf, "%pM", bp->serial);
+		err = devlink_info_serial_number_put(req, buf);
+		if (err)
+			return err;
+	}
 
-	snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN,
-		 (const char *)bp->board_id);
-	err = devlink_info_version_fixed_put(req,
-			DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
-			buf);
-	if (err)
-		return err;
+	/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+	if (smp_load_acquire(&bp->has_board_id)) {
+		snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN,
+			 (const char *)bp->board_id);
+		err = devlink_info_version_fixed_put(req, board_id_key, buf);
+		if (err)
+			return err;
+	}
 
 	return 0;
 }
@@ -3757,9 +3805,17 @@ static ssize_t
 serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct ptp_ocp *bp = dev_get_drvdata(dev);
+	int ret;
 
-	if (!bp->has_eeprom_data)
-		ptp_ocp_read_eeprom(bp);
+	/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+	if (!smp_load_acquire(&bp->has_serial)) {
+		ret = ptp_ocp_read_eeprom(bp, OCP_EEPROM_SERIAL);
+		if (ret)
+			return ret;
+		/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+		if (!smp_load_acquire(&bp->has_serial))
+			return -ENODATA;
+	}
 
 	return sysfs_emit(buf, "%pM\n", bp->serial);
 }
@@ -4729,6 +4785,7 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)
 
 	bp->ptp_info = ptp_ocp_clock_info;
 	spin_lock_init(&bp->lock);
+	mutex_init(&bp->eeprom_lock);
 
 	for (i = 0; i < __PORT_COUNT; i++)
 		bp->port[i].line = -1;
-- 
2.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.