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>
This commit is contained in:
Родитель
1174764e81
Коммит
a9e0aca4b3
|
@ -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)
|
static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
u8 *head;
|
int offset = 0;
|
||||||
u32 header;
|
|
||||||
char *packet;
|
while (offset + sizeof(u32) < skb->len) {
|
||||||
struct sk_buff *ax_skb;
|
struct sk_buff *ax_skb;
|
||||||
u16 size;
|
u16 size;
|
||||||
|
u32 header = get_unaligned_le32(skb->data + offset);
|
||||||
|
|
||||||
head = (u8 *) skb->data;
|
offset += sizeof(u32);
|
||||||
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");
|
|
||||||
|
|
||||||
/* get the packet length */
|
/* get the packet length */
|
||||||
size = (u16) (header & 0x000007ff);
|
size = (u16) (header & 0x7ff);
|
||||||
|
if (size != ((~header >> 16) & 0x07ff)) {
|
||||||
if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
|
netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
|
||||||
u8 alignment = (unsigned long)skb->data & 0x3;
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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",
|
netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
|
||||||
size);
|
size);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ax_skb = skb_clone(skb, GFP_ATOMIC);
|
ax_skb = netdev_alloc_skb_ip_align(dev->net, size);
|
||||||
if (ax_skb) {
|
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 {
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
skb_put(ax_skb, size);
|
||||||
|
memcpy(ax_skb->data, skb->data + offset, size);
|
||||||
|
usbnet_skb_return(dev, ax_skb);
|
||||||
|
|
||||||
|
offset += (size + 1) & 0xfffe;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_pull(skb, (size + 1) & 0xfffe);
|
if (skb->len != offset) {
|
||||||
|
|
||||||
if (skb->len < sizeof(header))
|
|
||||||
break;
|
|
||||||
|
|
||||||
head = (u8 *) skb->data;
|
|
||||||
memcpy(&header, head, sizeof(header));
|
|
||||||
le32_to_cpus(&header);
|
|
||||||
packet = head + sizeof(header);
|
|
||||||
skb_pull(skb, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skb->len < 0) {
|
|
||||||
netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n",
|
netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n",
|
||||||
skb->len);
|
skb->len);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1541,7 +1493,7 @@ static const struct driver_info ax88772_info = {
|
||||||
.status = asix_status,
|
.status = asix_status,
|
||||||
.link_reset = ax88772_link_reset,
|
.link_reset = ax88772_link_reset,
|
||||||
.reset = ax88772_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,
|
.rx_fixup = asix_rx_fixup,
|
||||||
.tx_fixup = asix_tx_fixup,
|
.tx_fixup = asix_tx_fixup,
|
||||||
};
|
};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче