X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fxen%2Fnetback%2Floopback.c;h=d98725adb9c0da1f01a856403ac61a905ba0bcca;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=e4e438b438a2a99ecd7cd38fa7970365470d1fe8;hpb=1db395853d4f30d6120458bd279ede1f882a8525;p=linux-2.6.git diff --git a/drivers/xen/netback/loopback.c b/drivers/xen/netback/loopback.c index e4e438b43..d98725adb 100644 --- a/drivers/xen/netback/loopback.c +++ b/drivers/xen/netback/loopback.c @@ -45,7 +45,6 @@ * IN THE SOFTWARE. */ -#include #include #include #include @@ -53,8 +52,12 @@ #include #include #include +#include /* secpath_reset() */ +#include /* is_initial_xendomain() */ -static int nloopbacks = 8; +#include "../../../net/core/kmap_skb.h" + +static int nloopbacks = -1; module_param(nloopbacks, int, 0); MODULE_PARM_DESC(nloopbacks, "Number of netback-loopback devices to create"); @@ -77,10 +80,60 @@ static int loopback_close(struct net_device *dev) return 0; } +#ifdef CONFIG_X86 +static int is_foreign(unsigned long pfn) +{ + /* NB. Play it safe for auto-translation mode. */ + return (xen_feature(XENFEAT_auto_translated_physmap) || + (phys_to_machine_mapping[pfn] & FOREIGN_FRAME_BIT)); +} +#else +/* How to detect a foreign mapping? Play it safe. */ +#define is_foreign(pfn) (1) +#endif + +static int skb_remove_foreign_references(struct sk_buff *skb) +{ + struct page *page; + unsigned long pfn; + int i, off; + char *vaddr; + + BUG_ON(skb_shinfo(skb)->frag_list); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + pfn = page_to_pfn(skb_shinfo(skb)->frags[i].page); + if (!is_foreign(pfn)) + continue; + + page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!page)) + return 0; + + vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]); + off = skb_shinfo(skb)->frags[i].page_offset; + memcpy(page_address(page) + off, + vaddr + off, + skb_shinfo(skb)->frags[i].size); + kunmap_skb_frag(vaddr); + + put_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb)->frags[i].page = page; + } + + return 1; +} + static int loopback_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_private *np = netdev_priv(dev); + if (!skb_remove_foreign_references(skb)) { + np->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + dst_release(skb->dst); skb->dst = NULL; @@ -96,7 +149,7 @@ static int loopback_start_xmit(struct sk_buff *skb, struct net_device *dev) np->stats.rx_bytes += skb->len; np->stats.rx_packets++; - if (skb->ip_summed == CHECKSUM_HW) { + if (skb->ip_summed == CHECKSUM_PARTIAL) { /* Defer checksum calculation. */ skb->proto_csum_blank = 1; /* Must be a local packet: assert its integrity. */ @@ -110,6 +163,11 @@ static int loopback_start_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; dev->last_rx = jiffies; + + /* Flush netfilter context: rx'ed skbuffs not expected to have any. */ + nf_reset(skb); + secpath_reset(skb); + netif_rx(skb); return 0; @@ -218,7 +276,7 @@ static int __init make_loopback(int i) return err; } -static void __init clean_loopback(int i) +static void __exit clean_loopback(int i) { struct net_device *dev1, *dev2; char dev_name[IFNAMSIZ]; @@ -239,6 +297,9 @@ static int __init loopback_init(void) { int i, err = 0; + if (nloopbacks == -1) + nloopbacks = is_initial_xendomain() ? 4 : 0; + for (i = 0; i < nloopbacks; i++) if ((err = make_loopback(i)) != 0) break;