When deregistering a bus, attempt to flush out all outstanding
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 2 Dec 2007 00:30:35 +0000 (00:30 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 2 Dec 2007 00:30:35 +0000 (00:30 +0000)
operations before returning. Point the bus at a dummy cam_sim
structure so that any CCBs will complete immediately with a
CAM_DEV_NOT_THERE status, and ensure that any xpt_schedule() calls
on the bus's devices will immediately call the peripheral's
periph_start() routine. Also repeat the async messages because
devices that were part of the way through being probed may appear
after the original AC_LOST_DEVICE was sent, and would otherwise
never go away.

Obtained-from: FreeBSD

sys/bus/cam/cam_xpt.c

index c47e1f0..382a7ff 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam_xpt.c,v 1.80.2.18 2002/12/09 17:31:55 gibbs Exp $
- * $DragonFly: src/sys/bus/cam/cam_xpt.c,v 1.55 2007/12/01 22:49:49 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/cam_xpt.c,v 1.56 2007/12/02 00:30:35 pavalos Exp $
  */
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -643,6 +643,18 @@ static struct dev_ops xpt_ops = {
 
 static struct intr_config_hook *xpt_config_hook;
 
+static void dead_sim_action(struct cam_sim *sim, union ccb *ccb);
+static void dead_sim_poll(struct cam_sim *sim);
+
+/* Dummy SIM that is used when the real one has gone. */
+static struct cam_sim cam_dead_sim = {
+       .sim_action =   dead_sim_action,
+       .sim_poll =     dead_sim_poll,
+       .sim_name =     "dead_sim",
+};
+
+#define SIM_DEAD(sim)  ((sim) == &cam_dead_sim)
+
 /* Registered busses */
 static TAILQ_HEAD(,cam_eb) xpt_busses;
 static u_int bus_generation;
@@ -2989,10 +3001,19 @@ xpt_action(union ccb *start_ccb)
        case XPT_ENG_EXEC:
        {
                struct cam_path *path;
+               struct cam_sim *sim;
                int runq;
 
                path = start_ccb->ccb_h.path;
 
+               sim = path->bus->sim;
+               if (SIM_DEAD(sim)) {
+                       /* The SIM has gone; just execute the CCB directly. */
+                       cam_ccbq_send_ccb(&path->device->ccbq, start_ccb);
+                       (*(sim->sim_action))(sim, start_ccb);
+                       break;
+               }
+
                cam_ccbq_insert_ccb(&path->device->ccbq, start_ccb);
                if (path->device->qfrozen_cnt == 0)
                        runq = xpt_schedule_dev_sendq(path->bus, path->device);
@@ -3562,6 +3583,7 @@ void
 xpt_schedule(struct cam_periph *perph, u_int32_t new_priority)
 {
        struct cam_ed *device;
+       union ccb *work_ccb;
        int runq;
 
        CAM_DEBUG(perph->path, CAM_DEBUG_TRACE, ("xpt_schedule\n"));
@@ -3577,6 +3599,16 @@ xpt_schedule(struct cam_periph *perph, u_int32_t new_priority)
                                             new_priority);
                }
                runq = 0;
+       } else if (SIM_DEAD(perph->path->bus->sim)) {
+               /* The SIM is gone so just call periph_start directly. */
+               work_ccb = xpt_get_ccb(perph->path->device);
+               crit_exit();
+               if (work_ccb == NULL)
+                       return; /* XXX */
+               xpt_setup_ccb(&work_ccb->ccb_h, perph->path, new_priority);
+               perph->pinfo.priority = new_priority;
+               perph->periph_start(perph, work_ccb);
+               return;
        } else {
                /* New entry on the queue */
                CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE,
@@ -4164,10 +4196,12 @@ xpt_release_ccb(union ccb *free_ccb)
        } else {
                SLIST_INSERT_HEAD(&ccb_freeq, &free_ccb->ccb_h, xpt_links.sle);
        }
-       if (bus->sim->devq) {
-               bus->sim->devq->alloc_openings++;
-               bus->sim->devq->alloc_active--;
+       if (bus->sim->devq == NULL) {
+               crit_exit();
+               return;
        }
+       bus->sim->devq->alloc_openings++;
+       bus->sim->devq->alloc_active--;
        /* XXX Turn this into an inline function - xpt_run_device?? */
        if ((device_is_alloc_queued(device) == 0)
         && (device->drvq.entries > 0)) {
@@ -4248,6 +4282,12 @@ int32_t
 xpt_bus_deregister(path_id_t pathid)
 {
        struct cam_path bus_path;
+       struct cam_ed *device;
+       struct cam_ed_qinfo *qinfo;
+       struct cam_devq *devq;
+       struct cam_periph *periph;
+       struct cam_sim *ccbsim;
+       union ccb *work_ccb;
        cam_status status;
 
        status = xpt_compile_path(&bus_path, NULL, pathid,
@@ -4265,13 +4305,50 @@ xpt_bus_deregister(path_id_t pathid)
        xpt_async(AC_LOST_DEVICE, &bus_path, NULL);
        xpt_async(AC_PATH_DEREGISTERED, &bus_path, NULL);
 
-       /* make sure all responses have been processed */
-       camisr(&cam_bioq);
-       
+       /* The SIM may be gone, so use a dummy SIM for any stray operations. */
+       devq = bus_path.bus->sim->devq;
+       bus_path.bus->sim = &cam_dead_sim;
+
+       /* Execute any pending operations now. */
+       while ((qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->send_queue,
+           CAMQ_HEAD)) != NULL ||
+           (qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->alloc_queue,
+           CAMQ_HEAD)) != NULL) {
+               do {
+                       device = qinfo->device;
+                       work_ccb = cam_ccbq_peek_ccb(&device->ccbq, CAMQ_HEAD);
+                       if (work_ccb != NULL) {
+                               devq->active_dev = device;
+                               cam_ccbq_remove_ccb(&device->ccbq, work_ccb);
+                               cam_ccbq_send_ccb(&device->ccbq, work_ccb);
+                               ccbsim = work_ccb->ccb_h.path->bus->sim;
+                               (*(ccbsim->sim_action))(ccbsim, work_ccb);
+                       }
+
+                       periph = (struct cam_periph *)camq_remove(&device->drvq,
+                           CAMQ_HEAD);
+                       if (periph != NULL)
+                               xpt_schedule(periph, periph->pinfo.priority);
+               } while (work_ccb != NULL || periph != NULL);
+       }
+
+       /* Make sure all completed CCBs are processed. */
+       while (!TAILQ_EMPTY(&cam_bioq)) {
+               camisr(&cam_bioq);
+
+               /* Repeat the async's for the benefit of any new devices. */
+               xpt_async(AC_LOST_DEVICE, &bus_path, NULL);
+               xpt_async(AC_PATH_DEREGISTERED, &bus_path, NULL);
+       }
+
        /* Release the reference count held while registered. */
        xpt_release_bus(bus_path.bus);
        xpt_release_path(&bus_path);
 
+       /* Recheck for more completed CCBs. */
+       while (!TAILQ_EMPTY(&cam_bioq))
+               camisr(&cam_bioq);
+
        return (CAM_REQ_CMP);
 }
 
@@ -4827,6 +4904,9 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
        struct     cam_devq *devq;
        cam_status status;
 
+       if (SIM_DEAD(bus->sim))
+               return (NULL);
+
        /* Make space for us in the device queue on our bus */
        if (bus->sim->devq == NULL)
                return(NULL);
@@ -4944,9 +5024,11 @@ xpt_release_device(struct cam_eb *bus, struct cam_et *target,
                TAILQ_REMOVE(&target->ed_entries, device,links);
                target->generation++;
                xpt_max_ccbs -= device->ccbq.devq_openings;
-               /* Release our slot in the devq */
-               devq = bus->sim->devq;
-               cam_devq_resize(devq, devq->alloc_queue.array_size - 1);
+               if (!SIM_DEAD(bus->sim)) {
+                       /* Release our slot in the devq */
+                       devq = bus->sim->devq;
+                       cam_devq_resize(devq, devq->alloc_queue.array_size - 1);
+               }
                camq_fini(&device->drvq);
                camq_fini(&device->ccbq.queue);
                xpt_release_target(bus, target);
@@ -6885,7 +6967,7 @@ camisr(cam_isrq_t *queue)
 
                        cam_ccbq_ccb_done(&dev->ccbq, (union ccb *)ccb_h);
 
-                       if (ccb_h->path->bus->sim->devq) {
+                       if (!SIM_DEAD(ccb_h->path->bus->sim)) {
                                ccb_h->path->bus->sim->devq->send_active--;
                                ccb_h->path->bus->sim->devq->send_openings++;
                        }
@@ -6932,3 +7014,16 @@ camisr(cam_isrq_t *queue)
        }
        crit_exit();
 }
+
+static void
+dead_sim_action(struct cam_sim *sim, union ccb *ccb)
+{
+
+       ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+       xpt_done(ccb);
+}
+
+static void
+dead_sim_poll(struct cam_sim *sim)
+{
+}