tilegx: fix some issues in the SW TSO support
authorChris Metcalf <cmetcalf@tilera.com>
Thu, 25 Oct 2012 07:25:20 +0000 (07:25 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 26 Oct 2012 07:50:08 +0000 (03:50 -0400)
This change correctly computes the header length and data length in
the fragments to avoid a bug where we would end up with extremely
slow performance.  Also adopt use of skb_frag_size() accessor.

Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Cc: stable@vger.kernel.org [v3.6]
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/tile/tilegx.c

index 4e2a1628484dfbc3a6fff901d976cf751bf8c151..4e9810013850ca3b4765ce002de0715d6b2c935b 100644 (file)
@@ -1334,11 +1334,11 @@ static int tso_count_edescs(struct sk_buff *skb)
 {
        struct skb_shared_info *sh = skb_shinfo(skb);
        unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
-       unsigned int data_len = skb->data_len + skb->hdr_len - sh_len;
+       unsigned int data_len = skb->len - sh_len;
        unsigned int p_len = sh->gso_size;
        long f_id = -1;    /* id of the current fragment */
-       long f_size = skb->hdr_len;  /* size of the current fragment */
-       long f_used = sh_len;  /* bytes used from the current fragment */
+       long f_size = skb_headlen(skb) - sh_len;  /* current fragment size */
+       long f_used = 0;  /* bytes used from the current fragment */
        long n;            /* size of the current piece of payload */
        int num_edescs = 0;
        int segment;
@@ -1353,7 +1353,7 @@ static int tso_count_edescs(struct sk_buff *skb)
                        /* Advance as needed. */
                        while (f_used >= f_size) {
                                f_id++;
-                               f_size = sh->frags[f_id].size;
+                               f_size = skb_frag_size(&sh->frags[f_id]);
                                f_used = 0;
                        }
 
@@ -1384,13 +1384,13 @@ static void tso_headers_prepare(struct sk_buff *skb, unsigned char *headers,
        struct iphdr *ih;
        struct tcphdr *th;
        unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
-       unsigned int data_len = skb->data_len + skb->hdr_len - sh_len;
+       unsigned int data_len = skb->len - sh_len;
        unsigned char *data = skb->data;
        unsigned int ih_off, th_off, p_len;
        unsigned int isum_seed, tsum_seed, id, seq;
        long f_id = -1;    /* id of the current fragment */
-       long f_size = skb->hdr_len;  /* size of the current fragment */
-       long f_used = sh_len;  /* bytes used from the current fragment */
+       long f_size = skb_headlen(skb) - sh_len;  /* current fragment size */
+       long f_used = 0;  /* bytes used from the current fragment */
        long n;            /* size of the current piece of payload */
        int segment;
 
@@ -1405,7 +1405,7 @@ static void tso_headers_prepare(struct sk_buff *skb, unsigned char *headers,
        isum_seed = ((0xFFFF - ih->check) +
                     (0xFFFF - ih->tot_len) +
                     (0xFFFF - ih->id));
-       tsum_seed = th->check + (0xFFFF ^ htons(sh_len + data_len));
+       tsum_seed = th->check + (0xFFFF ^ htons(skb->len));
        id = ntohs(ih->id);
        seq = ntohl(th->seq);
 
@@ -1444,7 +1444,7 @@ static void tso_headers_prepare(struct sk_buff *skb, unsigned char *headers,
                        /* Advance as needed. */
                        while (f_used >= f_size) {
                                f_id++;
-                               f_size = sh->frags[f_id].size;
+                               f_size = skb_frag_size(&sh->frags[f_id]);
                                f_used = 0;
                        }
 
@@ -1478,14 +1478,14 @@ static void tso_egress(struct net_device *dev, gxio_mpipe_equeue_t *equeue,
        struct tile_net_priv *priv = netdev_priv(dev);
        struct skb_shared_info *sh = skb_shinfo(skb);
        unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
-       unsigned int data_len = skb->data_len + skb->hdr_len - sh_len;
+       unsigned int data_len = skb->len - sh_len;
        unsigned int p_len = sh->gso_size;
        gxio_mpipe_edesc_t edesc_head = { { 0 } };
        gxio_mpipe_edesc_t edesc_body = { { 0 } };
        long f_id = -1;    /* id of the current fragment */
-       long f_size = skb->hdr_len;  /* size of the current fragment */
-       long f_used = sh_len;  /* bytes used from the current fragment */
-       void *f_data = skb->data;
+       long f_size = skb_headlen(skb) - sh_len;  /* current fragment size */
+       long f_used = 0;  /* bytes used from the current fragment */
+       void *f_data = skb->data + sh_len;
        long n;            /* size of the current piece of payload */
        unsigned long tx_packets = 0, tx_bytes = 0;
        unsigned int csum_start;
@@ -1516,15 +1516,18 @@ static void tso_egress(struct net_device *dev, gxio_mpipe_equeue_t *equeue,
 
                /* Egress the payload. */
                while (p_used < p_len) {
+                       void *va;
 
                        /* Advance as needed. */
                        while (f_used >= f_size) {
                                f_id++;
-                               f_size = sh->frags[f_id].size;
-                               f_used = 0;
+                               f_size = skb_frag_size(&sh->frags[f_id]);
                                f_data = tile_net_frag_buf(&sh->frags[f_id]);
+                               f_used = 0;
                        }
 
+                       va = f_data + f_used;
+
                        /* Use bytes from the current fragment. */
                        n = p_len - p_used;
                        if (n > f_size - f_used)
@@ -1533,7 +1536,7 @@ static void tso_egress(struct net_device *dev, gxio_mpipe_equeue_t *equeue,
                        p_used += n;
 
                        /* Egress a piece of the payload. */
-                       edesc_body.va = va_to_tile_io_addr(f_data) + f_used;
+                       edesc_body.va = va_to_tile_io_addr(va);
                        edesc_body.xfer_size = n;
                        edesc_body.bound = !(p_used < p_len);
                        gxio_mpipe_equeue_put_at(equeue, edesc_body, slot);