diff options
author | Eric Dumazet <edumazet@google.com> | 2012-07-05 00:31:01 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-07-07 19:26:56 -0400 |
commit | 95162d652477f6e3d04687f5d39d443fcc64d8d7 (patch) | |
tree | 8685d62afd2b8b77fe9d7d866d22733976364bab /drivers/net | |
parent | cabdc8ee3768ceb6367e88c6fe84a66dd667bdf9 (diff) |
asix: avoid copies in tx path
I noticed excess calls to skb_copy_expand() or memmove() in asix driver.
This driver needs to push 4 bytes in front of frame (packet_len)
and maybe add 4 bytes after the end (if padlen is 4)
So it should set needed_headroom & needed_tailroom to avoid
copies. But its not enough, because many packets are cloned
before entering asix_tx_fixup() and this driver use skb_cloned()
as a lazy way to check if it can push and put additional bytes in frame.
Avoid skb_copy_expand() expensive call, using following rules :
- We are allowed to push 4 bytes in headroom if skb_header_cloned()
is false (and if we have 4 bytes of headroom)
- We are allowed to put 4 bytes at tail if skb_cloned()
is false (and if we have 4 bytes of tailroom)
TCP packets for example are cloned, but skb_header_release()
was called in tcp stack, allowing us to use headroom for our needs.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Allan Chou <allan@asix.com.tw>
Cc: Trond Wuellner <trond@chromium.org>
Cc: Grant Grundler <grundler@chromium.org>
Cc: Paul Stewart <pstew@chromium.org>
Cc: Ming Lei <tom.leiming@gmail.com>
Tested-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/usb/asix.c | 28 |
1 files changed, 24 insertions, 4 deletions
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 3ae80eccd0ef..6564c32d3af0 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c | |||
@@ -358,14 +358,30 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | |||
358 | 358 | ||
359 | padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; | 359 | padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; |
360 | 360 | ||
361 | if ((!skb_cloned(skb)) && | 361 | /* We need to push 4 bytes in front of frame (packet_len) |
362 | ((headroom + tailroom) >= (4 + padlen))) { | 362 | * and maybe add 4 bytes after the end (if padlen is 4) |
363 | if ((headroom < 4) || (tailroom < padlen)) { | 363 | * |
364 | * Avoid skb_copy_expand() expensive call, using following rules : | ||
365 | * - We are allowed to push 4 bytes in headroom if skb_header_cloned() | ||
366 | * is false (and if we have 4 bytes of headroom) | ||
367 | * - We are allowed to put 4 bytes at tail if skb_cloned() | ||
368 | * is false (and if we have 4 bytes of tailroom) | ||
369 | * | ||
370 | * TCP packets for example are cloned, but skb_header_release() | ||
371 | * was called in tcp stack, allowing us to use headroom for our needs. | ||
372 | */ | ||
373 | if (!skb_header_cloned(skb) && | ||
374 | !(padlen && skb_cloned(skb)) && | ||
375 | headroom + tailroom >= 4 + padlen) { | ||
376 | /* following should not happen, but better be safe */ | ||
377 | if (headroom < 4 || | ||
378 | tailroom < padlen) { | ||
364 | skb->data = memmove(skb->head + 4, skb->data, skb->len); | 379 | skb->data = memmove(skb->head + 4, skb->data, skb->len); |
365 | skb_set_tail_pointer(skb, skb->len); | 380 | skb_set_tail_pointer(skb, skb->len); |
366 | } | 381 | } |
367 | } else { | 382 | } else { |
368 | struct sk_buff *skb2; | 383 | struct sk_buff *skb2; |
384 | |||
369 | skb2 = skb_copy_expand(skb, 4, padlen, flags); | 385 | skb2 = skb_copy_expand(skb, 4, padlen, flags); |
370 | dev_kfree_skb_any(skb); | 386 | dev_kfree_skb_any(skb); |
371 | skb = skb2; | 387 | skb = skb2; |
@@ -373,8 +389,8 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | |||
373 | return NULL; | 389 | return NULL; |
374 | } | 390 | } |
375 | 391 | ||
392 | packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; | ||
376 | skb_push(skb, 4); | 393 | skb_push(skb, 4); |
377 | packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); | ||
378 | cpu_to_le32s(&packet_len); | 394 | cpu_to_le32s(&packet_len); |
379 | skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); | 395 | skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); |
380 | 396 | ||
@@ -880,6 +896,8 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf) | |||
880 | 896 | ||
881 | dev->net->netdev_ops = &ax88172_netdev_ops; | 897 | dev->net->netdev_ops = &ax88172_netdev_ops; |
882 | dev->net->ethtool_ops = &ax88172_ethtool_ops; | 898 | dev->net->ethtool_ops = &ax88172_ethtool_ops; |
899 | dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ | ||
900 | dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ | ||
883 | 901 | ||
884 | asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); | 902 | asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); |
885 | asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, | 903 | asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, |
@@ -1075,6 +1093,8 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) | |||
1075 | 1093 | ||
1076 | dev->net->netdev_ops = &ax88772_netdev_ops; | 1094 | dev->net->netdev_ops = &ax88772_netdev_ops; |
1077 | dev->net->ethtool_ops = &ax88772_ethtool_ops; | 1095 | dev->net->ethtool_ops = &ax88772_ethtool_ops; |
1096 | dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ | ||
1097 | dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ | ||
1078 | 1098 | ||
1079 | embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); | 1099 | embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); |
1080 | 1100 | ||