diff options
| author | Wei Liu <wei.liu2@citrix.com> | 2014-06-04 10:30:42 +0100 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-06-04 14:48:16 -0700 | 
| commit | e9ce7cb6b107407e4798e8905b18ad8b642766f6 (patch) | |
| tree | dd99d31fa4f2bae0e836c99a811e5de4e1202567 /drivers/net/xen-netback/interface.c | |
| parent | a55d9766cecf2b1b9af4fcf93b2d41b71e599c76 (diff) | |
| download | blackbird-op-linux-e9ce7cb6b107407e4798e8905b18ad8b642766f6.tar.gz blackbird-op-linux-e9ce7cb6b107407e4798e8905b18ad8b642766f6.zip | |
xen-netback: Factor queue-specific data into queue struct
In preparation for multi-queue support in xen-netback, move the
queue-specific data from struct xenvif into struct xenvif_queue, and
update the rest of the code to use this.
Also adds loops over queues where appropriate, even though only one is
configured at this point, and uses alloc_netdev_mq() and the
corresponding multi-queue netif wake/start/stop functions in preparation
for multiple active queues.
Finally, implements a trivial queue selection function suitable for
ndo_select_queue, which simply returns 0 for a single queue and uses
skb_get_hash() to compute the queue index otherwise.
Signed-off-by: Andrew J. Bennieston <andrew.bennieston@citrix.com>
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/xen-netback/interface.c')
| -rw-r--r-- | drivers/net/xen-netback/interface.c | 502 | 
1 files changed, 326 insertions, 176 deletions
| diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 8fdedac3fab2..6005b5d1d404 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -34,7 +34,6 @@  #include <linux/ethtool.h>  #include <linux/rtnetlink.h>  #include <linux/if_vlan.h> -#include <linux/vmalloc.h>  #include <xen/events.h>  #include <asm/xen/hypercall.h> @@ -43,6 +42,16 @@  #define XENVIF_QUEUE_LENGTH 32  #define XENVIF_NAPI_WEIGHT  64 +static inline void xenvif_stop_queue(struct xenvif_queue *queue) +{ +	struct net_device *dev = queue->vif->dev; + +	if (!queue->vif->can_queue) +		return; + +	netif_tx_stop_queue(netdev_get_tx_queue(dev, queue->id)); +} +  int xenvif_schedulable(struct xenvif *vif)  {  	return netif_running(vif->dev) && netif_carrier_ok(vif->dev); @@ -50,33 +59,34 @@ int xenvif_schedulable(struct xenvif *vif)  static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)  { -	struct xenvif *vif = dev_id; +	struct xenvif_queue *queue = dev_id; -	if (RING_HAS_UNCONSUMED_REQUESTS(&vif->tx)) -		napi_schedule(&vif->napi); +	if (RING_HAS_UNCONSUMED_REQUESTS(&queue->tx)) +		napi_schedule(&queue->napi);  	return IRQ_HANDLED;  } -static int xenvif_poll(struct napi_struct *napi, int budget) +int xenvif_poll(struct napi_struct *napi, int budget)  { -	struct xenvif *vif = container_of(napi, struct xenvif, napi); +	struct xenvif_queue *queue = +		container_of(napi, struct xenvif_queue, napi);  	int work_done;  	/* This vif is rogue, we pretend we've there is nothing to do  	 * for this vif to deschedule it from NAPI. But this interface  	 * will be turned off in thread context later.  	 */ -	if (unlikely(vif->disabled)) { +	if (unlikely(queue->vif->disabled)) {  		napi_complete(napi);  		return 0;  	} -	work_done = xenvif_tx_action(vif, budget); +	work_done = xenvif_tx_action(queue, budget);  	if (work_done < budget) {  		napi_complete(napi); -		xenvif_napi_schedule_or_enable_events(vif); +		xenvif_napi_schedule_or_enable_events(queue);  	}  	return work_done; @@ -84,9 +94,9 @@ static int xenvif_poll(struct napi_struct *napi, int budget)  static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)  { -	struct xenvif *vif = dev_id; +	struct xenvif_queue *queue = dev_id; -	xenvif_kick_thread(vif); +	xenvif_kick_thread(queue);  	return IRQ_HANDLED;  } @@ -99,28 +109,81 @@ static irqreturn_t xenvif_interrupt(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static void xenvif_wake_queue(unsigned long data) +int xenvif_queue_stopped(struct xenvif_queue *queue) +{ +	struct net_device *dev = queue->vif->dev; +	unsigned int id = queue->id; +	return netif_tx_queue_stopped(netdev_get_tx_queue(dev, id)); +} + +void xenvif_wake_queue(struct xenvif_queue *queue) +{ +	struct net_device *dev = queue->vif->dev; +	unsigned int id = queue->id; +	netif_tx_wake_queue(netdev_get_tx_queue(dev, id)); +} + +/* Callback to wake the queue and drain it on timeout */ +static void xenvif_wake_queue_callback(unsigned long data)  { -	struct xenvif *vif = (struct xenvif *)data; +	struct xenvif_queue *queue = (struct xenvif_queue *)data; + +	if (xenvif_queue_stopped(queue)) { +		netdev_err(queue->vif->dev, "draining TX queue\n"); +		queue->rx_queue_purge = true; +		xenvif_kick_thread(queue); +		xenvif_wake_queue(queue); +	} +} -	if (netif_queue_stopped(vif->dev)) { -		netdev_err(vif->dev, "draining TX queue\n"); -		vif->rx_queue_purge = true; -		xenvif_kick_thread(vif); -		netif_wake_queue(vif->dev); +static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb, +			       void *accel_priv, select_queue_fallback_t fallback) +{ +	struct xenvif *vif = netdev_priv(dev); +	unsigned int num_queues = dev->real_num_tx_queues; +	u32 hash; +	u16 queue_index; + +	/* First, check if there is only one queue to optimise the +	 * single-queue or old frontend scenario. +	 */ +	if (num_queues == 1) { +		queue_index = 0; +	} else { +		/* Use skb_get_hash to obtain an L4 hash if available */ +		hash = skb_get_hash(skb); +		queue_index = hash % num_queues;  	} + +	return queue_index;  }  static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)  {  	struct xenvif *vif = netdev_priv(dev); +	struct xenvif_queue *queue = NULL; +	unsigned int num_queues = dev->real_num_tx_queues; +	u16 index;  	int min_slots_needed;  	BUG_ON(skb->dev != dev); -	/* Drop the packet if vif is not ready */ -	if (vif->task == NULL || -	    vif->dealloc_task == NULL || +	/* Drop the packet if queues are not set up */ +	if (num_queues < 1) +		goto drop; + +	/* Obtain the queue to be used to transmit this packet */ +	index = skb_get_queue_mapping(skb); +	if (index >= num_queues) { +		pr_warn_ratelimited("Invalid queue %hu for packet on interface %s\n.", +				    index, vif->dev->name); +		index %= num_queues; +	} +	queue = &vif->queues[index]; + +	/* Drop the packet if queue is not ready */ +	if (queue->task == NULL || +	    queue->dealloc_task == NULL ||  	    !xenvif_schedulable(vif))  		goto drop; @@ -139,16 +202,16 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)  	 * then turn off the queue to give the ring a chance to  	 * drain.  	 */ -	if (!xenvif_rx_ring_slots_available(vif, min_slots_needed)) { -		vif->wake_queue.function = xenvif_wake_queue; -		vif->wake_queue.data = (unsigned long)vif; -		xenvif_stop_queue(vif); -		mod_timer(&vif->wake_queue, +	if (!xenvif_rx_ring_slots_available(queue, min_slots_needed)) { +		queue->wake_queue.function = xenvif_wake_queue_callback; +		queue->wake_queue.data = (unsigned long)queue; +		xenvif_stop_queue(queue); +		mod_timer(&queue->wake_queue,  			jiffies + rx_drain_timeout_jiffies);  	} -	skb_queue_tail(&vif->rx_queue, skb); -	xenvif_kick_thread(vif); +	skb_queue_tail(&queue->rx_queue, skb); +	xenvif_kick_thread(queue);  	return NETDEV_TX_OK; @@ -161,25 +224,65 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)  static struct net_device_stats *xenvif_get_stats(struct net_device *dev)  {  	struct xenvif *vif = netdev_priv(dev); +	struct xenvif_queue *queue = NULL; +	unsigned int num_queues = dev->real_num_tx_queues; +	unsigned long rx_bytes = 0; +	unsigned long rx_packets = 0; +	unsigned long tx_bytes = 0; +	unsigned long tx_packets = 0; +	unsigned int index; + +	if (vif->queues == NULL) +		goto out; + +	/* Aggregate tx and rx stats from each queue */ +	for (index = 0; index < num_queues; ++index) { +		queue = &vif->queues[index]; +		rx_bytes += queue->stats.rx_bytes; +		rx_packets += queue->stats.rx_packets; +		tx_bytes += queue->stats.tx_bytes; +		tx_packets += queue->stats.tx_packets; +	} + +out: +	vif->dev->stats.rx_bytes = rx_bytes; +	vif->dev->stats.rx_packets = rx_packets; +	vif->dev->stats.tx_bytes = tx_bytes; +	vif->dev->stats.tx_packets = tx_packets; +  	return &vif->dev->stats;  }  static void xenvif_up(struct xenvif *vif)  { -	napi_enable(&vif->napi); -	enable_irq(vif->tx_irq); -	if (vif->tx_irq != vif->rx_irq) -		enable_irq(vif->rx_irq); -	xenvif_napi_schedule_or_enable_events(vif); +	struct xenvif_queue *queue = NULL; +	unsigned int num_queues = vif->dev->real_num_tx_queues; +	unsigned int queue_index; + +	for (queue_index = 0; queue_index < num_queues; ++queue_index) { +		queue = &vif->queues[queue_index]; +		napi_enable(&queue->napi); +		enable_irq(queue->tx_irq); +		if (queue->tx_irq != queue->rx_irq) +			enable_irq(queue->rx_irq); +		xenvif_napi_schedule_or_enable_events(queue); +	}  }  static void xenvif_down(struct xenvif *vif)  { -	napi_disable(&vif->napi); -	disable_irq(vif->tx_irq); -	if (vif->tx_irq != vif->rx_irq) -		disable_irq(vif->rx_irq); -	del_timer_sync(&vif->credit_timeout); +	struct xenvif_queue *queue = NULL; +	unsigned int num_queues = vif->dev->real_num_tx_queues; +	unsigned int queue_index; + +	for (queue_index = 0; queue_index < num_queues; ++queue_index) { +		queue = &vif->queues[queue_index]; +		napi_disable(&queue->napi); +		disable_irq(queue->tx_irq); +		if (queue->tx_irq != queue->rx_irq) +			disable_irq(queue->rx_irq); +		del_timer_sync(&queue->credit_timeout); +	}  }  static int xenvif_open(struct net_device *dev) @@ -187,7 +290,7 @@ static int xenvif_open(struct net_device *dev)  	struct xenvif *vif = netdev_priv(dev);  	if (netif_carrier_ok(dev))  		xenvif_up(vif); -	netif_start_queue(dev); +	netif_tx_start_all_queues(dev);  	return 0;  } @@ -196,7 +299,7 @@ static int xenvif_close(struct net_device *dev)  	struct xenvif *vif = netdev_priv(dev);  	if (netif_carrier_ok(dev))  		xenvif_down(vif); -	netif_stop_queue(dev); +	netif_tx_stop_all_queues(dev);  	return 0;  } @@ -236,29 +339,29 @@ static const struct xenvif_stat {  } xenvif_stats[] = {  	{  		"rx_gso_checksum_fixup", -		offsetof(struct xenvif, rx_gso_checksum_fixup) +		offsetof(struct xenvif_stats, rx_gso_checksum_fixup)  	},  	/* If (sent != success + fail), there are probably packets never  	 * freed up properly!  	 */  	{  		"tx_zerocopy_sent", -		offsetof(struct xenvif, tx_zerocopy_sent), +		offsetof(struct xenvif_stats, tx_zerocopy_sent),  	},  	{  		"tx_zerocopy_success", -		offsetof(struct xenvif, tx_zerocopy_success), +		offsetof(struct xenvif_stats, tx_zerocopy_success),  	},  	{  		"tx_zerocopy_fail", -		offsetof(struct xenvif, tx_zerocopy_fail) +		offsetof(struct xenvif_stats, tx_zerocopy_fail)  	},  	/* Number of packets exceeding MAX_SKB_FRAG slots. You should use  	 * a guest with the same MAX_SKB_FRAG  	 */  	{  		"tx_frag_overflow", -		offsetof(struct xenvif, tx_frag_overflow) +		offsetof(struct xenvif_stats, tx_frag_overflow)  	},  }; @@ -275,11 +378,20 @@ static int xenvif_get_sset_count(struct net_device *dev, int string_set)  static void xenvif_get_ethtool_stats(struct net_device *dev,  				     struct ethtool_stats *stats, u64 * data)  { -	void *vif = netdev_priv(dev); +	struct xenvif *vif = netdev_priv(dev); +	unsigned int num_queues = dev->real_num_tx_queues;  	int i; - -	for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) -		data[i] = *(unsigned long *)(vif + xenvif_stats[i].offset); +	unsigned int queue_index; +	struct xenvif_stats *vif_stats; + +	for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) { +		unsigned long accum = 0; +		for (queue_index = 0; queue_index < num_queues; ++queue_index) { +			vif_stats = &vif->queues[queue_index].stats; +			accum += *(unsigned long *)(vif_stats + xenvif_stats[i].offset); +		} +		data[i] = accum; +	}  }  static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data) @@ -312,6 +424,7 @@ static const struct net_device_ops xenvif_netdev_ops = {  	.ndo_fix_features = xenvif_fix_features,  	.ndo_set_mac_address = eth_mac_addr,  	.ndo_validate_addr   = eth_validate_addr, +	.ndo_select_queue = xenvif_select_queue,  };  struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, @@ -321,10 +434,9 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,  	struct net_device *dev;  	struct xenvif *vif;  	char name[IFNAMSIZ] = {}; -	int i;  	snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle); -	dev = alloc_netdev(sizeof(struct xenvif), name, ether_setup); +	dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup, 1);  	if (dev == NULL) {  		pr_warn("Could not allocate netdev for %s\n", name);  		return ERR_PTR(-ENOMEM); @@ -339,15 +451,13 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,  	vif->can_sg = 1;  	vif->ip_csum = 1;  	vif->dev = dev; -  	vif->disabled = false; -	vif->credit_bytes = vif->remaining_credit = ~0UL; -	vif->credit_usec  = 0UL; -	init_timer(&vif->credit_timeout); -	vif->credit_window_start = get_jiffies_64(); - -	init_timer(&vif->wake_queue); +	/* Start out with no queues. The call below does not require +	 * rtnl_lock() as it happens before register_netdev(). +	 */ +	vif->queues = NULL; +	netif_set_real_num_tx_queues(dev, 0);  	dev->netdev_ops	= &xenvif_netdev_ops;  	dev->hw_features = NETIF_F_SG | @@ -358,34 +468,6 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,  	dev->tx_queue_len = XENVIF_QUEUE_LENGTH; -	skb_queue_head_init(&vif->rx_queue); -	skb_queue_head_init(&vif->tx_queue); - -	vif->pending_cons = 0; -	vif->pending_prod = MAX_PENDING_REQS; -	for (i = 0; i < MAX_PENDING_REQS; i++) -		vif->pending_ring[i] = i; -	spin_lock_init(&vif->callback_lock); -	spin_lock_init(&vif->response_lock); -	/* If ballooning is disabled, this will consume real memory, so you -	 * better enable it. The long term solution would be to use just a -	 * bunch of valid page descriptors, without dependency on ballooning -	 */ -	err = alloc_xenballooned_pages(MAX_PENDING_REQS, -				       vif->mmap_pages, -				       false); -	if (err) { -		netdev_err(dev, "Could not reserve mmap_pages\n"); -		return ERR_PTR(-ENOMEM); -	} -	for (i = 0; i < MAX_PENDING_REQS; i++) { -		vif->pending_tx_info[i].callback_struct = (struct ubuf_info) -			{ .callback = xenvif_zerocopy_callback, -			  .ctx = NULL, -			  .desc = i }; -		vif->grant_tx_handle[i] = NETBACK_INVALID_HANDLE; -	} -  	/*  	 * Initialise a dummy MAC address. We choose the numerically  	 * largest non-broadcast address to prevent the address getting @@ -395,8 +477,6 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,  	memset(dev->dev_addr, 0xFF, ETH_ALEN);  	dev->dev_addr[0] &= ~0x01; -	netif_napi_add(dev, &vif->napi, xenvif_poll, XENVIF_NAPI_WEIGHT); -  	netif_carrier_off(dev);  	err = register_netdev(dev); @@ -413,98 +493,147 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,  	return vif;  } -int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, +int xenvif_init_queue(struct xenvif_queue *queue) +{ +	int err, i; + +	queue->credit_bytes = queue->remaining_credit = ~0UL; +	queue->credit_usec  = 0UL; +	init_timer(&queue->credit_timeout); +	queue->credit_window_start = get_jiffies_64(); + +	skb_queue_head_init(&queue->rx_queue); +	skb_queue_head_init(&queue->tx_queue); + +	queue->pending_cons = 0; +	queue->pending_prod = MAX_PENDING_REQS; +	for (i = 0; i < MAX_PENDING_REQS; ++i) +		queue->pending_ring[i] = i; + +	spin_lock_init(&queue->callback_lock); +	spin_lock_init(&queue->response_lock); + +	/* If ballooning is disabled, this will consume real memory, so you +	 * better enable it. The long term solution would be to use just a +	 * bunch of valid page descriptors, without dependency on ballooning +	 */ +	err = alloc_xenballooned_pages(MAX_PENDING_REQS, +				       queue->mmap_pages, +				       false); +	if (err) { +		netdev_err(queue->vif->dev, "Could not reserve mmap_pages\n"); +		return -ENOMEM; +	} + +	for (i = 0; i < MAX_PENDING_REQS; i++) { +		queue->pending_tx_info[i].callback_struct = (struct ubuf_info) +			{ .callback = xenvif_zerocopy_callback, +			  .ctx = NULL, +			  .desc = i }; +		queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE; +	} + +	init_timer(&queue->wake_queue); + +	netif_napi_add(queue->vif->dev, &queue->napi, xenvif_poll, +			XENVIF_NAPI_WEIGHT); + +	return 0; +} + +void xenvif_carrier_on(struct xenvif *vif) +{ +	rtnl_lock(); +	if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN) +		dev_set_mtu(vif->dev, ETH_DATA_LEN); +	netdev_update_features(vif->dev); +	netif_carrier_on(vif->dev); +	if (netif_running(vif->dev)) +		xenvif_up(vif); +	rtnl_unlock(); +} + +int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,  		   unsigned long rx_ring_ref, unsigned int tx_evtchn,  		   unsigned int rx_evtchn)  {  	struct task_struct *task;  	int err = -ENOMEM; -	BUG_ON(vif->tx_irq); -	BUG_ON(vif->task); -	BUG_ON(vif->dealloc_task); +	BUG_ON(queue->tx_irq); +	BUG_ON(queue->task); +	BUG_ON(queue->dealloc_task); -	err = xenvif_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref); +	err = xenvif_map_frontend_rings(queue, tx_ring_ref, rx_ring_ref);  	if (err < 0)  		goto err; -	init_waitqueue_head(&vif->wq); -	init_waitqueue_head(&vif->dealloc_wq); +	init_waitqueue_head(&queue->wq); +	init_waitqueue_head(&queue->dealloc_wq);  	if (tx_evtchn == rx_evtchn) {  		/* feature-split-event-channels == 0 */  		err = bind_interdomain_evtchn_to_irqhandler( -			vif->domid, tx_evtchn, xenvif_interrupt, 0, -			vif->dev->name, vif); +			queue->vif->domid, tx_evtchn, xenvif_interrupt, 0, +			queue->name, queue);  		if (err < 0)  			goto err_unmap; -		vif->tx_irq = vif->rx_irq = err; -		disable_irq(vif->tx_irq); +		queue->tx_irq = queue->rx_irq = err; +		disable_irq(queue->tx_irq);  	} else {  		/* feature-split-event-channels == 1 */ -		snprintf(vif->tx_irq_name, sizeof(vif->tx_irq_name), -			 "%s-tx", vif->dev->name); +		snprintf(queue->tx_irq_name, sizeof(queue->tx_irq_name), +			 "%s-tx", queue->name);  		err = bind_interdomain_evtchn_to_irqhandler( -			vif->domid, tx_evtchn, xenvif_tx_interrupt, 0, -			vif->tx_irq_name, vif); +			queue->vif->domid, tx_evtchn, xenvif_tx_interrupt, 0, +			queue->tx_irq_name, queue);  		if (err < 0)  			goto err_unmap; -		vif->tx_irq = err; -		disable_irq(vif->tx_irq); +		queue->tx_irq = err; +		disable_irq(queue->tx_irq); -		snprintf(vif->rx_irq_name, sizeof(vif->rx_irq_name), -			 "%s-rx", vif->dev->name); +		snprintf(queue->rx_irq_name, sizeof(queue->rx_irq_name), +			 "%s-rx", queue->name);  		err = bind_interdomain_evtchn_to_irqhandler( -			vif->domid, rx_evtchn, xenvif_rx_interrupt, 0, -			vif->rx_irq_name, vif); +			queue->vif->domid, rx_evtchn, xenvif_rx_interrupt, 0, +			queue->rx_irq_name, queue);  		if (err < 0)  			goto err_tx_unbind; -		vif->rx_irq = err; -		disable_irq(vif->rx_irq); +		queue->rx_irq = err; +		disable_irq(queue->rx_irq);  	}  	task = kthread_create(xenvif_kthread_guest_rx, -			      (void *)vif, "%s-guest-rx", vif->dev->name); +			      (void *)queue, "%s-guest-rx", queue->name);  	if (IS_ERR(task)) { -		pr_warn("Could not allocate kthread for %s\n", vif->dev->name); +		pr_warn("Could not allocate kthread for %s\n", queue->name);  		err = PTR_ERR(task);  		goto err_rx_unbind;  	} - -	vif->task = task; +	queue->task = task;  	task = kthread_create(xenvif_dealloc_kthread, -			      (void *)vif, "%s-dealloc", vif->dev->name); +			      (void *)queue, "%s-dealloc", queue->name);  	if (IS_ERR(task)) { -		pr_warn("Could not allocate kthread for %s\n", vif->dev->name); +		pr_warn("Could not allocate kthread for %s\n", queue->name);  		err = PTR_ERR(task);  		goto err_rx_unbind;  	} +	queue->dealloc_task = task; -	vif->dealloc_task = task; - -	rtnl_lock(); -	if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN) -		dev_set_mtu(vif->dev, ETH_DATA_LEN); -	netdev_update_features(vif->dev); -	netif_carrier_on(vif->dev); -	if (netif_running(vif->dev)) -		xenvif_up(vif); -	rtnl_unlock(); - -	wake_up_process(vif->task); -	wake_up_process(vif->dealloc_task); +	wake_up_process(queue->task); +	wake_up_process(queue->dealloc_task);  	return 0;  err_rx_unbind: -	unbind_from_irqhandler(vif->rx_irq, vif); -	vif->rx_irq = 0; +	unbind_from_irqhandler(queue->rx_irq, queue); +	queue->rx_irq = 0;  err_tx_unbind: -	unbind_from_irqhandler(vif->tx_irq, vif); -	vif->tx_irq = 0; +	unbind_from_irqhandler(queue->tx_irq, queue); +	queue->tx_irq = 0;  err_unmap: -	xenvif_unmap_frontend_rings(vif); +	xenvif_unmap_frontend_rings(queue);  err:  	module_put(THIS_MODULE);  	return err; @@ -521,38 +650,67 @@ void xenvif_carrier_off(struct xenvif *vif)  	rtnl_unlock();  } +static void xenvif_wait_unmap_timeout(struct xenvif_queue *queue, +				      unsigned int worst_case_skb_lifetime) +{ +	int i, unmap_timeout = 0; + +	for (i = 0; i < MAX_PENDING_REQS; ++i) { +		if (queue->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) { +			unmap_timeout++; +			schedule_timeout(msecs_to_jiffies(1000)); +			if (unmap_timeout > worst_case_skb_lifetime && +			    net_ratelimit()) +				netdev_err(queue->vif->dev, +					   "Page still granted! Index: %x\n", +					   i); +			i = -1; +		} +	} +} +  void xenvif_disconnect(struct xenvif *vif)  { +	struct xenvif_queue *queue = NULL; +	unsigned int num_queues = vif->dev->real_num_tx_queues; +	unsigned int queue_index; +  	if (netif_carrier_ok(vif->dev))  		xenvif_carrier_off(vif); -	if (vif->task) { -		del_timer_sync(&vif->wake_queue); -		kthread_stop(vif->task); -		vif->task = NULL; -	} +	for (queue_index = 0; queue_index < num_queues; ++queue_index) { +		queue = &vif->queues[queue_index]; -	if (vif->dealloc_task) { -		kthread_stop(vif->dealloc_task); -		vif->dealloc_task = NULL; -	} +		if (queue->task) { +			del_timer_sync(&queue->wake_queue); +			kthread_stop(queue->task); +			queue->task = NULL; +		} -	if (vif->tx_irq) { -		if (vif->tx_irq == vif->rx_irq) -			unbind_from_irqhandler(vif->tx_irq, vif); -		else { -			unbind_from_irqhandler(vif->tx_irq, vif); -			unbind_from_irqhandler(vif->rx_irq, vif); +		if (queue->dealloc_task) { +			kthread_stop(queue->dealloc_task); +			queue->dealloc_task = NULL; +		} + +		if (queue->tx_irq) { +			if (queue->tx_irq == queue->rx_irq) +				unbind_from_irqhandler(queue->tx_irq, queue); +			else { +				unbind_from_irqhandler(queue->tx_irq, queue); +				unbind_from_irqhandler(queue->rx_irq, queue); +			} +			queue->tx_irq = 0;  		} -		vif->tx_irq = 0; -	} -	xenvif_unmap_frontend_rings(vif); +		xenvif_unmap_frontend_rings(queue); +	}  }  void xenvif_free(struct xenvif *vif)  { -	int i, unmap_timeout = 0; +	struct xenvif_queue *queue = NULL; +	unsigned int num_queues = vif->dev->real_num_tx_queues; +	unsigned int queue_index;  	/* Here we want to avoid timeout messages if an skb can be legitimately  	 * stuck somewhere else. Realistically this could be an another vif's  	 * internal or QDisc queue. That another vif also has this @@ -567,31 +725,23 @@ void xenvif_free(struct xenvif *vif)  	unsigned int worst_case_skb_lifetime = (rx_drain_timeout_msecs/1000) *  		DIV_ROUND_UP(XENVIF_QUEUE_LENGTH, (XEN_NETIF_RX_RING_SIZE / MAX_SKB_FRAGS)); -	for (i = 0; i < MAX_PENDING_REQS; ++i) { -		if (vif->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) { -			unmap_timeout++; -			schedule_timeout(msecs_to_jiffies(1000)); -			if (unmap_timeout > worst_case_skb_lifetime && -			    net_ratelimit()) -				netdev_err(vif->dev, -					   "Page still granted! Index: %x\n", -					   i); -			/* If there are still unmapped pages, reset the loop to -			 * start checking again. We shouldn't exit here until -			 * dealloc thread and NAPI instance release all the -			 * pages. If a kernel bug causes the skbs to stall -			 * somewhere, the interface cannot be brought down -			 * properly. -			 */ -			i = -1; -		} -	} +	unregister_netdev(vif->dev); -	free_xenballooned_pages(MAX_PENDING_REQS, vif->mmap_pages); +	for (queue_index = 0; queue_index < num_queues; ++queue_index) { +		queue = &vif->queues[queue_index]; -	netif_napi_del(&vif->napi); +		xenvif_wait_unmap_timeout(queue, worst_case_skb_lifetime); +		free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages); -	unregister_netdev(vif->dev); +		netif_napi_del(&queue->napi); +	} + +	/* Free the array of queues. The call below does not require +	 * rtnl_lock() because it happens after unregister_netdev(). +	 */ +	netif_set_real_num_tx_queues(vif->dev, 0); +	vfree(vif->queues); +	vif->queues = NULL;  	free_netdev(vif->dev); | 

