]> Pileus Git - ~andy/linux/blobdiff - drivers/net/xen-netfront.c
xen-netfront: handle backend CLOSED without CLOSING
[~andy/linux] / drivers / net / xen-netfront.c
index 2ab82fe75edeeb6192c5088408082c8f0318e10f..f9daa9e183f216e7114a1d1b604fe4fc6b6d3861 100644 (file)
@@ -117,6 +117,7 @@ struct netfront_info {
        } tx_skbs[NET_TX_RING_SIZE];
        grant_ref_t gref_tx_head;
        grant_ref_t grant_tx_ref[NET_TX_RING_SIZE];
+       struct page *grant_tx_page[NET_TX_RING_SIZE];
        unsigned tx_skb_freelist;
 
        spinlock_t   rx_lock ____cacheline_aligned_in_smp;
@@ -396,6 +397,7 @@ static void xennet_tx_buf_gc(struct net_device *dev)
                        gnttab_release_grant_reference(
                                &np->gref_tx_head, np->grant_tx_ref[id]);
                        np->grant_tx_ref[id] = GRANT_INVALID_REF;
+                       np->grant_tx_page[id] = NULL;
                        add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, id);
                        dev_kfree_skb_irq(skb);
                }
@@ -452,6 +454,7 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id,
                                                mfn, GNTMAP_readonly);
 
+               np->grant_tx_page[id] = virt_to_page(data);
                tx->gref = np->grant_tx_ref[id] = ref;
                tx->offset = offset;
                tx->size = len;
@@ -497,6 +500,7 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                                                        np->xbdev->otherend_id,
                                                        mfn, GNTMAP_readonly);
 
+                       np->grant_tx_page[id] = page;
                        tx->gref = np->grant_tx_ref[id] = ref;
                        tx->offset = offset;
                        tx->size = bytes;
@@ -596,6 +600,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        mfn = virt_to_mfn(data);
        gnttab_grant_foreign_access_ref(
                ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly);
+       np->grant_tx_page[id] = virt_to_page(data);
        tx->gref = np->grant_tx_ref[id] = ref;
        tx->offset = offset;
        tx->size = len;
@@ -617,7 +622,9 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                tx->flags |= XEN_NETTXF_extra_info;
 
                gso->u.gso.size = skb_shinfo(skb)->gso_size;
-               gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
+               gso->u.gso.type = (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) ?
+                       XEN_NETIF_GSO_TYPE_TCPV6 :
+                       XEN_NETIF_GSO_TYPE_TCPV4;
                gso->u.gso.pad = 0;
                gso->u.gso.features = 0;
 
@@ -809,15 +816,18 @@ static int xennet_set_skb_gso(struct sk_buff *skb,
                return -EINVAL;
        }
 
-       /* Currently only TCPv4 S.O. is supported. */
-       if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) {
+       if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4 &&
+           gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV6) {
                if (net_ratelimit())
                        pr_warn("Bad GSO type %d\n", gso->u.gso.type);
                return -EINVAL;
        }
 
        skb_shinfo(skb)->gso_size = gso->u.gso.size;
-       skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+       skb_shinfo(skb)->gso_type =
+               (gso->u.gso.type == XEN_NETIF_GSO_TYPE_TCPV4) ?
+               SKB_GSO_TCPV4 :
+               SKB_GSO_TCPV6;
 
        /* Header must be checked, and gso_segs computed. */
        skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
