Index: sys/cam/cam_sim.h =================================================================== --- sys/cam/cam_sim.h +++ sys/cam/cam_sim.h @@ -102,8 +102,9 @@ int max_tagged_dev_openings; int max_dev_openings; u_int32_t flags; +#define CAM_SIM_FREE_DEVQ 0x1 struct cam_devq *devq; /* Device Queue to use for this SIM */ - int refcount; /* References to the SIM. */ + volatile u_int refcount; /* References to the SIM. */ device_t sim_dev; /* For attached peripherals. */ }; Index: sys/cam/cam_sim.c =================================================================== --- sys/cam/cam_sim.c +++ sys/cam/cam_sim.c @@ -37,20 +37,19 @@ #include #include #include +#include #include #include #include #include #include +#include #define CAM_PATH_ANY (u_int32_t)-1 static MALLOC_DEFINE(M_CAMSIM, "CAM SIM", "CAM SIM buffers"); -static struct mtx cam_sim_free_mtx; -MTX_SYSINIT(cam_sim_free_init, &cam_sim_free_mtx, "CAM SIM free lock", MTX_DEF); - struct cam_devq * cam_simq_alloc(u_int32_t max_sim_transactions) { @@ -122,7 +121,7 @@ sim->max_tagged_dev_openings = max_tagged_dev_transactions; sim->max_dev_openings = max_dev_transactions; sim->flags = 0; - sim->refcount = 1; + refcount_init(&sim->refcount, 1); sim->devq = queue; sim->mtx = mtx; return (sim); @@ -155,72 +154,79 @@ return (sim); } +static void +cam_sim_final_free(struct cam_sim *sim) +{ + if (sim->flags & CAM_SIM_FREE_DEVQ) + cam_simq_free(sim->devq); + free(sim, M_CAMSIM); +} + +static void +cam_sim_action_gone(struct cam_sim *sim, union ccb *ccb) +{ + ccb->ccb_h.status = CAM_DEV_NOT_THERE; + xpt_done(ccb); +} + +static void +cam_sim_poll_gone(struct cam_sim *sim) +{ +} + +/** + * @brief frees up the sim + * + * Frees up the CAM @c sim and optionally the devq. After this call returns, no + * further @c sim_action or @c sim_poll requests will make it to the driver. The + * driver is responsible for cleaning up all requests submitted via sim_action + * that were recieved prior to calling this function (though it is unspecified + * when the sim must do this by). + * + * This function asserts that the mtx associated with this sim, if any, is locked + * when this function is called. It will remain locked. Once this function returns, + * CAM will cease using that mutex. + * + * If there are no more references to this sim, then it will be freed prior to + * returning. If there are references still, the free will be deferred util + * those references are released. All new calls to @c sim_action or @c sim_poll + * will return immediately with an error (without calling the registered @c + * sim_action or @c sim_poll). The @c devq will be freed once all references + * have been released. + * + * @param sim The sim to free + * @param free_devq Free the devq associated with the sim at creation. + */ void cam_sim_free(struct cam_sim *sim, int free_devq) { - struct mtx *mtx; - int error; - - if (sim->mtx == NULL) { - mtx = &cam_sim_free_mtx; - mtx_lock(mtx); - } else { - mtx = sim->mtx; - mtx_assert(mtx, MA_OWNED); - } - KASSERT(sim->refcount >= 1, ("sim->refcount >= 1")); - sim->refcount--; - if (sim->refcount > 0) { - error = msleep(sim, mtx, PRIBIO, "simfree", 0); - KASSERT(error == 0, ("invalid error value for msleep(9)")); - } - KASSERT(sim->refcount == 0, ("sim->refcount == 0")); - if (mtx == &cam_sim_free_mtx) /* sim->mtx == NULL */ - mtx_unlock(mtx); + if (sim->mtx != NULL) + mtx_assert(sim->mtx, MA_OWNED); if (free_devq) - cam_simq_free(sim->devq); - free(sim, M_CAMSIM); + sim->flags |= CAM_SIM_FREE_DEVQ; + sim->sim_action = cam_sim_action_gone; + sim->sim_poll = cam_sim_poll_gone; + sim->mtx = NULL; + cam_sim_release(sim); } void cam_sim_release(struct cam_sim *sim) { - struct mtx *mtx; - - if (sim->mtx == NULL) - mtx = &cam_sim_free_mtx; - else if (!mtx_owned(sim->mtx)) - mtx = sim->mtx; - else - mtx = NULL; /* We hold the lock. */ - if (mtx) - mtx_lock(mtx); - KASSERT(sim->refcount >= 1, ("sim->refcount >= 1")); - sim->refcount--; - if (sim->refcount == 0) - wakeup(sim); - if (mtx) - mtx_unlock(mtx); + if (refcount_release(&sim->refcount)) + cam_sim_final_free(sim); } void cam_sim_hold(struct cam_sim *sim) { - struct mtx *mtx; - - if (sim->mtx == NULL) - mtx = &cam_sim_free_mtx; - else if (!mtx_owned(sim->mtx)) - mtx = sim->mtx; - else - mtx = NULL; /* We hold the lock. */ - if (mtx) - mtx_lock(mtx); - KASSERT(sim->refcount >= 1, ("sim->refcount >= 1")); - sim->refcount++; - if (mtx) - mtx_unlock(mtx); +#ifdef INVARIANTS + u_int old = +#endif + refcount_acquire(&sim->refcount); + KASSERT(old > 0, ("cam_sim_hold: %s%d tried to hold refcount 0", + sim->sim_name, sim->unit_number)); } void