From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jeremy Fitzhardinge Subject: [patch 26/29] xen: fix netfront checksums Date: Fri, 04 May 2007 16:21:17 -0700 Message-ID: <20070504232121.554055808@goop.org> References: <20070504232051.411946839@goop.org> Return-path: Content-Disposition: inline; filename=xen-netfront-csum-fixes.patch List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: virtualization-bounces@lists.linux-foundation.org Errors-To: virtualization-bounces@lists.linux-foundation.org To: Andi Kleen Cc: Chris Wright , virtualization@lists.osdl.org, Andrew Morton , lkml , Herbert Xu List-Id: virtualization@lists.linuxfoundation.org If we receive a partially checksummed packed, we need to work out how much of it was checksummed based on its protocol. The ideal would be that Xen could tell us how much is checksummed, but in the meantime we'll make do with this. [ This is a separate patch for review; will be folded into xen-netfront later. ] From: Herbert Xu Signed-off-by: Herbert Xu Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Chris Wright --- drivers/net/xen-netfront.c | 76 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) =================================================================== --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -35,10 +35,12 @@ #include #include #include -#include #include +#include +#include #include #include +#include #include #include @@ -564,8 +566,12 @@ static int xennet_start_xmit(struct sk_b extra = NULL; tx->flags = 0; - if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */ + if (skb->ip_summed == CHECKSUM_PARTIAL) + /* local packet? */ tx->flags |= NETTXF_csum_blank | NETTXF_data_validated; + else if (skb->ip_summed == CHECKSUM_UNNECESSARY) + /* remote but checksummed. */ + tx->flags |= NETTXF_data_validated; if (skb_shinfo(skb)->gso_size) { struct xen_netif_extra_info *gso; @@ -867,9 +873,50 @@ static RING_IDX xennet_fill_frags(struct return cons; } -static void handle_incoming_queue(struct net_device *dev, +static int skb_checksum_setup(struct sk_buff *skb) +{ + struct iphdr *iph; + unsigned char *th; + int err = -EPROTO; + + if (skb->protocol != htons(ETH_P_IP)) + goto out; + + iph = (void *)skb->data; + th = skb->data + 4 * iph->ihl; + if (th >= skb_tail_pointer(skb)) + goto out; + + skb->csum_start = th - skb->head; + switch (iph->protocol) { + case IPPROTO_TCP: + skb->csum_offset = offsetof(struct tcphdr, check); + break; + case IPPROTO_UDP: + skb->csum_offset = offsetof(struct udphdr, check); + break; + default: + if (net_ratelimit()) + printk(KERN_ERR "Attempting to checksum a non-" + "TCP/UDP packet, dropping a protocol" + " %d packet", iph->protocol); + goto out; + } + + if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb)) + goto out; + + err = 0; + +out: + return err; +} + +static int handle_incoming_queue(struct net_device *dev, struct sk_buff_head *rxq) { + struct netfront_info *np = netdev_priv(dev); + int packets_dropped = 0; struct sk_buff *skb; while ((skb = __skb_dequeue(rxq)) != NULL) { @@ -886,10 +933,24 @@ static void handle_incoming_queue(struct /* Ethernet work: Delayed to here as it peeks the header. */ skb->protocol = eth_type_trans(skb, dev); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (skb_checksum_setup(skb)) { + kfree_skb(skb); + packets_dropped++; + np->stats.rx_errors++; + continue; + } + } + + np->stats.rx_packets++; + np->stats.rx_bytes += skb->len; + /* Pass it up. */ netif_receive_skb(skb); dev->last_rx = jiffies; } + + return packets_dropped; } static int xennet_poll(struct net_device *dev, int *pbudget) @@ -1003,11 +1064,10 @@ err: skb->truesize += skb->data_len - (RX_COPY_THRESHOLD - len); skb->len += skb->data_len; - if (rx->flags & NETRXF_data_validated) + if (rx->flags & NETRXF_csum_blank) + skb->ip_summed = CHECKSUM_PARTIAL; + else if (rx->flags & NETRXF_data_validated) skb->ip_summed = CHECKSUM_UNNECESSARY; - - np->stats.rx_packets++; - np->stats.rx_bytes += skb->len; __skb_queue_tail(&rxq, skb); @@ -1029,7 +1089,7 @@ err: while ((skb = __skb_dequeue(&errq))) kfree_skb(skb); - handle_incoming_queue(dev, &rxq); + work_done -= handle_incoming_queue(dev, &rxq); /* If we get a callback with very few responses, reduce fill target. */ /* NB. Note exponential increase, linear decrease. */ --