@@ -859,9 +869,7 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np,
 
 static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
 {
-       struct iphdr *iph;
-       int err = -EPROTO;
-       int recalculate_partial_csum = 0;
+       bool recalculate_partial_csum = false;
 
        /*
         * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy
@@ -873,54 +881,14 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
                struct netfront_info *np = netdev_priv(dev);
                np->rx_gso_checksum_fixup++;
                skb->ip_summed = CHECKSUM_PARTIAL;
-               recalculate_partial_csum = 1;
+               recalculate_partial_csum = true;
        }
 
        /* A non-CHECKSUM_PARTIAL SKB does not require setup. */
        if (skb->ip_summed != CHECKSUM_PARTIAL)
                return 0;
 
-       if (skb->protocol != htons(ETH_P_IP))
-               goto out;
-
-       iph = (void *)skb->data;
-
-       switch (iph->protocol) {
-       case IPPROTO_TCP:
-               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
-                                         offsetof(struct tcphdr, check)))
-                       goto out;
-
-               if (recalculate_partial_csum) {
-                       struct tcphdr *tcph = tcp_hdr(skb);
-                       tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                        skb->len - iph->ihl*4,
-                                                        IPPROTO_TCP, 0);
-               }
-               break;
-       case IPPROTO_UDP:
-               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
-                                         offsetof(struct udphdr, check)))
-                       goto out;
-
-               if (recalculate_partial_csum) {
-                       struct udphdr *udph = udp_hdr(skb);
-                       udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                        skb->len - iph->ihl*4,
-                                                        IPPROTO_UDP, 0);
-               }
-               break;
-       default:
-               if (net_ratelimit())
-                       pr_err("Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n",
-                              iph->protocol);
-               goto out;
-       }
-
-       err = 0;
-
-out:
-       return err;
+       return skb_checksum_setup(skb, recalculate_partial_csum);
 }
 
 static int handle_incoming_queue(struct net_device *dev,
@@ -1122,10 +1090,11 @@ static void xennet_release_tx_bufs(struct netfront_info *np)
                        continue;
 
                skb = np->tx_skbs[i].skb;
-               gnttab_end_foreign_access_ref(np->grant_tx_ref[i],
-                                             GNTMAP_readonly);
-               gnttab_release_grant_reference(&np->gref_tx_head,
-                                              np->grant_tx_ref[i]);
+               get_page(np->grant_tx_page[i]);
+               gnttab_end_foreign_access(np->grant_tx_ref[i],
+                                         GNTMAP_readonly,
+                                         (unsigned long)page_address(np->grant_tx_page[i]));
+               np->grant_tx_page[i] = NULL;
                np->grant_tx_ref[i] = GRANT_INVALID_REF;
                add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, i);
                dev_kfree_skb_irq(skb);
@@ -1134,78 +1103,35 @@ static void xennet_release_tx_bufs(struct netfront_info *np)
 
 static void xennet_release_rx_bufs(struct netfront_info *np)
 {
-       struct mmu_update      *mmu = np->rx_mmu;
-       struct multicall_entry *mcl = np->rx_mcl;
-       struct sk_buff_head free_list;
-       struct sk_buff *skb;
-       unsigned long mfn;
-       int xfer = 0, noxfer = 0, unused = 0;
        int id, ref;
 
-       dev_warn(&np->netdev->dev, "%s: fix me for copying receiver.\n",
-                        __func__);
-       return;
-
-       skb_queue_head_init(&free_list);
-
        spin_lock_bh(&np->rx_lock);
 
        for (id = 0; id < NET_RX_RING_SIZE; id++) {
-               ref = np->grant_rx_ref[id];
-               if (ref == GRANT_INVALID_REF) {
-                       unused++;
-                       continue;
-               }
+               struct sk_buff *skb;
+               struct page *page;
 
                skb = np->rx_skbs[id];
-               mfn = gnttab_end_foreign_transfer_ref(ref);
-               gnttab_release_grant_reference(&np->gref_rx_head, ref);
-               np->grant_rx_ref[id] = GRANT_INVALID_REF;
-
-               if (0 == mfn) {
-                       skb_shinfo(skb)->nr_frags = 0;
-                       dev_kfree_skb(skb);
-                       noxfer++;
+               if (!skb)
                        continue;
-               }
 
-               if (!xen_feature(XENFEAT_auto_translated_physmap)) {
-                       /* Remap the page. */
-                       const struct page *page =
-                               skb_frag_page(&skb_shinfo(skb)->frags[0]);
-                       unsigned long pfn = page_to_pfn(page);
-                       void *vaddr = page_address(page);
-
-                       MULTI_update_va_mapping(mcl, (unsigned long)vaddr,
-                                               mfn_pte(mfn, PAGE_KERNEL),
-                                               0);
-                       mcl++;
-                       mmu->ptr = ((u64)mfn << PAGE_SHIFT)
-                               | MMU_MACHPHYS_UPDATE;
-                       mmu->val = pfn;
-                       mmu++;
+               ref = np->grant_rx_ref[id];
+               if (ref == GRANT_INVALID_REF)
+                       continue;
 
-                       set_phys_to_machine(pfn, mfn);
-               }
-               __skb_queue_tail(&free_list, skb);
-               xfer++;
-       }
+               page = skb_frag_page(&skb_shinfo(skb)->frags[0]);
 
-       dev_info(&np->netdev->dev, "%s: %d xfer, %d noxfer, %d unused\n",
-                __func__, xfer, noxfer, unused);
+               /* gnttab_end_foreign_access() needs a page ref until
+                * foreign access is ended (which may be deferred).
+                */
+               get_page(page);
+               gnttab_end_foreign_access(ref, 0,
+                                         (unsigned long)page_address(page));
+               np->grant_rx_ref[id] = GRANT_INVALID_REF;
 
-       if (xfer) {
-               if (!xen_feature(XENFEAT_auto_translated_physmap)) {
-                       /* Do all the remapping work and M2P updates. */
-                       MULTI_mmu_update(mcl, np->rx_mmu, mmu - np->rx_mmu,
-                                        NULL, DOMID_SELF);
-                       mcl++;
-                       HYPERVISOR_multicall(np->rx_mcl, mcl - np->rx_mcl);
-               }
+               kfree_skb(skb);
        }
 
-       __skb_queue_purge(&free_list);
-
        spin_unlock_bh(&np->rx_lock);
 }
 
