diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-03-14 16:18:32 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-03-16 04:52:13 -0400 |
commit | a9e0aca4b37885b5599e52211f098bd7f565e749 (patch) | |
tree | 7f4edd4ed8a96bfccca216ef4ce84d29d6049f9d /drivers/net/usb/asix.c | |
parent | 1174764e810998e81b334b5ccdfad8a9d059c6a1 (diff) |
asix: asix_rx_fixup surgery to reduce skb truesizes
asix_rx_fixup() is complex, and does some unnecessary memory copies (at
least on x86 where NET_IP_ALIGN is 0)
Also, it tends to provide skbs with a big truesize (4096+256 with
MTU=1500) to upper stack, so incoming trafic consume a lot of memory and
I noticed early packet drops because we hit socket rcvbuf too fast.
Switch to a different strategy, using copybreak so that we provide nice
skbs to upper stack (including the NET_SKB_PAD to avoid future head
reallocations in some paths)
With this patch, I no longer see packets drops or tcp collapses on
various tcp workload with a AX88772 adapter.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Aurelien Jacobs <aurel@gnuage.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Trond Wuellner <trond@chromium.org>
Cc: Grant Grundler <grundler@chromium.org>
Cc: Paul Stewart <pstew@chromium.org>
Reviewed-by: Grant Grundler <grundler@chromium.org>
Reviewed-by: Grant Grundler <grundler@chromium.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb/asix.c')
-rw-r--r-- | drivers/net/usb/asix.c | 88 |
1 files changed, 20 insertions, 68 deletions
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 8e84f5bdd6ca..25fe1838d886 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c | |||
@@ -305,88 +305,40 @@ asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, | |||
305 | 305 | ||
306 | static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | 306 | static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) |
307 | { | 307 | { |
308 | u8 *head; | 308 | int offset = 0; |
309 | u32 header; | ||
310 | char *packet; | ||
311 | struct sk_buff *ax_skb; | ||
312 | u16 size; | ||
313 | 309 | ||
314 | head = (u8 *) skb->data; | 310 | while (offset + sizeof(u32) < skb->len) { |
315 | memcpy(&header, head, sizeof(header)); | 311 | struct sk_buff *ax_skb; |
316 | le32_to_cpus(&header); | 312 | u16 size; |
317 | packet = head + sizeof(header); | 313 | u32 header = get_unaligned_le32(skb->data + offset); |
318 | 314 | ||
319 | skb_pull(skb, 4); | 315 | offset += sizeof(u32); |
320 | |||
321 | while (skb->len > 0) { | ||
322 | if ((header & 0x07ff) != ((~header >> 16) & 0x07ff)) | ||
323 | netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); | ||
324 | 316 | ||
325 | /* get the packet length */ | 317 | /* get the packet length */ |
326 | size = (u16) (header & 0x000007ff); | 318 | size = (u16) (header & 0x7ff); |
327 | 319 | if (size != ((~header >> 16) & 0x07ff)) { | |
328 | if ((skb->len) - ((size + 1) & 0xfffe) == 0) { | 320 | netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); |
329 | u8 alignment = (unsigned long)skb->data & 0x3; | 321 | return 0; |
330 | if (alignment != 0x2) { | ||
331 | /* | ||
332 | * not 16bit aligned so use the room provided by | ||
333 | * the 32 bit header to align the data | ||
334 | * | ||
335 | * note we want 16bit alignment as MAC header is | ||
336 | * 14bytes thus ip header will be aligned on | ||
337 | * 32bit boundary so accessing ipheader elements | ||
338 | * using a cast to struct ip header wont cause | ||
339 | * an unaligned accesses. | ||
340 | */ | ||
341 | u8 realignment = (alignment + 2) & 0x3; | ||
342 | memmove(skb->data - realignment, | ||
343 | skb->data, | ||
344 | size); | ||
345 | skb->data -= realignment; | ||
346 | skb_set_tail_pointer(skb, size); | ||
347 | } | ||
348 | return 2; | ||
349 | } | 322 | } |
350 | 323 | ||
351 | if (size > dev->net->mtu + ETH_HLEN) { | 324 | if ((size > dev->net->mtu + ETH_HLEN) || |
325 | (size + offset > skb->len)) { | ||
352 | netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", | 326 | netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", |
353 | size); | 327 | size); |
354 | return 0; | 328 | return 0; |
355 | } | 329 | } |
356 | ax_skb = skb_clone(skb, GFP_ATOMIC); | 330 | ax_skb = netdev_alloc_skb_ip_align(dev->net, size); |
357 | if (ax_skb) { | 331 | if (!ax_skb) |
358 | u8 alignment = (unsigned long)packet & 0x3; | ||
359 | ax_skb->len = size; | ||
360 | |||
361 | if (alignment != 0x2) { | ||
362 | /* | ||
363 | * not 16bit aligned use the room provided by | ||
364 | * the 32 bit header to align the data | ||
365 | */ | ||
366 | u8 realignment = (alignment + 2) & 0x3; | ||
367 | memmove(packet - realignment, packet, size); | ||
368 | packet -= realignment; | ||
369 | } | ||
370 | ax_skb->data = packet; | ||
371 | skb_set_tail_pointer(ax_skb, size); | ||
372 | usbnet_skb_return(dev, ax_skb); | ||
373 | } else { | ||
374 | return 0; | 332 | return 0; |
375 | } | ||
376 | |||
377 | skb_pull(skb, (size + 1) & 0xfffe); | ||
378 | 333 | ||
379 | if (skb->len < sizeof(header)) | 334 | skb_put(ax_skb, size); |
380 | break; | 335 | memcpy(ax_skb->data, skb->data + offset, size); |
336 | usbnet_skb_return(dev, ax_skb); | ||
381 | 337 | ||
382 | head = (u8 *) skb->data; | 338 | offset += (size + 1) & 0xfffe; |
383 | memcpy(&header, head, sizeof(header)); | ||
384 | le32_to_cpus(&header); | ||
385 | packet = head + sizeof(header); | ||
386 | skb_pull(skb, 4); | ||
387 | } | 339 | } |
388 | 340 | ||
389 | if (skb->len < 0) { | 341 | if (skb->len != offset) { |
390 | netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n", | 342 | netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n", |
391 | skb->len); | 343 | skb->len); |
392 | return 0; | 344 | return 0; |
@@ -1541,7 +1493,7 @@ static const struct driver_info ax88772_info = { | |||
1541 | .status = asix_status, | 1493 | .status = asix_status, |
1542 | .link_reset = ax88772_link_reset, | 1494 | .link_reset = ax88772_link_reset, |
1543 | .reset = ax88772_reset, | 1495 | .reset = ax88772_reset, |
1544 | .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, | 1496 | .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, |
1545 | .rx_fixup = asix_rx_fixup, | 1497 | .rx_fixup = asix_rx_fixup, |
1546 | .tx_fixup = asix_tx_fixup, | 1498 | .tx_fixup = asix_tx_fixup, |
1547 | }; | 1499 | }; |