pcifront/back: Fix handling of device disconnect. Signed-off-by: Yosuke Iwamatsu diff -r a682229d0eac drivers/xen/pciback/xenbus.c --- a/drivers/xen/pciback/xenbus.c Tue Jul 15 16:39:39 2008 +0100 +++ b/drivers/xen/pciback/xenbus.c Wed Jul 16 11:44:29 2008 +0900 @@ -42,22 +42,35 @@ static struct pciback_device *alloc_pdev return pdev; } -static void free_pdev(struct pciback_device *pdev) -{ - if (pdev->be_watching) - unregister_xenbus_watch(&pdev->be_watch); +static void pciback_disconnect(struct pciback_device *pdev) +{ + spin_lock(&pdev->dev_lock); /* Ensure the guest can't trigger our handler before removing devices */ - if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) + if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) { unbind_from_irqhandler(pdev->evtchn_irq, pdev); + pdev->evtchn_irq = INVALID_EVTCHN_IRQ; + } /* If the driver domain started an op, make sure we complete it or * delete it before releasing the shared memory */ cancel_delayed_work(&pdev->op_work); flush_scheduled_work(); - if (pdev->sh_info) + if (pdev->sh_info != NULL) { xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_area); + pdev->sh_info = NULL; + } + + spin_unlock(&pdev->dev_lock); +} + +static void free_pdev(struct pciback_device *pdev) +{ + if (pdev->be_watching) + unregister_xenbus_watch(&pdev->be_watch); + + pciback_disconnect(pdev); pciback_release_devices(pdev); @@ -471,16 +484,6 @@ static void pciback_frontend_changed(str pciback_attach(pdev); break; - case XenbusStateClosing: - xenbus_switch_state(xdev, XenbusStateClosed); - break; - - case XenbusStateUnknown: - case XenbusStateClosed: - dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); - device_unregister(&xdev->dev); - break; - case XenbusStateReconfiguring: pciback_reconfigure(pdev); break; @@ -490,6 +493,22 @@ static void pciback_frontend_changed(str * Then switch to connected state. */ xenbus_switch_state(xdev, XenbusStateConnected); + break; + + case XenbusStateClosing: + pciback_disconnect(pdev); + xenbus_switch_state(xdev, XenbusStateClosing); + break; + + case XenbusStateClosed: + pciback_disconnect(pdev); + xenbus_switch_state(xdev, XenbusStateClosed); + if (xenbus_dev_is_online(xdev)) + break; + /* fall through if not online */ + case XenbusStateUnknown: + dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); + device_unregister(&xdev->dev); break; default: diff -r a682229d0eac drivers/xen/pcifront/xenbus.c --- a/drivers/xen/pcifront/xenbus.c Tue Jul 15 16:39:39 2008 +0100 +++ b/drivers/xen/pcifront/xenbus.c Wed Jul 16 11:44:29 2008 +0900 @@ -207,12 +207,17 @@ static int pcifront_try_disconnect(struc prev_state = xenbus_read_driver_state(pdev->xdev->nodename); - if (prev_state < XenbusStateClosing) - err = xenbus_switch_state(pdev->xdev, XenbusStateClosing); - - if (!err && prev_state == XenbusStateConnected) + if (prev_state >= XenbusStateClosing) + goto out; + + if(prev_state == XenbusStateConnected) { + pcifront_free_roots(pdev); pcifront_disconnect(pdev); - + } + + err = xenbus_switch_state(pdev->xdev, XenbusStateClosed); + + out: spin_unlock(&pdev->dev_lock); return err; @@ -370,32 +375,28 @@ static void __init_refok pcifront_backen struct pcifront_device *pdev = xdev->dev.driver_data; switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateClosed: + break; + + case XenbusStateConnected: + pcifront_try_connect(pdev); + break; + case XenbusStateClosing: dev_warn(&xdev->dev, "backend going away!\n"); pcifront_try_disconnect(pdev); break; - case XenbusStateUnknown: - case XenbusStateClosed: - dev_warn(&xdev->dev, "backend went away!\n"); - pcifront_try_disconnect(pdev); - - device_unregister(&pdev->xdev->dev); - break; - - case XenbusStateConnected: - pcifront_try_connect(pdev); - break; - case XenbusStateReconfiguring: pcifront_detach_devices(pdev); break; case XenbusStateReconfigured: pcifront_attach_devices(pdev); - break; - - default: break; } }