| 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 |
| |