diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index f2faa779e3fe2..dd6b7c362813c 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -66,6 +66,8 @@ struct xenvif { /* The shared rings and indexes. */ struct xen_netif_tx_back_ring tx; struct xen_netif_rx_back_ring rx; + atomic_t ring_refcnt; + wait_queue_head_t waiting_to_unmap; /* Frontend feature information. */ u8 can_sg:1; @@ -120,6 +122,8 @@ void xenvif_free(struct xenvif *vif); void xenvif_get(struct xenvif *vif); void xenvif_put(struct xenvif *vif); +void xenvif_get_rings(struct xenvif *vif); +void xenvif_put_rings(struct xenvif *vif); int xenvif_xenbus_init(void); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 540a796593a34..7e3817a55e778 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -44,12 +44,23 @@ void xenvif_get(struct xenvif *vif) atomic_inc(&vif->refcnt); } +void xenvif_get_rings(struct xenvif *vif) +{ + atomic_inc(&vif->ring_refcnt); +} + void xenvif_put(struct xenvif *vif) { if (atomic_dec_and_test(&vif->refcnt)) wake_up(&vif->waiting_to_free); } +void xenvif_put_rings(struct xenvif *vif) +{ + if (atomic_dec_and_test(&vif->ring_refcnt)) + wake_up(&vif->waiting_to_unmap); +} + int xenvif_schedulable(struct xenvif *vif) { return netif_running(vif->dev) && netif_carrier_ok(vif->dev); @@ -91,6 +102,7 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Reserve ring slots for the worst-case number of fragments. */ vif->rx_req_cons_peek += xen_netbk_count_skb_slots(vif, skb); xenvif_get(vif); + xenvif_get_rings(vif); if (vif->can_queue && xen_netbk_must_stop_queue(vif)) netif_stop_queue(dev); @@ -271,6 +283,7 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, vif->dev = dev; INIT_LIST_HEAD(&vif->schedule_list); INIT_LIST_HEAD(&vif->notify_list); + init_waitqueue_head(&vif->waiting_to_unmap); vif->credit_bytes = vif->remaining_credit = ~0UL; vif->credit_usec = 0UL; @@ -365,12 +378,12 @@ void xenvif_disconnect(struct xenvif *vif) if (netif_carrier_ok(vif->dev)) xenvif_carrier_off(vif); + disable_irq(vif->irq); + xen_netbk_unmap_frontend_rings(vif); if (vif->irq) { unbind_from_irqhandler(vif->irq, vif); vif->irq = 0; } - - xen_netbk_unmap_frontend_rings(vif); } void xenvif_free(struct xenvif *vif) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 70b830f6c4bf3..1595f818b8c01 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -814,6 +814,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk) xenvif_put(vif); npo.meta_cons += sco->meta_slots_used; dev_kfree_skb(skb); + xenvif_put_rings(vif); } list_for_each_entry_safe(vif, tmp, ¬ify, notify_list) { @@ -1864,6 +1865,9 @@ static int xen_netbk_kthread(void *data) void xen_netbk_unmap_frontend_rings(struct xenvif *vif) { + atomic_dec(&vif->ring_refcnt); + wait_event(vif->waiting_to_unmap, atomic_read(&vif->ring_refcnt) == 0); + if (vif->tx.sring) xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif), vif->tx.sring); @@ -1882,6 +1886,8 @@ int xen_netbk_map_frontend_rings(struct xenvif *vif, int err = -ENOMEM; + atomic_set(&vif->ring_refcnt, 1); + err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif), tx_ring_ref, &addr); if (err)