net: mctp-i2c: invalidate flows immediately on TX errors
authorJeremy Kerr <jk@codeconstruct.com.au>
Wed, 10 Jul 2024 02:17:22 +0000 (10:17 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Jul 2024 12:37:31 +0000 (13:37 +0100)
If we encounter an error on i2c packet transmit, we won't have a valid
flow anymore; since we didn't transmit a valid packet sequence, we'll
have to wait for the key to timeout instead of dropping it on the reply.

This causes the i2c lock to be held for longer than necessary.

Instead, invalidate the flow on TX error, and release the i2c lock
immediately.

Cc: Bonnie Lo <Bonnie_Lo@wiwynn.com>
Tested-by: Jerry C Chen <Jerry_C_Chen@wiwynn.com>
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/mctp/mctp-i2c.c

index f9afea25044f00195854865cc75ab804aaf9e800..4dc057c121f5d0fb9c9c48bf16b6933ae2f7b2ac 100644 (file)
@@ -442,6 +442,42 @@ static void mctp_i2c_unlock_reset(struct mctp_i2c_dev *midev)
                i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
 }
 
+static void mctp_i2c_invalidate_tx_flow(struct mctp_i2c_dev *midev,
+                                       struct sk_buff *skb)
+{
+       struct mctp_sk_key *key;
+       struct mctp_flow *flow;
+       unsigned long flags;
+       bool release;
+
+       flow = skb_ext_find(skb, SKB_EXT_MCTP);
+       if (!flow)
+               return;
+
+       key = flow->key;
+       if (!key)
+               return;
+
+       spin_lock_irqsave(&key->lock, flags);
+       if (key->manual_alloc) {
+               /* we don't have control over lifetimes for manually-allocated
+                * keys, so cannot assume we can invalidate all future flows
+                * that would use this key.
+                */
+               release = false;
+       } else {
+               release = key->dev_flow_state == MCTP_I2C_FLOW_STATE_ACTIVE;
+               key->dev_flow_state = MCTP_I2C_FLOW_STATE_INVALID;
+       }
+       spin_unlock_irqrestore(&key->lock, flags);
+
+       /* if we have changed state from active, the flow held a reference on
+        * the lock; release that now.
+        */
+       if (release)
+               mctp_i2c_unlock_nest(midev);
+}
+
 static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
 {
        struct net_device_stats *stats = &midev->ndev->stats;
@@ -500,6 +536,11 @@ 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);
+
+               /* on tx errors, the flow can no longer be considered valid */
+               if (rc)
+                       mctp_i2c_invalidate_tx_flow(midev, skb);
+
                break;
 
        case MCTP_I2C_TX_FLOW_INVALID: