--- a/sys/dev/disk/mmcsd/mmcsd.c +++ b/sys/dev/disk/mmcsd/mmcsd.c @@ -86,6 +86,8 @@ daddr_t eblock, eend; /* Range remaining after the last erase. */ int running; int suspend; + int gone; /* teardown excludes the data path */ + int strategy_refs; /* in-flight strategy()/open() callers */ }; /* bus entry points */ @@ -215,6 +217,7 @@ MMCSD_LOCK(sc); sc->suspend = 0; + sc->gone = 1; /* no new strategy()/open() work */ if (sc->running > 0) { /* kill thread */ sc->running = 0; @@ -223,6 +226,12 @@ while (sc->running != -1) lksleep(sc, &sc->sc_lock, 0, "detach", 0); } + /* + * Drain callers inside mmcsd_strategy()/mmcsd_open() so none is + * parked on sc_lock when we lockuninit() and newbus frees the softc. + */ + while (sc->strategy_refs > 0) + lksleep(sc, &sc->sc_lock, 0, "dtstrd", 0); MMCSD_UNLOCK(sc); /* @@ -230,13 +239,21 @@ * * XXX: Return all queued I/O with ENXIO. Is this correct? */ + MMCSD_LOCK(sc); while ((q_bio = bioq_takefirst(&sc->bio_queue)) != NULL) { + MMCSD_UNLOCK(sc); q_bp = q_bio->bio_buf; q_bp->b_resid = q_bp->b_bcount; q_bp->b_error = ENXIO; q_bp->b_flags |= B_ERROR; biodone(q_bio); + MMCSD_LOCK(sc); } + MMCSD_UNLOCK(sc); + + /* Detach the raw cdev from the softc about to be freed. */ + if (sc->dev_t != NULL) + sc->dev_t->si_drv1 = NULL; /* kill disk */ disk_destroy(&sc->disk); @@ -287,11 +304,23 @@ { struct mmcsd_softc *sc; + int error; + sc = (struct mmcsd_softc *)ap->a_head.a_dev->si_drv1; - if ((ap->a_oflags & FWRITE) && mmc_get_read_only(sc->dev)) - return (EACCES); + if (sc == NULL) + return (ENXIO); + MMCSD_LOCK(sc); + if (sc->gone) { + MMCSD_UNLOCK(sc); + return (ENXIO); + } + sc->strategy_refs++; + error = ((ap->a_oflags & FWRITE) && mmc_get_read_only(sc->dev)) + ? EACCES : 0; + sc->strategy_refs--; + MMCSD_UNLOCK(sc); - return (0); + return (error); } static int @@ -308,12 +337,22 @@ struct buf *bp = bio->bio_buf; sc = (struct mmcsd_softc *)ap->a_head.a_dev->si_drv1; + if (sc == NULL) { + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + bp->b_resid = bp->b_bcount; + biodone(bio); + return (0); + } MMCSD_LOCK(sc); - if (sc->running > 0 || sc->suspend > 0) { + sc->strategy_refs++; + if (!sc->gone && (sc->running > 0 || sc->suspend > 0)) { bioqdisksort(&sc->bio_queue, bio); + sc->strategy_refs--; MMCSD_UNLOCK(sc); wakeup(sc); } else { + sc->strategy_refs--; MMCSD_UNLOCK(sc); bp->b_error = ENXIO; bp->b_flags |= B_ERROR;