| From 5b10ebf13d0cda519ac15382c33c178c945af99e Mon Sep 17 00:00:00 2001 |
| From: Oliver Hartkopp <oliver@hartkopp.net> |
| Date: Thu, 8 May 2008 02:49:55 -0700 |
| Subject: can: Fix can_send() handling on dev_queue_xmit() failures |
| |
| From: Oliver Hartkopp <oliver@hartkopp.net> |
| |
| [ Upstream commit: c2ab7ac225e29006b7117d6a9fe8f3be8d98b0c2 ] |
| |
| The tx packet counting and the local loopback of CAN frames should |
| only happen in the case that the CAN frame has been enqueued to the |
| netdevice tx queue successfully. |
| |
| Thanks to Andre Naujoks <nautsch@gmail.com> for reporting this issue. |
| |
| Signed-off-by: Oliver Hartkopp <oliver@hartkopp.net> |
| Signed-off-by: Urs Thuermann <urs@isnogud.escape.de> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| net/can/af_can.c | 16 ++++++++++++---- |
| 1 file changed, 12 insertions(+), 4 deletions(-) |
| |
| --- a/net/can/af_can.c |
| +++ b/net/can/af_can.c |
| @@ -208,6 +208,7 @@ static int can_create(struct net *net, s |
| */ |
| int can_send(struct sk_buff *skb, int loop) |
| { |
| + struct sk_buff *newskb = NULL; |
| int err; |
| |
| if (skb->dev->type != ARPHRD_CAN) { |
| @@ -244,8 +245,7 @@ int can_send(struct sk_buff *skb, int lo |
| * If the interface is not capable to do loopback |
| * itself, we do it here. |
| */ |
| - struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); |
| - |
| + newskb = skb_clone(skb, GFP_ATOMIC); |
| if (!newskb) { |
| kfree_skb(skb); |
| return -ENOMEM; |
| @@ -254,7 +254,6 @@ int can_send(struct sk_buff *skb, int lo |
| newskb->sk = skb->sk; |
| newskb->ip_summed = CHECKSUM_UNNECESSARY; |
| newskb->pkt_type = PACKET_BROADCAST; |
| - netif_rx(newskb); |
| } |
| } else { |
| /* indication for the CAN driver: no loopback required */ |
| @@ -266,11 +265,20 @@ int can_send(struct sk_buff *skb, int lo |
| if (err > 0) |
| err = net_xmit_errno(err); |
| |
| + if (err) { |
| + if (newskb) |
| + kfree_skb(newskb); |
| + return err; |
| + } |
| + |
| + if (newskb) |
| + netif_rx(newskb); |
| + |
| /* update statistics */ |
| can_stats.tx_frames++; |
| can_stats.tx_frames_delta++; |
| |
| - return err; |
| + return 0; |
| } |
| EXPORT_SYMBOL(can_send); |
| |