DF-5071 / fix.diff
--- a/sys/dev/raid/amr/amr_disk.c +++ b/sys/dev/raid/amr/amr_disk.c @@ -176,6 +176,15 @@ bp->b_error = EINVAL; goto bad; } + + /* controller shutting down? fail fast instead of parking the bio on + * amr_bioq, which is never drained once AMR_STATE_SHUTDOWN is set + * (amrd_open already refuses new opens in that state). */ + if (sc->amrd_controller->amr_state & AMR_STATE_SHUTDOWN) { + bp->b_error = ENXIO; + goto bad; + } + bio->bio_driver_info = sc; devstat_start_transaction(&sc->amrd_stats); @@ -213,6 +222,31 @@ biodone(bio); } +/* + * Fail every bio parked on the controller queue. Must be called once + * AMR_STATE_SHUTDOWN is set: nothing will ever complete them (amr has no + * timeout/watchdog), and their issuers would otherwise sleep in biowait + * forever. Caller holds amr_list_lock. + */ +void +amrd_fail_queued_bios(struct amr_softc *sc) +{ + struct bio *bio; + struct buf *bp; + + for (;;) { + bio = bioq_first(&sc->amr_bioq); + if (bio == NULL) + break; + bioq_remove(&sc->amr_bioq, bio); + bp = bio->bio_buf; + bp->b_flags |= B_ERROR; + bp->b_error = ENXIO; + bp->b_resid = bp->b_bcount; + biodone(bio); + } +} + static int amrd_probe(device_t dev) { --- a/sys/dev/raid/amr/amrvar.h +++ b/sys/dev/raid/amr/amrvar.h @@ -299,6 +299,7 @@ * Interface between driver core and disk driver (should be using a bus?) */ extern int amr_submit_bio(struct amr_softc *sc, struct bio *bio); +extern void amrd_fail_queued_bios(struct amr_softc *sc); extern int amr_dump_blocks(struct amr_softc *sc, int unit, u_int32_t lba, void *data, int blks); extern void amrd_intr(void *data); --- a/sys/dev/raid/amr/amr.c +++ b/sys/dev/raid/amr/amr.c @@ -465,6 +465,11 @@ /* mark ourselves as in-shutdown */ sc->amr_state |= AMR_STATE_SHUTDOWN; + /* fail bios already queued: nothing will complete them now */ + lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE); + amrd_fail_queued_bios(sc); + lockmgr(&sc->amr_list_lock, LK_RELEASE); + /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); kprintf("%s\n", amr_flush(sc) ? "failed" : "done"); --- a/sys/dev/raid/amr/amr_pci.c +++ b/sys/dev/raid/amr/amr_pci.c @@ -394,6 +394,12 @@ /* mark ourselves as in-shutdown */ sc->amr_state |= AMR_STATE_SHUTDOWN; + /* fail bios already queued: nothing will complete them now + * (the "bioq is empty" assumption in the comment above does not hold: + * only the control device sets AMR_STATE_OPEN, amrd_open sets nothing) */ + lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE); + amrd_fail_queued_bios(sc); + lockmgr(&sc->amr_list_lock, LK_RELEASE); /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); |