Re: [PATCH] can: xilinx_can: avoid non-requested bus error frames

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 10/4/19 11:02 AM, Anssi Hannula wrote:
> Userspace can signal with CAN_CTRLMODE_BERR_REPORTING whether they need
> reporting of bus errors (CAN_ERR_BUSERROR) or not.
> 
> However, xilinx_can driver currently always sends CAN_ERR_BUSERROR
> frames to userspace on bus errors.
> 
> To improve performance on error conditions when bus error reporting is
> not needed, avoid sending CAN_ERR_BUSERROR frames unless requested via
> CAN_CTRLMODE_BERR_REPORTING.
> 
> The error interrupt is still kept enabled as there is no dedicated state
> transition interrupt, but just disabling error frame submission still
> yields a significant performance improvement. In a simple test with
> continuous bus errors and no userspace programs reading/writing CAN I
> saw system CPU load reduced by 1/3.
> 
> Tested on a ZynqMP board with CAN-FD v1.0.
> 
> Signed-off-by: Anssi Hannula <anssi.hannula@xxxxxxxxxx>
> ---
>  drivers/net/can/xilinx_can.c | 84 +++++++++++++++++++-----------------
>  1 file changed, 45 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
> index 911b34316c9d..9b9ec07f7e5b 100644
> --- a/drivers/net/can/xilinx_can.c
> +++ b/drivers/net/can/xilinx_can.c
> @@ -471,6 +471,10 @@ static int xcan_chip_start(struct net_device *ndev)
>  		return err;
>  
>  	/* Enable interrupts */
> +	/* We enable the ERROR interrupt even with CAN_CTRLMODE_BERR_REPORTING
> +	 * disabled as there is no dedicated interrupt for a state change to
> +	 * ERROR_WARNING/ERROR_PASSIVE.
> +	 */
>  	ier = XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |
>  		XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK |
>  		XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
> @@ -981,11 +985,10 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
>  {
>  	struct xcan_priv *priv = netdev_priv(ndev);
>  	struct net_device_stats *stats = &ndev->stats;
> -	struct can_frame *cf;
> -	struct sk_buff *skb;
> +	struct can_frame cf;
>  	u32 err_status;
>  
> -	skb = alloc_can_err_skb(ndev, &cf);
> +	memset(&cf, 0, sizeof(cf));

You can use C99 initializers instead of the memset.

>  
>  	err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
>  	priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
> @@ -996,32 +999,27 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
>  		/* Leave device in Config Mode in bus-off state */
>  		priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
>  		can_bus_off(ndev);
> -		if (skb)
> -			cf->can_id |= CAN_ERR_BUSOFF;
> +		cf.can_id |= CAN_ERR_BUSOFF;
>  	} else {
>  		enum can_state new_state = xcan_current_error_state(ndev);
>  
>  		if (new_state != priv->can.state)
> -			xcan_set_error_state(ndev, new_state, skb ? cf : NULL);
> +			xcan_set_error_state(ndev, new_state, &cf);
>  	}
>  
>  	/* Check for Arbitration lost interrupt */
>  	if (isr & XCAN_IXR_ARBLST_MASK) {
>  		priv->can.can_stats.arbitration_lost++;
> -		if (skb) {
> -			cf->can_id |= CAN_ERR_LOSTARB;
> -			cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
> -		}
> +		cf.can_id |= CAN_ERR_LOSTARB;
> +		cf.data[0] = CAN_ERR_LOSTARB_UNSPEC;
>  	}
>  
>  	/* Check for RX FIFO Overflow interrupt */
>  	if (isr & XCAN_IXR_RXOFLW_MASK) {
>  		stats->rx_over_errors++;
>  		stats->rx_errors++;
> -		if (skb) {
> -			cf->can_id |= CAN_ERR_CRTL;
> -			cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> -		}
> +		cf.can_id |= CAN_ERR_CRTL;
> +		cf.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
>  	}
>  
>  	/* Check for RX Match Not Finished interrupt */
> @@ -1029,68 +1027,76 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
>  		stats->rx_dropped++;
>  		stats->rx_errors++;
>  		netdev_err(ndev, "RX match not finished, frame discarded\n");
> -		if (skb) {
> -			cf->can_id |= CAN_ERR_CRTL;
> -			cf->data[1] |= CAN_ERR_CRTL_UNSPEC;
> -		}
> +		cf.can_id |= CAN_ERR_CRTL;
> +		cf.data[1] |= CAN_ERR_CRTL_UNSPEC;
>  	}
>  
>  	/* Check for error interrupt */
>  	if (isr & XCAN_IXR_ERROR_MASK) {
> -		if (skb)
> -			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +		bool berr_reporting = !!(priv->can.ctrlmode &
> +					 CAN_CTRLMODE_BERR_REPORTING);

Please use if() instead of !!

> +
> +		if (berr_reporting)
> +			cf.can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>  
>  		/* Check for Ack error interrupt */
>  		if (err_status & XCAN_ESR_ACKER_MASK) {
>  			stats->tx_errors++;
> -			if (skb) {
> -				cf->can_id |= CAN_ERR_ACK;
> -				cf->data[3] = CAN_ERR_PROT_LOC_ACK;
> +			if (berr_reporting) {
> +				cf.can_id |= CAN_ERR_ACK;
> +				cf.data[3] = CAN_ERR_PROT_LOC_ACK;
>  			}
>  		}
>  
>  		/* Check for Bit error interrupt */
>  		if (err_status & XCAN_ESR_BERR_MASK) {
>  			stats->tx_errors++;
> -			if (skb) {
> -				cf->can_id |= CAN_ERR_PROT;
> -				cf->data[2] = CAN_ERR_PROT_BIT;
> +			if (berr_reporting) {
> +				cf.can_id |= CAN_ERR_PROT;
> +				cf.data[2] = CAN_ERR_PROT_BIT;
>  			}
>  		}
>  
>  		/* Check for Stuff error interrupt */
>  		if (err_status & XCAN_ESR_STER_MASK) {
>  			stats->rx_errors++;
> -			if (skb) {
> -				cf->can_id |= CAN_ERR_PROT;
> -				cf->data[2] = CAN_ERR_PROT_STUFF;
> +			if (berr_reporting) {
> +				cf.can_id |= CAN_ERR_PROT;
> +				cf.data[2] = CAN_ERR_PROT_STUFF;
>  			}
>  		}
>  
>  		/* Check for Form error interrupt */
>  		if (err_status & XCAN_ESR_FMER_MASK) {
>  			stats->rx_errors++;
> -			if (skb) {
> -				cf->can_id |= CAN_ERR_PROT;
> -				cf->data[2] = CAN_ERR_PROT_FORM;
> +			if (berr_reporting) {
> +				cf.can_id |= CAN_ERR_PROT;
> +				cf.data[2] = CAN_ERR_PROT_FORM;
>  			}
>  		}
>  
>  		/* Check for CRC error interrupt */
>  		if (err_status & XCAN_ESR_CRCER_MASK) {
>  			stats->rx_errors++;
> -			if (skb) {
> -				cf->can_id |= CAN_ERR_PROT;
> -				cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
> +			if (berr_reporting) {
> +				cf.can_id |= CAN_ERR_PROT;
> +				cf.data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
>  			}
>  		}
>  		priv->can.can_stats.bus_error++;
>  	}
>  
> -	if (skb) {
> -		stats->rx_packets++;
> -		stats->rx_bytes += cf->can_dlc;
> -		netif_rx(skb);
> +	if (cf.can_id) {
> +		struct can_frame *skb_cf;
> +		struct sk_buff *skb = alloc_can_err_skb(ndev, &skb_cf);
> +
> +		if (skb) {
> +			skb_cf->can_id |= cf.can_id;
> +			memcpy(skb_cf->data, cf.data, CAN_ERR_DLC);
> +			stats->rx_packets++;
> +			stats->rx_bytes += CAN_ERR_DLC;
> +			netif_rx(skb);
> +		}
>  	}
>  
>  	netdev_dbg(ndev, "%s: error status register:0x%x\n",

I've send a v2 which incorporates these changes.

regards,
Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

Attachment: signature.asc
Description: OpenPGP digital signature


[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux