blob: 281cefc274b76edacb45e8b313d24aad8bf917f7 [file] [log] [blame]
From 9e2ff138c053f809c1d994d4d013ef8c35bddc89 Mon Sep 17 00:00:00 2001
From: Jinliang Wang <jinliangw@google.com>
Date: Mon, 22 Apr 2024 15:50:24 -0700
Subject: [PATCH] mctp-i2c: add MCTP retry mechanism
In include/uapi/linux/i2c.h, define two new flags for i2c_msg.flags:
I2C_M_IS_MCTP and I2C_M_LAST_RETRY.
In mctp-i2c.c, add mctp_i2c_transfer_with_retry function
which will perform TX mctp packet retry logic, approximately
20 ms x 100 times = 2 seconds.
Patch Tracking Bug: b/335839424
Upstream info / review: N/A
Upstream-Status: Pending
Justification: Need to solve the MCTP retry and recover issues
Google-Bug-Id: 286397121
Signed-off-by: Jinliang Wang <jinliangw@google.com>
---
drivers/net/mctp/mctp-i2c.c | 40 +++++++++++++++++++++++++++++++++++--
include/uapi/linux/i2c.h | 2 ++
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/drivers/net/mctp/mctp-i2c.c b/drivers/net/mctp/mctp-i2c.c
index baf7afac7857..820e6f61b7f9 100644
--- a/drivers/net/mctp/mctp-i2c.c
+++ b/drivers/net/mctp/mctp-i2c.c
@@ -435,6 +435,42 @@ static void mctp_i2c_unlock_reset(struct mctp_i2c_dev *midev)
i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
}
+#define RETRY_TIME_MS 2000
+#define RETRY_INVERVAL_MS 20
+#define RETRY_CNT (RETRY_TIME_MS / RETRY_INVERVAL_MS)
+#define ENXIO_RETRY_LIMIT 5
+
+static int mctp_i2c_transfer_with_retry(struct i2c_adapter *adap,
+ struct i2c_msg *msg)
+{
+ int rc = 0;
+ int retry = 0; /* General retry count */
+ int enxio_retry = 0; /* Special retry count for ENXIO error */
+ msg->flags = I2C_M_IS_MCTP;
+ while (retry < RETRY_CNT) {
+ retry++;
+ rc = __i2c_transfer(adap, msg, 1);
+ if (rc == -ENXIO) {
+ enxio_retry++;
+ if (enxio_retry >= ENXIO_RETRY_LIMIT) {
+ /* Jump out if we accumulate enough ENXIO error */
+ break;
+ }
+ } else if (rc == -EBUSY || rc == -EAGAIN || rc == -EPROTO) {
+ if (retry >= (RETRY_CNT - 1)) {
+ /* Set last retry flag so i2c driver layer may perform necessary recovery */
+ msg->flags |= I2C_M_LAST_RETRY;
+ }
+ } else {
+ break;
+ }
+ msleep_interruptible(RETRY_INVERVAL_MS);
+ /* Continue to retry */
+ }
+
+ return rc;
+}
+
static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
{
struct net_device_stats *stats = &midev->ndev->stats;
@@ -478,7 +514,7 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
/* no flow: full lock & unlock */
mctp_i2c_lock_nest(midev);
mctp_i2c_device_select(midev->client, midev);
- rc = __i2c_transfer(midev->adapter, &msg, 1);
+ rc = mctp_i2c_transfer_with_retry(midev->adapter, &msg);
mctp_i2c_unlock_nest(midev);
break;
@@ -492,7 +528,7 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
case MCTP_I2C_TX_FLOW_EXISTING:
/* existing flow: we already have the lock; just tx */
- rc = __i2c_transfer(midev->adapter, &msg, 1);
+ rc = mctp_i2c_transfer_with_retry(midev->adapter, &msg);
break;
case MCTP_I2C_TX_FLOW_INVALID:
diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h
index 92326ebde350..c77b74501b05 100644
--- a/include/uapi/linux/i2c.h
+++ b/include/uapi/linux/i2c.h
@@ -75,6 +75,8 @@ struct i2c_msg {
__u16 flags;
#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* use only if I2C_FUNC_10BIT_ADDR */
+#define I2C_M_IS_MCTP 0x0020 /* use only if msg is MCTP over i2c binding */
+#define I2C_M_LAST_RETRY 0x0040 /* use only if msg is the last retry of a message */
#define I2C_M_DMA_SAFE 0x0200 /* use only in kernel space */
#define I2C_M_RECV_LEN 0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */
#define I2C_M_NO_RD_ACK 0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
--
2.44.0.769.g3c40516874-goog