Merge patch series "Fix {rx,tx}_errors CAN statistics"

Dario Binacchi <dario.binacchi@amarulasolutions.com> says:

This series extends the patch 4d6d265379 ("can: c_can: fix {rx,tx}_errors statistics"),
already merged into the mainline, to other CAN devices that similarly do
not correctly increment the error counters for reception/transmission.

Changes in v2:
- Fix patches 7 through 12 to ensure that statistics are updated even
  if the allocation of skb fails.
- Add five new patches (i. e. 1-5), created during the further analysis
  of the code while correcting patches from the v1 series (i. e. 7-12).

Link: https://patch.msgid.link/20241122221650.633981-1-dario.binacchi@amarulasolutions.com
[mkl: omitted patch 3]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde
2024-11-26 11:09:07 +01:00
8 changed files with 203 additions and 122 deletions
+17 -9
View File
@@ -1014,49 +1014,57 @@ static int c_can_handle_bus_err(struct net_device *dev,
/* propagate the error condition to the CAN stack */
skb = alloc_can_err_skb(dev, &cf);
if (unlikely(!skb))
return 0;
/* check for 'last error code' which tells us the
* type of the last error to occur on the CAN bus
*/
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
if (likely(skb))
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
switch (lec_type) {
case LEC_STUFF_ERROR:
netdev_dbg(dev, "stuff error\n");
cf->data[2] |= CAN_ERR_PROT_STUFF;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_STUFF;
stats->rx_errors++;
break;
case LEC_FORM_ERROR:
netdev_dbg(dev, "form error\n");
cf->data[2] |= CAN_ERR_PROT_FORM;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_FORM;
stats->rx_errors++;
break;
case LEC_ACK_ERROR:
netdev_dbg(dev, "ack error\n");
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
if (likely(skb))
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
stats->tx_errors++;
break;
case LEC_BIT1_ERROR:
netdev_dbg(dev, "bit1 error\n");
cf->data[2] |= CAN_ERR_PROT_BIT1;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_BIT1;
stats->tx_errors++;
break;
case LEC_BIT0_ERROR:
netdev_dbg(dev, "bit0 error\n");
cf->data[2] |= CAN_ERR_PROT_BIT0;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_BIT0;
stats->tx_errors++;
break;
case LEC_CRC_ERROR:
netdev_dbg(dev, "CRC error\n");
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
if (likely(skb))
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
stats->rx_errors++;
break;
default:
break;
}
if (unlikely(!skb))
return 0;
netif_receive_skb(skb);
return 1;
}
+40 -18
View File
@@ -390,36 +390,55 @@ static int ifi_canfd_handle_lec_err(struct net_device *ndev)
return 0;
priv->can.can_stats.bus_error++;
stats->rx_errors++;
/* Propagate the error condition to the CAN stack. */
skb = alloc_can_err_skb(ndev, &cf);
if (unlikely(!skb))
return 0;
/* Read the error counter register and check for new errors. */
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
if (likely(skb))
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
if (errctr & IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST)
cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
if (errctr & IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST) {
stats->rx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
}
if (errctr & IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST)
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
if (errctr & IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST) {
stats->tx_errors++;
if (likely(skb))
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
}
if (errctr & IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST)
cf->data[2] |= CAN_ERR_PROT_BIT0;
if (errctr & IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST) {
stats->tx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_BIT0;
}
if (errctr & IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST)
cf->data[2] |= CAN_ERR_PROT_BIT1;
if (errctr & IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST) {
stats->tx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_BIT1;
}
if (errctr & IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST)
cf->data[2] |= CAN_ERR_PROT_STUFF;
if (errctr & IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST) {
stats->rx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_STUFF;
}
if (errctr & IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST)
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
if (errctr & IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST) {
stats->rx_errors++;
if (likely(skb))
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
}
if (errctr & IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST)
cf->data[2] |= CAN_ERR_PROT_FORM;
if (errctr & IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST) {
stats->rx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_FORM;
}
/* Reset the error counter, ack the IRQ and re-enable the counter. */
writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR);
@@ -427,6 +446,9 @@ static int ifi_canfd_handle_lec_err(struct net_device *ndev)
priv->base + IFI_CANFD_INTERRUPT);
writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR);
if (unlikely(!skb))
return 0;
netif_receive_skb(skb);
return 1;
+23 -10
View File
@@ -695,47 +695,60 @@ static int m_can_handle_lec_err(struct net_device *dev,
u32 timestamp = 0;
cdev->can.can_stats.bus_error++;
stats->rx_errors++;
/* propagate the error condition to the CAN stack */
skb = alloc_can_err_skb(dev, &cf);
if (unlikely(!skb))
return 0;
/* check for 'last error code' which tells us the
* type of the last error to occur on the CAN bus
*/
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
if (likely(skb))
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
switch (lec_type) {
case LEC_STUFF_ERROR:
netdev_dbg(dev, "stuff error\n");
cf->data[2] |= CAN_ERR_PROT_STUFF;
stats->rx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
case LEC_FORM_ERROR:
netdev_dbg(dev, "form error\n");
cf->data[2] |= CAN_ERR_PROT_FORM;
stats->rx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_FORM;
break;
case LEC_ACK_ERROR:
netdev_dbg(dev, "ack error\n");
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
stats->tx_errors++;
if (likely(skb))
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
break;
case LEC_BIT1_ERROR:
netdev_dbg(dev, "bit1 error\n");
cf->data[2] |= CAN_ERR_PROT_BIT1;
stats->tx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_BIT1;
break;
case LEC_BIT0_ERROR:
netdev_dbg(dev, "bit0 error\n");
cf->data[2] |= CAN_ERR_PROT_BIT0;
stats->tx_errors++;
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_BIT0;
break;
case LEC_CRC_ERROR:
netdev_dbg(dev, "CRC error\n");
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
stats->rx_errors++;
if (likely(skb))
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
break;
default:
break;
}
if (unlikely(!skb))
return 0;
if (cdev->is_peripheral)
timestamp = m_can_get_timestamp(cdev);
+38 -27
View File
@@ -416,8 +416,6 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
int ret = 0;
skb = alloc_can_err_skb(dev, &cf);
if (skb == NULL)
return -ENOMEM;
txerr = priv->read_reg(priv, SJA1000_TXERR);
rxerr = priv->read_reg(priv, SJA1000_RXERR);
@@ -425,8 +423,11 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
if (isrc & IRQ_DOI) {
/* data overrun interrupt */
netdev_dbg(dev, "data overrun interrupt\n");
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
if (skb) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
}
stats->rx_over_errors++;
stats->rx_errors++;
sja1000_write_cmdreg(priv, CMD_CDO); /* clear bit */
@@ -452,7 +453,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
else
state = CAN_STATE_ERROR_ACTIVE;
}
if (state != CAN_STATE_BUS_OFF) {
if (state != CAN_STATE_BUS_OFF && skb) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = txerr;
cf->data[7] = rxerr;
@@ -460,33 +461,38 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
if (isrc & IRQ_BEI) {
/* bus error interrupt */
priv->can.can_stats.bus_error++;
stats->rx_errors++;
ecc = priv->read_reg(priv, SJA1000_ECC);
if (skb) {
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
/* set error type */
switch (ecc & ECC_MASK) {
case ECC_BIT:
cf->data[2] |= CAN_ERR_PROT_BIT;
break;
case ECC_FORM:
cf->data[2] |= CAN_ERR_PROT_FORM;
break;
case ECC_STUFF:
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
break;
}
/* set error type */
switch (ecc & ECC_MASK) {
case ECC_BIT:
cf->data[2] |= CAN_ERR_PROT_BIT;
break;
case ECC_FORM:
cf->data[2] |= CAN_ERR_PROT_FORM;
break;
case ECC_STUFF:
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
break;
/* set error location */
cf->data[3] = ecc & ECC_SEG;
}
/* set error location */
cf->data[3] = ecc & ECC_SEG;
/* Error occurred during transmission? */
if ((ecc & ECC_DIR) == 0)
cf->data[2] |= CAN_ERR_PROT_TX;
if ((ecc & ECC_DIR) == 0) {
stats->tx_errors++;
if (skb)
cf->data[2] |= CAN_ERR_PROT_TX;
} else {
stats->rx_errors++;
}
}
if (isrc & IRQ_EPI) {
/* error passive interrupt */
@@ -502,8 +508,10 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
netdev_dbg(dev, "arbitration lost interrupt\n");
alc = priv->read_reg(priv, SJA1000_ALC);
priv->can.can_stats.arbitration_lost++;
cf->can_id |= CAN_ERR_LOSTARB;
cf->data[0] = alc & 0x1f;
if (skb) {
cf->can_id |= CAN_ERR_LOSTARB;
cf->data[0] = alc & 0x1f;
}
}
if (state != priv->can.state) {
@@ -516,6 +524,9 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
can_bus_off(dev);
}
if (!skb)
return -ENOMEM;
netif_rx(skb);
return ret;
+32 -21
View File
@@ -663,27 +663,27 @@ static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
u8 rxerr, txerr;
skb = alloc_can_err_skb(net, &cf);
if (!skb)
break;
txerr = hi3110_read(spi, HI3110_READ_TEC);
rxerr = hi3110_read(spi, HI3110_READ_REC);
tx_state = txerr >= rxerr ? new_state : 0;
rx_state = txerr <= rxerr ? new_state : 0;
can_change_state(net, cf, tx_state, rx_state);
netif_rx(skb);
if (new_state == CAN_STATE_BUS_OFF) {
if (skb)
netif_rx(skb);
can_bus_off(net);
if (priv->can.restart_ms == 0) {
priv->force_quit = 1;
hi3110_hw_sleep(spi);
break;
}
} else {
} else if (skb) {
cf->can_id |= CAN_ERR_CNT;
cf->data[6] = txerr;
cf->data[7] = rxerr;
netif_rx(skb);
}
}
@@ -696,27 +696,38 @@ static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
/* Check for protocol errors */
if (eflag & HI3110_ERR_PROTOCOL_MASK) {
skb = alloc_can_err_skb(net, &cf);
if (!skb)
break;
if (skb)
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
priv->can.can_stats.bus_error++;
priv->net->stats.rx_errors++;
if (eflag & HI3110_ERR_BITERR)
cf->data[2] |= CAN_ERR_PROT_BIT;
else if (eflag & HI3110_ERR_FRMERR)
cf->data[2] |= CAN_ERR_PROT_FORM;
else if (eflag & HI3110_ERR_STUFERR)
cf->data[2] |= CAN_ERR_PROT_STUFF;
else if (eflag & HI3110_ERR_CRCERR)
cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
else if (eflag & HI3110_ERR_ACKERR)
cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
if (eflag & HI3110_ERR_BITERR) {
priv->net->stats.tx_errors++;
if (skb)
cf->data[2] |= CAN_ERR_PROT_BIT;
} else if (eflag & HI3110_ERR_FRMERR) {
priv->net->stats.rx_errors++;
if (skb)
cf->data[2] |= CAN_ERR_PROT_FORM;
} else if (eflag & HI3110_ERR_STUFERR) {
priv->net->stats.rx_errors++;
if (skb)
cf->data[2] |= CAN_ERR_PROT_STUFF;
} else if (eflag & HI3110_ERR_CRCERR) {
priv->net->stats.rx_errors++;
if (skb)
cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
} else if (eflag & HI3110_ERR_ACKERR) {
priv->net->stats.tx_errors++;
if (skb)
cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
}
cf->data[6] = hi3110_read(spi, HI3110_READ_TEC);
cf->data[7] = hi3110_read(spi, HI3110_READ_REC);
netdev_dbg(priv->net, "Bus Error\n");
netif_rx(skb);
if (skb) {
cf->data[6] = hi3110_read(spi, HI3110_READ_TEC);
cf->data[7] = hi3110_read(spi, HI3110_READ_REC);
netif_rx(skb);
}
}
}
+13 -9
View File
@@ -579,11 +579,9 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
/* bus error interrupt */
netdev_dbg(dev, "bus error interrupt\n");
priv->can.can_stats.bus_error++;
stats->rx_errors++;
ecc = readl(priv->base + SUN4I_REG_STA_ADDR);
if (likely(skb)) {
ecc = readl(priv->base + SUN4I_REG_STA_ADDR);
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
switch (ecc & SUN4I_STA_MASK_ERR) {
@@ -601,9 +599,15 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
>> 16;
break;
}
/* error occurred during transmission? */
if ((ecc & SUN4I_STA_ERR_DIR) == 0)
}
/* error occurred during transmission? */
if ((ecc & SUN4I_STA_ERR_DIR) == 0) {
if (likely(skb))
cf->data[2] |= CAN_ERR_PROT_TX;
stats->tx_errors++;
} else {
stats->rx_errors++;
}
}
if (isrc & SUN4I_INT_ERR_PASSIVE) {
@@ -629,10 +633,10 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
tx_state = txerr >= rxerr ? state : 0;
rx_state = txerr <= rxerr ? state : 0;
if (likely(skb))
can_change_state(dev, cf, tx_state, rx_state);
else
priv->can.state = state;
/* The skb allocation might fail, but can_change_state()
* handles cf == NULL.
*/
can_change_state(dev, cf, tx_state, rx_state);
if (state == CAN_STATE_BUS_OFF)
can_bus_off(dev);
}
+33 -25
View File
@@ -335,15 +335,14 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg)
struct net_device_stats *stats = &dev->netdev->stats;
skb = alloc_can_err_skb(dev->netdev, &cf);
if (skb == NULL)
return;
if (msg->type == CPC_MSG_TYPE_CAN_STATE) {
u8 state = msg->msg.can_state;
if (state & SJA1000_SR_BS) {
dev->can.state = CAN_STATE_BUS_OFF;
cf->can_id |= CAN_ERR_BUSOFF;
if (skb)
cf->can_id |= CAN_ERR_BUSOFF;
dev->can.can_stats.bus_off++;
can_bus_off(dev->netdev);
@@ -361,44 +360,53 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg)
/* bus error interrupt */
dev->can.can_stats.bus_error++;
stats->rx_errors++;
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
if (skb) {
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
switch (ecc & SJA1000_ECC_MASK) {
case SJA1000_ECC_BIT:
cf->data[2] |= CAN_ERR_PROT_BIT;
break;
case SJA1000_ECC_FORM:
cf->data[2] |= CAN_ERR_PROT_FORM;
break;
case SJA1000_ECC_STUFF:
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
cf->data[3] = ecc & SJA1000_ECC_SEG;
break;
switch (ecc & SJA1000_ECC_MASK) {
case SJA1000_ECC_BIT:
cf->data[2] |= CAN_ERR_PROT_BIT;
break;
case SJA1000_ECC_FORM:
cf->data[2] |= CAN_ERR_PROT_FORM;
break;
case SJA1000_ECC_STUFF:
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
cf->data[3] = ecc & SJA1000_ECC_SEG;
break;
}
}
/* Error occurred during transmission? */
if ((ecc & SJA1000_ECC_DIR) == 0)
cf->data[2] |= CAN_ERR_PROT_TX;
if ((ecc & SJA1000_ECC_DIR) == 0) {
stats->tx_errors++;
if (skb)
cf->data[2] |= CAN_ERR_PROT_TX;
} else {
stats->rx_errors++;
}
if (dev->can.state == CAN_STATE_ERROR_WARNING ||
dev->can.state == CAN_STATE_ERROR_PASSIVE) {
if (skb && (dev->can.state == CAN_STATE_ERROR_WARNING ||
dev->can.state == CAN_STATE_ERROR_PASSIVE)) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (txerr > rxerr) ?
CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE;
}
} else if (msg->type == CPC_MSG_TYPE_OVERRUN) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
if (skb) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
}
stats->rx_over_errors++;
stats->rx_errors++;
}
netif_rx(skb);
if (skb)
netif_rx(skb);
}
/*
+7 -3
View File
@@ -526,7 +526,6 @@ static void f81604_handle_can_bus_errors(struct f81604_port_priv *priv,
netdev_dbg(netdev, "bus error interrupt\n");
priv->can.can_stats.bus_error++;
stats->rx_errors++;
if (skb) {
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
@@ -548,10 +547,15 @@ static void f81604_handle_can_bus_errors(struct f81604_port_priv *priv,
/* set error location */
cf->data[3] = data->ecc & F81604_SJA1000_ECC_SEG;
}
/* Error occurred during transmission? */
if ((data->ecc & F81604_SJA1000_ECC_DIR) == 0)
/* Error occurred during transmission? */
if ((data->ecc & F81604_SJA1000_ECC_DIR) == 0) {
stats->tx_errors++;
if (skb)
cf->data[2] |= CAN_ERR_PROT_TX;
} else {
stats->rx_errors++;
}
set_bit(F81604_CLEAR_ECC, &priv->clear_flags);