@@ -1233,6 +1159,15 @@ static netdev_features_t xennet_fix_features(struct net_device *dev,
                        features &= ~NETIF_F_SG;
        }
 
+       if (features & NETIF_F_IPV6_CSUM) {
+               if (xenbus_scanf(XBT_NIL, np->xbdev->otherend,
+                                "feature-ipv6-csum-offload", "%d", &val) < 0)
+                       val = 0;
+
+               if (!val)
+                       features &= ~NETIF_F_IPV6_CSUM;
+       }
+
        if (features & NETIF_F_TSO) {
                if (xenbus_scanf(XBT_NIL, np->xbdev->otherend,
                                 "feature-gso-tcpv4", "%d", &val) < 0)
@@ -1242,6 +1177,15 @@ static netdev_features_t xennet_fix_features(struct net_device *dev,
                        features &= ~NETIF_F_TSO;
        }
 
+       if (features & NETIF_F_TSO6) {
+               if (xenbus_scanf(XBT_NIL, np->xbdev->otherend,
+                                "feature-gso-tcpv6", "%d", &val) < 0)
+                       val = 0;
+
+               if (!val)
+                       features &= ~NETIF_F_TSO6;
+       }
+
        return features;
 }
 
@@ -1358,6 +1302,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
        for (i = 0; i < NET_RX_RING_SIZE; i++) {
                np->rx_skbs[i] = NULL;
                np->grant_rx_ref[i] = GRANT_INVALID_REF;
+               np->grant_tx_page[i] = NULL;
        }
 
        /* A grant for every tx ring slot */
@@ -1380,7 +1325,9 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
        netif_napi_add(netdev, &np->napi, xennet_poll, 64);
        netdev->features        = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                                  NETIF_F_GSO_ROBUST;
-       netdev->hw_features     = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO;
+       netdev->hw_features     = NETIF_F_SG |
+                                 NETIF_F_IPV6_CSUM |
+                                 NETIF_F_TSO | NETIF_F_TSO6;
 
        /*
          * Assume that all hw features are available for now. This set
@@ -1758,6 +1705,19 @@ again:
                goto abort_transaction;
        }
 
+       err = xenbus_write(xbt, dev->nodename, "feature-gso-tcpv6", "1");
+       if (err) {
+               message = "writing feature-gso-tcpv6";
+               goto abort_transaction;
+       }
+
+       err = xenbus_write(xbt, dev->nodename, "feature-ipv6-csum-offload",
+                          "1");
+       if (err) {
+               message = "writing feature-ipv6-csum-offload";
+               goto abort_transaction;
+       }
+
        err = xenbus_transaction_end(xbt, 0);
        if (err) {
                if (err == -EAGAIN)
@@ -1872,7 +1832,6 @@ static void netback_changed(struct xenbus_device *dev,
        case XenbusStateReconfiguring:
        case XenbusStateReconfigured:
        case XenbusStateUnknown:
-       case XenbusStateClosed:
                break;
 
        case XenbusStateInitWait:
@@ -1887,6 +1846,10 @@ static void netback_changed(struct xenbus_device *dev,
                netdev_notify_peers(netdev);
                break;
 
+       case XenbusStateClosed:
+               if (dev->state == XenbusStateClosed)
+                       break;
+               /* Missed the backend's CLOSING state -- fallthrough */
        case XenbusStateClosing:
                xenbus_frontend_closed(dev);
                break;