asix: asix_rx_fixup surgery to reduce skb truesizes
[pandora-kernel.git] / drivers / net / usb / asix.c
index 6729585..5f6b4c3 100644 (file)
@@ -306,88 +306,40 @@ asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 
 static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 {
-       u8  *head;
-       u32  header;
-       char *packet;
-       struct sk_buff *ax_skb;
-       u16 size;
+       int offset = 0;
 
-       head = (u8 *) skb->data;
-       memcpy(&header, head, sizeof(header));
-       le32_to_cpus(&header);
-       packet = head + sizeof(header);
+       while (offset + sizeof(u32) < skb->len) {
+               struct sk_buff *ax_skb;
+               u16 size;
+               u32 header = get_unaligned_le32(skb->data + offset);
 
-       skb_pull(skb, 4);
-
-       while (skb->len > 0) {
-               if ((header & 0x07ff) != ((~header >> 16) & 0x07ff))
-                       netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
+               offset += sizeof(u32);
 
                /* get the packet length */
-               size = (u16) (header & 0x000007ff);
-
-               if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
-                       u8 alignment = (unsigned long)skb->data & 0x3;
-                       if (alignment != 0x2) {
-                               /*
-                                * not 16bit aligned so use the room provided by
-                                * the 32 bit header to align the data
-                                *
-                                * note we want 16bit alignment as MAC header is
-                                * 14bytes thus ip header will be aligned on
-                                * 32bit boundary so accessing ipheader elements
-                                * using a cast to struct ip header wont cause
-                                * an unaligned accesses.
-                                */
-                               u8 realignment = (alignment + 2) & 0x3;
-                               memmove(skb->data - realignment,
-                                       skb->data,
-                                       size);
-                               skb->data -= realignment;
-                               skb_set_tail_pointer(skb, size);
-                       }
-                       return 2;
+               size = (u16) (header & 0x7ff);
+               if (size != ((~header >> 16) & 0x07ff)) {
+                       netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
+                       return 0;
                }
 
-               if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
+               if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) ||
+                   (size + offset > skb->len)) {
                        netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
                                   size);
                        return 0;
                }
-               ax_skb = skb_clone(skb, GFP_ATOMIC);
-               if (ax_skb) {
-                       u8 alignment = (unsigned long)packet & 0x3;
-                       ax_skb->len = size;
-
-                       if (alignment != 0x2) {
-                               /*
-                                * not 16bit aligned use the room provided by
-                                * the 32 bit header to align the data
-                                */
-                               u8 realignment = (alignment + 2) & 0x3;
-                               memmove(packet - realignment, packet, size);
-                               packet -= realignment;
-                       }
-                       ax_skb->data = packet;
-                       skb_set_tail_pointer(ax_skb, size);
-                       usbnet_skb_return(dev, ax_skb);
-               } else {
+               ax_skb = netdev_alloc_skb_ip_align(dev->net, size);
+               if (!ax_skb)
                        return 0;
-               }
-
-               skb_pull(skb, (size + 1) & 0xfffe);
 
-               if (skb->len < sizeof(header))
-                       break;
+               skb_put(ax_skb, size);
+               memcpy(ax_skb->data, skb->data + offset, size);
+               usbnet_skb_return(dev, ax_skb);
 
-               head = (u8 *) skb->data;
-               memcpy(&header, head, sizeof(header));
-               le32_to_cpus(&header);
-               packet = head + sizeof(header);
-               skb_pull(skb, 4);
+               offset += (size + 1) & 0xfffe;
        }
 
-       if (skb->len < 0) {
+       if (skb->len != offset) {
                netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n",
                           skb->len);
                return 0;
@@ -1538,7 +1490,7 @@ static const struct driver_info ax88772_info = {
        .status = asix_status,
        .link_reset = ax88772_link_reset,
        .reset = ax88772_reset,
-       .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
+       .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
        .rx_fixup = asix_rx_fixup,
        .tx_fixup = asix_tx_fixup,
 };