Index: sys/cam/cam_xpt.c =================================================================== --- sys/cam/cam_xpt.c +++ sys/cam/cam_xpt.c @@ -5374,8 +5374,8 @@ static void xpt_done_process(struct ccb_hdr *ccb_h) { - struct cam_sim *sim; - struct cam_devq *devq; + struct cam_sim *sim = NULL; + struct cam_devq *devq = NULL; struct mtx *mtx = NULL; #if defined(BUF_TRACKING) || defined(FULL_BUF_TRACKING) @@ -5418,9 +5418,15 @@ mtx_unlock(&xsoftc.xpt_highpower_lock); } - sim = ccb_h->path->bus->sim; + /* + * Insulate against a race where the periph is destroyed + * but CCBs are still not all processed. + */ + if (ccb_h->path->bus) + sim = ccb_h->path->bus->sim; if (ccb_h->status & CAM_RELEASE_SIMQ) { + KASSERT(sim, "sim missing for CAM_RELEASE_SIMQ request"); xpt_release_simq(sim, /*run_queue*/FALSE); ccb_h->status &= ~CAM_RELEASE_SIMQ; } @@ -5431,10 +5437,13 @@ ccb_h->status &= ~CAM_DEV_QFRZN; } - devq = sim->devq; if ((ccb_h->func_code & XPT_FC_USER_CCB) == 0) { struct cam_ed *dev = ccb_h->path->device; + if (sim) + devq = sim->devq; + KASSERT(devq, "sim missing for XPT_FC_USER_CCB request"); + mtx_lock(&devq->send_mtx); devq->send_active--; devq->send_openings++; Index: sys/cam/scsi/scsi_da.c =================================================================== --- sys/cam/scsi/scsi_da.c +++ sys/cam/scsi/scsi_da.c @@ -51,6 +51,7 @@ #include #include #include +#include #endif /* _KERNEL */ #ifndef _KERNEL @@ -291,6 +292,18 @@ #define DA_WORK_TUR (1 << 16) +typedef enum { + DA_REF_OPEN, + DA_REF_OPEN_HOLD, + DA_REF_CLOSE_HOLD, + DA_REF_PROBE_HOLD, + DA_REF_TUR, + DA_REF_GEOM, + DA_REF_SYSCTL, + DA_REF_REPROBE, + DA_REF_MAX /* KEEP LAST */ +} da_ref_token; + struct da_softc { struct cam_iosched_softc *cam_iosched; struct bio_queue_head delete_run_queue; @@ -335,6 +348,7 @@ uint8_t unmap_buf[UNMAP_BUF_SIZE]; struct scsi_read_capacity_data_long rcaplong; struct callout mediapoll_c; + int ref_flags[DA_REF_MAX]; #ifdef CAM_IO_STATS struct sysctl_ctx_list sysctl_stats_ctx; struct sysctl_oid *sysctl_stats_tree; @@ -1469,6 +1483,80 @@ static MALLOC_DEFINE(M_SCSIDA, "scsi_da", "scsi_da buffers"); +static inline int +da_periph_hold(struct cam_periph *periph, int priority, da_ref_token token) +{ + int err = cam_periph_hold(periph, priority); + + if (err == 0) { + int cnt; + struct da_softc *softc = periph->softc; + + cnt = atomic_fetchadd_int(&softc->ref_flags[token], 1); + if (cnt != 0) + panic("Re-holding for reason %d, cnt = %d", token, cnt); + } + return (err); +} + +static inline void +da_periph_unhold(struct cam_periph *periph, da_ref_token token) +{ + int cnt; + struct da_softc *softc = periph->softc; + + cam_periph_unhold(periph); + cnt = atomic_fetchadd_int(&softc->ref_flags[token], -1); + if (cnt != 1) + panic("Unholding %d with cnt = %d", token, cnt); +} + +static inline int +da_periph_acquire(struct cam_periph *periph, da_ref_token token) +{ + int err = cam_periph_acquire(periph); + + if (err == 0) { + int cnt; + struct da_softc *softc = periph->softc; + + cnt = atomic_fetchadd_int(&softc->ref_flags[token], 1); + if (cnt != 0) + panic("Re-refing for reason %d, cnt = %d", token, cnt); + } + return (err); +} + +static inline void +da_periph_release(struct cam_periph *periph, da_ref_token token) +{ + int cnt; + struct da_softc *softc = periph->softc; + + cam_periph_release(periph); + cnt = atomic_fetchadd_int(&softc->ref_flags[token], -1); + if (cnt != 1) + panic("Unholding %d with cnt = %d", token, cnt); +} + +static inline void +da_periph_release_locked(struct cam_periph *periph, da_ref_token token) +{ + int cnt; + struct da_softc *softc = periph->softc; + + cam_periph_release_locked(periph); + cnt = atomic_fetchadd_int(&softc->ref_flags[token], -1); + if (cnt != 1) + panic("Unholding %d with cnt = %d", token, cnt); +} + +#define cam_periph_hold POISON +#define cam_periph_unhold POISON +#define cam_periph_acquire POISON +#define cam_periph_release POISON +#define cam_periph_release_locked POISON + static int daopen(struct disk *dp) { @@ -1477,14 +1565,14 @@ int error; periph = (struct cam_periph *)dp->d_drv1; - if (cam_periph_acquire(periph) != CAM_REQ_CMP) { + if (da_periph_acquire(periph, DA_REF_OPEN) != CAM_REQ_CMP) { return (ENXIO); } cam_periph_lock(periph); - if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) { + if ((error = da_periph_hold(periph, PRIBIO|PCATCH, DA_REF_OPEN_HOLD)) != 0) { cam_periph_unlock(periph); - cam_periph_release(periph); + da_periph_release(periph, DA_REF_OPEN); return (error); } @@ -1512,11 +1600,11 @@ softc->flags |= DA_FLAG_OPEN; } - cam_periph_unhold(periph); + da_periph_unhold(periph, DA_REF_OPEN_HOLD); cam_periph_unlock(periph); if (error != 0) - cam_periph_release(periph); + da_periph_release(periph, DA_REF_OPEN); return (error); } @@ -1534,7 +1622,7 @@ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, ("daclose\n")); - if (cam_periph_hold(periph, PRIBIO) == 0) { + if (da_periph_hold(periph, PRIBIO, DA_REF_CLOSE_HOLD) == 0) { /* Flush disk cache. */ if ((softc->flags & DA_FLAG_DIRTY) != 0 && @@ -1557,7 +1645,7 @@ (softc->quirks & DA_Q_NO_PREVENT) == 0) daprevent(periph, PR_ALLOW); - cam_periph_unhold(periph); + da_periph_unhold(periph, DA_REF_CLOSE_HOLD); } /* @@ -1572,7 +1660,7 @@ while (softc->refcount != 0) cam_periph_sleep(periph, &softc->refcount, PRIBIO, "daclose", 1); cam_periph_unlock(periph); - cam_periph_release(periph); + da_periph_release(periph, DA_REF_OPEN); return (0); } @@ -1750,7 +1838,7 @@ struct cam_periph *periph; periph = (struct cam_periph *)dp->d_drv1; - cam_periph_release(periph); + da_periph_release(periph, DA_REF_GEOM); } static void @@ -1910,7 +1998,7 @@ case AC_SCSI_AEN: softc = (struct da_softc *)periph->softc; if (!cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { - if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + if (da_periph_acquire(periph, DA_REF_TUR) == CAM_REQ_CMP) { cam_iosched_set_work_flags(softc->cam_iosched, DA_WORK_TUR); daschedule(periph); } @@ -1955,7 +2043,7 @@ * periph was held for us when this task was enqueued */ if (periph->flags & CAM_PERIPH_INVALID) { - cam_periph_release(periph); + da_periph_release(periph, DA_REF_SYSCTL); return; } @@ -1970,7 +2058,7 @@ CTLFLAG_RD, 0, tmpstr, "device_index"); if (softc->sysctl_tree == NULL) { printf("dasysctlinit: unable to allocate sysctl tree\n"); - cam_periph_release(periph); + da_periph_release(periph, DA_REF_SYSCTL); return; } @@ -2052,7 +2140,7 @@ xpt_action((union ccb *)&cts); cam_periph_unlock(periph); if (cts.ccb_h.status != CAM_REQ_CMP) { - cam_periph_release(periph); + da_periph_release(periph, DA_REF_SYSCTL); return; } if (cts.protocol == PROTO_SCSI && cts.transport == XPORT_FC) { @@ -2103,7 +2191,7 @@ cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx, softc->sysctl_tree); - cam_periph_release(periph); + da_periph_release(periph, DA_REF_SYSCTL); } static int @@ -2269,9 +2357,9 @@ wakeup(&softc->disk->d_mediasize); if ((softc->flags & DA_FLAG_ANNOUNCED) == 0) { softc->flags |= DA_FLAG_ANNOUNCED; - cam_periph_unhold(periph); + da_periph_unhold(periph, DA_REF_PROBE_HOLD); } else - cam_periph_release_locked(periph); + da_periph_release_locked(periph, DA_REF_REPROBE); } static void @@ -2484,8 +2572,10 @@ * Take an exclusive refcount on the periph while dastart is called * to finish the probe. The reference will be dropped in dadone at * the end of probe. + * + * XXX if cam_periph_hold returns an error, we don't hold a refcount. */ - (void)cam_periph_hold(periph, PRIBIO); + (void)da_periph_hold(periph, PRIBIO, DA_REF_PROBE_HOLD); /* * Schedule a periodic event to occasionally send an @@ -2579,7 +2669,7 @@ * We'll release this reference once GEOM calls us back (via * dadiskgonecb()) telling us that our provider has been freed. */ - if (cam_periph_acquire(periph) != CAM_REQ_CMP) { + if (da_periph_acquire(periph, DA_REF_GEOM) != CAM_REQ_CMP) { xpt_print(periph->path, "%s: lost periph during " "registration!\n", __func__); cam_periph_lock(periph); @@ -2965,7 +3055,7 @@ if (cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { cam_iosched_clr_work_flags(softc->cam_iosched, DA_WORK_TUR); - cam_periph_release_locked(periph); + da_periph_release_locked(periph, DA_REF_TUR); } if ((bp->bio_flags & BIO_ORDERED) != 0 || @@ -4547,7 +4637,7 @@ * we have successfully attached. */ /* increase the refcount */ - if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + if (da_periph_acquire(periph, DA_REF_SYSCTL) == CAM_REQ_CMP) { taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task); @@ -5392,7 +5482,7 @@ /*getcount_only*/0); } xpt_release_ccb(done_ccb); - cam_periph_release_locked(periph); + da_periph_release_locked(periph, DA_REF_TUR); return; } default: @@ -5413,7 +5503,7 @@ if (softc->state != DA_STATE_NORMAL) return; - status = cam_periph_acquire(periph); + status = da_periph_acquire(periph, DA_REF_REPROBE); KASSERT(status == CAM_REQ_CMP, ("dareprobe: cam_periph_acquire failed")); @@ -5513,7 +5603,7 @@ if (!cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR) && LIST_EMPTY(&softc->pending_ccbs)) { - if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + if (da_periph_acquire(periph, DA_REF_TUR) == CAM_REQ_CMP) { cam_iosched_set_work_flags(softc->cam_iosched, DA_WORK_TUR); daschedule(periph); }