Index: cam_xpt.c =================================================================== --- cam_xpt.c (revision 290391) +++ cam_xpt.c (working copy) @@ -2901,7 +2901,24 @@ call_sim: */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; callout_stop(&dev->callout); + /* + * If CAM_DEV_REL_TIMEOUT_PENDING is still + * set, then the callout is still + * cancelable, since we hold the callout + * mutex and the callout function clears the + * flag before it ever unlocks/relocks the + * mutex. Since we already acquired a + * device ref for the callout we just + * stopped, we don't need another ref. + */ } else { + /* + * Since the callout gets a pointer to the + * device, acquire a reference to make sure + * the device can't go away until the + * callout finishes or is stopped. + */ + xpt_acquire_device(dev); start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } @@ -4308,8 +4325,10 @@ xpt_release_devq_timeout(void *arg) CAM_DEBUG_DEV(dev, CAM_DEBUG_TRACE, ("xpt_release_devq_timeout\n")); devq = dev->sim->devq; mtx_assert(&devq->send_mtx, MA_OWNED); + dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING; if (xpt_release_devq_device(dev, /*count*/1, /*run_queue*/TRUE)) xpt_run_devq(devq); + xpt_release_device(dev); } void @@ -4345,20 +4364,11 @@ xpt_release_devq_device(struct cam_ed *dev, u_int } dev->ccbq.queue.qfrozen_cnt -= count; if (dev->ccbq.queue.qfrozen_cnt == 0) { + KASSERT((dev->flags & CAM_DEV_REL_ON_COMPLETE) == 0, + ("released the queue while still waiting for completion")); + KASSERT((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) == 0, + ("released the queue while still having timeout pending")); /* - * No longer need to wait for a successful - * command completion. - */ - dev->flags &= ~CAM_DEV_REL_ON_COMPLETE; - /* - * Remove any timeouts that might be scheduled - * to release this queue. - */ - if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { - callout_stop(&dev->callout); - dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING; - } - /* * Now that we are unfrozen schedule the * device so any pending transactions are * run. @@ -4745,10 +4755,9 @@ xpt_release_device(struct cam_ed *device) ("destroying device, but periphs list is not empty")); KASSERT(device->devq_entry.index == CAM_UNQUEUED_INDEX, ("destroying device while still queued for ccbs")); + KASSERT((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) == 0, + ("destroying device while still having timeout pending")); - if ((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) - callout_stop(&device->callout); - xpt_release_target(device->target); cam_ccbq_fini(&device->ccbq);