diff options
Diffstat (limited to 'drivers/net/xen-netback/interface.c')
-rw-r--r-- | drivers/net/xen-netback/interface.c | 523 |
1 files changed, 338 insertions, 185 deletions
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 20e9defa1060..852da34b8961 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -43,6 +43,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 +60,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 +95,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 +110,80 @@ 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 xenvif *vif = (struct xenvif *)data; + struct net_device *dev = queue->vif->dev; + unsigned int id = queue->id; + return netif_tx_queue_stopped(netdev_get_tx_queue(dev, id)); +} - 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); +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_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); } } +static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + 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,14 @@ 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); + /* Allocate a netdev with the max. supported number of queues. + * When the guest selects the desired number, it will be updated + * via netif_set_real_num_tx_queues(). + */ + dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup, + xenvif_max_queues); if (dev == NULL) { pr_warn("Could not allocate netdev for %s\n", name); return ERR_PTR(-ENOMEM); @@ -334,66 +451,28 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, vif = netdev_priv(dev); - vif->grant_copy_op = vmalloc(sizeof(struct gnttab_copy) * - MAX_GRANT_COPY_OPS); - if (vif->grant_copy_op == NULL) { - pr_warn("Could not allocate grant copy space for %s\n", name); - free_netdev(dev); - return ERR_PTR(-ENOMEM); - } - vif->domid = domid; vif->handle = handle; 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 | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6; dev->features = dev->hw_features | NETIF_F_RXCSUM; - SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops); + dev->ethtool_ops = &xenvif_ethtool_ops; 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 @@ -403,8 +482,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); @@ -421,98 +498,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; @@ -529,38 +655,77 @@ 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; } - vif->tx_irq = 0; + + 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; + } + + xenvif_unmap_frontend_rings(queue); } +} - xenvif_unmap_frontend_rings(vif); +/* Reverse the relevant parts of xenvif_init_queue(). + * Used for queue teardown from xenvif_free(), and on the + * error handling paths in xenbus.c:connect(). + */ +void xenvif_deinit_queue(struct xenvif_queue *queue) +{ + free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages); + netif_napi_del(&queue->napi); } 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 @@ -575,33 +740,21 @@ 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; - } - } - - free_xenballooned_pages(MAX_PENDING_REQS, vif->mmap_pages); + unregister_netdev(vif->dev); - netif_napi_del(&vif->napi); + for (queue_index = 0; queue_index < num_queues; ++queue_index) { + queue = &vif->queues[queue_index]; + xenvif_wait_unmap_timeout(queue, worst_case_skb_lifetime); + xenvif_deinit_queue(queue); + } - unregister_netdev(vif->dev); + /* 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; - vfree(vif->grant_copy_op); free_netdev(vif->dev); module_put(THIS_MODULE); |