diff options
Diffstat (limited to 'drivers/net/xen-netfront.c')
-rw-r--r-- | drivers/net/xen-netfront.c | 203 |
1 files changed, 117 insertions, 86 deletions
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index ff04d4f95baa..36808bf25677 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -117,7 +117,6 @@ 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; @@ -278,13 +277,12 @@ static void xennet_alloc_rx_buffers(struct net_device *dev) if (!page) { kfree_skb(skb); no_skb: - /* Could not allocate any skbuffs. Try again later. */ - mod_timer(&np->rx_refill_timer, - jiffies + (HZ/10)); - /* Any skbuffs queued for refill? Force them out. */ if (i != 0) goto refill; + /* Could not allocate any skbuffs. Try again later. */ + mod_timer(&np->rx_refill_timer, + jiffies + (HZ/10)); break; } @@ -397,7 +395,6 @@ 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); } @@ -454,7 +451,6 @@ 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; @@ -500,7 +496,6 @@ 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; @@ -600,7 +595,6 @@ 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; @@ -622,9 +616,7 @@ 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 = (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) ? - XEN_NETIF_GSO_TYPE_TCPV6 : - XEN_NETIF_GSO_TYPE_TCPV4; + gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4; gso->u.gso.pad = 0; gso->u.gso.features = 0; @@ -816,18 +808,15 @@ static int xennet_set_skb_gso(struct sk_buff *skb, return -EINVAL; } - if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4 && - gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV6) { + /* Currently only TCPv4 S.O. is supported. */ + if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { 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 = - (gso->u.gso.type == XEN_NETIF_GSO_TYPE_TCPV4) ? - SKB_GSO_TCPV4 : - SKB_GSO_TCPV6; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; /* Header must be checked, and gso_segs computed. */ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; @@ -869,7 +858,9 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np, static int checksum_setup(struct net_device *dev, struct sk_buff *skb) { - bool recalculate_partial_csum = false; + struct iphdr *iph; + int err = -EPROTO; + int recalculate_partial_csum = 0; /* * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy @@ -881,14 +872,54 @@ 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 = true; + recalculate_partial_csum = 1; } /* A non-CHECKSUM_PARTIAL SKB does not require setup. */ if (skb->ip_summed != CHECKSUM_PARTIAL) return 0; - return skb_checksum_setup(skb, recalculate_partial_csum); + 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; } static int handle_incoming_queue(struct net_device *dev, @@ -921,7 +952,7 @@ static int handle_incoming_queue(struct net_device *dev, u64_stats_update_end(&stats->syncp); /* Pass it up. */ - napi_gro_receive(&np->napi, skb); + netif_receive_skb(skb); } return packets_dropped; @@ -1020,8 +1051,6 @@ err: if (work_done < budget) { int more_to_do = 0; - napi_gro_flush(napi, false); - local_irq_save(flags); RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do); @@ -1090,11 +1119,10 @@ static void xennet_release_tx_bufs(struct netfront_info *np) continue; skb = np->tx_skbs[i].skb; - 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; + 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]); 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); @@ -1103,35 +1131,78 @@ 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++) { - struct sk_buff *skb; - struct page *page; + ref = np->grant_rx_ref[id]; + if (ref == GRANT_INVALID_REF) { + unused++; + continue; + } skb = np->rx_skbs[id]; - if (!skb) - continue; + mfn = gnttab_end_foreign_transfer_ref(ref); + gnttab_release_grant_reference(&np->gref_rx_head, ref); + np->grant_rx_ref[id] = GRANT_INVALID_REF; - ref = np->grant_rx_ref[id]; - if (ref == GRANT_INVALID_REF) + if (0 == mfn) { + skb_shinfo(skb)->nr_frags = 0; + dev_kfree_skb(skb); + noxfer++; continue; + } - page = skb_frag_page(&skb_shinfo(skb)->frags[0]); + 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); - /* 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; + 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++; - kfree_skb(skb); + set_phys_to_machine(pfn, mfn); + } + __skb_queue_tail(&free_list, skb); + xfer++; } + dev_info(&np->netdev->dev, "%s: %d xfer, %d noxfer, %d unused\n", + __func__, xfer, noxfer, unused); + + 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); + } + } + + __skb_queue_purge(&free_list); + spin_unlock_bh(&np->rx_lock); } @@ -1159,15 +1230,6 @@ 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) @@ -1177,15 +1239,6 @@ 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; } @@ -1285,12 +1338,6 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) if (np->stats == NULL) goto exit; - for_each_possible_cpu(i) { - struct netfront_stats *xen_nf_stats; - xen_nf_stats = per_cpu_ptr(np->stats, i); - u64_stats_init(&xen_nf_stats->syncp); - } - /* Initialise tx_skbs as a free chain containing every entry. */ np->tx_skb_freelist = 0; for (i = 0; i < NET_TX_RING_SIZE; i++) { @@ -1302,7 +1349,6 @@ 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 */ @@ -1325,9 +1371,7 @@ 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_SG | - NETIF_F_IPV6_CSUM | - NETIF_F_TSO | NETIF_F_TSO6; + netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO; /* * Assume that all hw features are available for now. This set @@ -1705,19 +1749,6 @@ 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) @@ -2075,7 +2106,7 @@ static int __init netif_init(void) if (!xen_domain()) return -ENODEV; - if (!xen_has_pv_nic_devices()) + if (xen_hvm_domain() && !xen_platform_pci_unplug) return -ENODEV; pr_info("Initialising Xen virtual ethernet driver\n"); |