Commit a9e0aca4 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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: default avatarEric 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: default avatarGrant Grundler <grundler@chromium.org>
Reviewed-by: default avatarGrant Grundler <grundler@chromium.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1174764e
Loading
Loading
Loading
Loading
+20 −68
Original line number Diff line number Diff line
@@ -305,88 +305,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;
	int offset = 0;

	while (offset + sizeof(u32) < skb->len) {
		struct sk_buff *ax_skb;
		u16 size;
		u32 header = get_unaligned_le32(skb->data + offset);

	head = (u8 *) skb->data;
	memcpy(&header, head, sizeof(header));
	le32_to_cpus(&header);
	packet = head + sizeof(header);

	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) {
		if ((size > dev->net->mtu + ETH_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;
@@ -1541,7 +1493,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,
};