--- a/sys/dev/raid/ida/ida.c +++ b/sys/dev/raid/ida/ida.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ static void ida_start(struct ida_softc *ida); static void ida_done(struct ida_softc *ida, struct ida_qcb *qcb); static int ida_wait(struct ida_softc *ida, struct ida_qcb *qcb); +static void ida_watchdog(void *arg); DECLARE_DUMMY_MODULE(ida); @@ -69,6 +71,8 @@ { int i; + callout_stop_sync(&ida->watchdog); + for (i = 0; i < ida->num_qcbs; i++) bus_dmamap_destroy(ida->buffer_dmat, ida->qcbs[i].dmamap); @@ -230,6 +234,10 @@ ida_alloc_qcb(ida); /* allocate an initial qcb */ + /* DF-5048: fail parked bios if the controller stops completing. */ + callout_init_mp(&ida->watchdog); + callout_reset(&ida->watchdog, hz, ida_watchdog, ida); + return (0); } @@ -313,6 +321,37 @@ hwqcb->req.sgcount = nsegments; } +/* + * DF-5048: a wedged controller (no completions ever) used to park bios in + * ida->bio_queue forever, hanging every issuer in an unkillable D-state and + * growing the queue without bound. If work has been pending continuously + * for 60 seconds, declare the controller wedged and fail everything queued. + */ +static void +ida_watchdog(void *arg) +{ + struct ida_softc *ida = arg; + struct bio *bio; + + crit_enter(); + if (STAILQ_EMPTY(&ida->qcb_queue) && bioq_first(&ida->bio_queue) == NULL) + ida->stall_ticks = 0; + else if (++ida->stall_ticks >= 60) { + device_printf(ida->dev, + "watchdog: no progress in 60s, failing queued I/O\n"); + while ((bio = bioq_first(&ida->bio_queue)) != NULL) { + bioq_remove(&ida->bio_queue, bio); + bio->bio_buf->b_flags |= B_ERROR; + bio->bio_buf->b_error = ETIMEDOUT; + biodone(bio); + } + ida->stall_ticks = 60; /* keep failing new arrivals */ + } + crit_exit(); + + callout_reset(&ida->watchdog, hz, ida_watchdog, ida); +} + int ida_command(struct ida_softc *ida, int command, void *data, int datasize, int drive, u_int64_t pblkno, int flags) --- a/sys/dev/raid/ida/idavar.h +++ b/sys/dev/raid/ida/idavar.h @@ -158,6 +158,9 @@ STAILQ_HEAD(, ida_qcb) qcb_queue; struct bio_queue_head bio_queue; + struct callout watchdog; /* DF-5048 */ + int stall_ticks; /* DF-5048 */ + struct ida_access cmd; };