pcifront/back: Fix handling of device disconnect.

Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>

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;
 	}
 }
