⬢ DragonFlyBSD Kernel Audit
DF-5048 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/raid/ida/ida.c
+++ b/sys/dev/raid/ida/ida.c
@@ -48,6 +48,7 @@
 #include <sys/disk.h>
 #include <sys/rman.h>
 #include <sys/buf2.h>
+#include <sys/callout.h>
 #include <sys/thread2.h>
 
 #include <machine/clock.h>
@@ -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;
 };