Module Name:    src
Committed By:   jdolecek
Date:           Sat Jul 29 12:58:30 UTC 2017

Modified Files:
        src/sys/dev/ata [jdolecek-ncq]: ata.c atavar.h

Log Message:
reserve the highest slot for error recovery, and also have ata_channel
include space for the READ LOG EXT sector, so that it's not necessary
to allocate memory on the error handling path; now ata_read_log_ext_ncq()
will never fail due to resource shortage


To generate a diff of this commit:
cvs rdiff -u -r1.132.8.21 -r1.132.8.22 src/sys/dev/ata/ata.c
cvs rdiff -u -r1.92.8.19 -r1.92.8.20 src/sys/dev/ata/atavar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.132.8.21 src/sys/dev/ata/ata.c:1.132.8.22
--- src/sys/dev/ata/ata.c:1.132.8.21	Sat Jul 22 22:02:21 2017
+++ src/sys/dev/ata/ata.c	Sat Jul 29 12:58:29 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ata.c,v 1.132.8.21 2017/07/22 22:02:21 jdolecek Exp $	*/
+/*	$NetBSD: ata.c,v 1.132.8.22 2017/07/29 12:58:29 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.21 2017/07/22 22:02:21 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.22 2017/07/29 12:58:29 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -191,7 +191,7 @@ ata_queue_reset(struct ata_queue *chq)
 	chq->queue_freeze = 0;
 	chq->queue_active = 0;
 	chq->active_xfers_used = 0;
-	chq->queue_xfers_avail = (1 << chq->queue_openings) - 1;
+	chq->queue_xfers_avail = __BIT(chq->queue_openings) - 1;
 }
 
 struct ata_xfer *
@@ -202,7 +202,8 @@ ata_queue_hwslot_to_xfer(struct ata_chan
 
 	mutex_enter(&chp->ch_lock);
 
-	KASSERT(hwslot < chq->queue_openings);
+	KASSERTMSG(hwslot < chq->queue_openings, "hwslot %d > openings %d",
+	    hwslot, chq->queue_openings);
 	KASSERT((chq->active_xfers_used & __BIT(hwslot)) != 0);
 
 	/* Usually the first entry will be the one */
@@ -1048,20 +1049,9 @@ ata_read_log_ext_ncq(struct ata_drive_da
 	    (drvp->drive_flags & ATA_DRIVE_NCQ) == 0)
 		return EOPNOTSUPP;
 
-	xfer = ata_get_xfer_ext(chp, false, 0);
-	if (xfer == NULL) {
-		ATADEBUG_PRINT(("%s: no xfer\n", __func__),
-		    DEBUG_FUNCS|DEBUG_XFERS);
-		return EAGAIN;
-	}
+	xfer = ata_get_xfer_ext(chp, C_RECOVERY, 0);
 
-	tb = malloc(DEV_BSIZE, M_DEVBUF, M_NOWAIT);
-	if (tb == NULL) {
-		ATADEBUG_PRINT(("%s: memory allocation failed\n", __func__),
-		    DEBUG_FUNCS|DEBUG_XFERS);
-		rv = EAGAIN;
-		goto out;
-	}
+	tb = chp->ch_recovery;
 	memset(tb, 0, DEV_BSIZE);
 
 	/*
@@ -1070,14 +1060,14 @@ ata_read_log_ext_ncq(struct ata_drive_da
 	 * and to make this a little faster. Realistically, it
 	 * should not matter.
 	 */
-	xfer->c_flags |= C_IMMEDIATE;
+	xfer->c_flags |= C_RECOVERY;
 	xfer->c_ata_c.r_command = WDCC_READ_LOG_EXT;
 	xfer->c_ata_c.r_lba = page = WDCC_LOG_PAGE_NCQ;
 	xfer->c_ata_c.r_st_bmask = WDCS_DRDY;
 	xfer->c_ata_c.r_st_pmask = WDCS_DRDY;
 	xfer->c_ata_c.r_count = 1;
 	xfer->c_ata_c.r_device = WDSD_LBA;
-	xfer->c_ata_c.flags = AT_READ | AT_LBA | AT_LBA48 | flags;
+	xfer->c_ata_c.flags = AT_READ | AT_LBA | flags;
 	xfer->c_ata_c.timeout = 1000; /* 1s */
 	xfer->c_ata_c.data = tb;
 	xfer->c_ata_c.bcount = DEV_BSIZE;
@@ -1085,11 +1075,11 @@ ata_read_log_ext_ncq(struct ata_drive_da
 	if ((*atac->atac_bustype_ata->ata_exec_command)(drvp,
 						xfer) != ATACMD_COMPLETE) {
 		rv = EAGAIN;
-		goto out2;
+		goto out;
 	}
 	if (xfer->c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
 		rv = EINVAL;
-		goto out2;
+		goto out;
 	}
 
 	cksum = 0;
@@ -1100,23 +1090,26 @@ ata_read_log_ext_ncq(struct ata_drive_da
 		    "invalid checksum %x for READ LOG EXT page %x\n",
 		    cksum, page);
 		rv = EINVAL;
-		goto out2;
+		goto out;
 	}
 
 	if (tb[0] & WDCC_LOG_NQ) {
 		/* not queued command */
 		rv = EOPNOTSUPP;
-		goto out2;
+		goto out;
 	}
 
 	*slot = tb[0] & 0x1f;
 	*status = tb[2];
 	*err = tb[3];
 
+	KASSERTMSG((*status & WDCS_ERR),
+	    "%s: non-error command slot %d reported by READ LOG EXT page %x: "
+	    "err %x status %x\n",
+	    device_xname(drvp->drv_softc), *slot, page, *err, *status);
+
 	rv = 0;
 
-out2:
-	free(tb, DEV_BSIZE);
 out:
 	ata_free_xfer(chp, xfer);
 	return rv;
@@ -1180,12 +1173,15 @@ ata_exec_xfer(struct ata_channel *chp, s
 
 	mutex_enter(&chp->ch_lock);
 
-	/* insert at the end of command list unless specially requested */
-	if (xfer->c_flags & C_IMMEDIATE)
-		TAILQ_INSERT_HEAD(&chp->ch_queue->queue_xfer, xfer,
+	/*
+	 * Standard commands are added to the end of command list, but
+	 * recovery commands must be run immediatelly.
+	 */
+	if ((xfer->c_flags & C_RECOVERY) == 0)
+		TAILQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer,
 		    c_xferchain);
 	else
-		TAILQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer,
+		TAILQ_INSERT_HEAD(&chp->ch_queue->queue_xfer, xfer,
 		    c_xferchain);
 	ATADEBUG_PRINT(("atastart from ata_exec_xfer, flags 0x%x\n",
 	    chp->ch_flags), DEBUG_XFERS);
@@ -1255,7 +1251,7 @@ atastart(struct ata_channel *chp)
 	if ((xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer)) == NULL)
 		goto out;
 
-	immediate = ISSET(xfer->c_flags, C_IMMEDIATE);
+	immediate = ISSET(xfer->c_flags, C_RECOVERY);
 
 	/* is the queue frozen? */
 	if (__predict_false(!immediate && chq->queue_freeze > 0)) {
@@ -1330,28 +1326,56 @@ out:
 
 /*
  * Does it's own locking, does not require splbio().
- * wait - whether to block waiting for free xfer
+ * flags - whether to block waiting for free xfer
  * openings - limit of openings supported by device, <= 0 means tag not
  *     relevant, and any available xfer can be returned
  */
 struct ata_xfer *
-ata_get_xfer_ext(struct ata_channel *chp, bool wait, int8_t openings)
+ata_get_xfer_ext(struct ata_channel *chp, int flags, uint8_t openings)
 {
 	struct ata_queue *chq = chp->ch_queue;
 	struct ata_xfer *xfer = NULL;
 	uint32_t avail, slot, mask;
 	int error;
 
+	ATADEBUG_PRINT(("%s: channel %d flags %x openings %d\n",
+	    __func__, chp->ch_channel, flags, openings),
+	    DEBUG_XFERS);
+
 	mutex_enter(&chp->ch_lock);
 
-retry:
-	mask = (openings > 0)
-	    ? (__BIT(MIN(ATA_MAX_OPENINGS, openings)) - 1)
-	    : chq->queue_xfers_avail;
+	/*
+	 * When openings is just 1, can't reserve anything for
+	 * recovery. KASSERT() here is to catch code which naively
+	 * relies on C_RECOVERY to work under this condition.
+	 */
+	KASSERT((flags & C_RECOVERY) == 0 || chq->queue_openings > 1);
+
+	if (flags & C_RECOVERY) {
+		mask = UINT32_MAX;
+	} else {
+		if (openings <= 0 || openings > chq->queue_openings)
+			openings = chq->queue_openings;
 
+		if (openings > 1) {
+			mask = __BIT(openings - 1) - 1;
+		} else {
+			mask = UINT32_MAX;
+		}
+	}
+
+retry:
 	avail = ffs32(chq->queue_xfers_avail & mask);
 	if (avail == 0) {
-		if (wait) {
+		/*
+		 * Catch code which tries to get another recovery xfer while
+		 * already holding one (wrong recursion).
+		 */
+		KASSERTMSG((flags & C_RECOVERY) == 0,
+		    "recovery xfer busy openings %d mask %x avail %x",
+		    openings, mask, chq->queue_xfers_avail);
+
+		if (flags & C_WAIT) {
 			chq->queue_flags |= QF_NEED_XFER;
 			error = cv_wait_sig(&chq->queue_busy, &chp->ch_lock);
 			if (error == 0)
@@ -1786,7 +1810,7 @@ ata_print_modes(struct ata_channel *chp)
 
 		if (drvp->drive_flags & ATA_DRIVE_NCQ) {
 			aprint_verbose(", NCQ (%d tags)%s",
-			    chp->ch_queue->queue_openings,
+			    ATA_REAL_OPENINGS(chp->ch_queue->queue_openings),
 			    (drvp->drive_flags & ATA_DRIVE_NCQ_PRIO)
 			    ? " w/PRIO" : "");
 		} else if (drvp->drive_flags & ATA_DRIVE_WFUA)

Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.92.8.19 src/sys/dev/ata/atavar.h:1.92.8.20
--- src/sys/dev/ata/atavar.h:1.92.8.19	Sat Jul 22 22:02:21 2017
+++ src/sys/dev/ata/atavar.h	Sat Jul 29 12:58:29 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: atavar.h,v 1.92.8.19 2017/07/22 22:02:21 jdolecek Exp $	*/
+/*	$NetBSD: atavar.h,v 1.92.8.20 2017/07/29 12:58:29 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -184,7 +184,7 @@ struct ata_xfer {
 #define C_FREE		0x0040		/* call ata_free_xfer() asap */
 #define C_PIOBM		0x0080		/* command uses busmastering PIO */
 #define	C_NCQ		0x0100		/* command is queued  */
-#define C_IMMEDIATE	0x0200		/* execute command without queuing */
+#define C_RECOVERY	0x0200		/* executed as part of recovery */
 #define C_WAITTIMO	0x0400		/* race vs. timeout */
 #define C_CHAOS		0x0800		/* forced error xfer */
 
@@ -197,9 +197,10 @@ struct ata_xfer {
 /*
  * While hw supports up to 32 tags, in practice we must never
  * allow 32 active commands, since that would signal same as
- * channel error. So just limit this to 31.
+ * channel error. We use slot 32 only for error recovery if available.
  */
-#define ATA_MAX_OPENINGS	31
+#define ATA_MAX_OPENINGS	32
+#define ATA_REAL_OPENINGS(op)	((op) > 1 ? (op) - 1 : 1)
 
 /* Per-channel queue of ata_xfers */
 #ifndef ATABUS_PRIVATE
@@ -210,7 +211,7 @@ struct ata_queue {
 #define QF_IDLE_WAIT	0x01    	/* someone wants the controller idle */
 #define QF_NEED_XFER	0x02    	/* someone wants xfer */
 	int8_t queue_active; 		/* number of active transfers */
-	int8_t queue_openings; 			/* max number of active xfers */
+	uint8_t queue_openings;			/* max number of active xfers */
 	TAILQ_HEAD(, ata_xfer) queue_xfer; 	/* queue of pending commands */
 	int queue_freeze; 			/* freeze count for the queue */
 	kcondvar_t queue_busy;			/* c: waiting of xfer */
@@ -424,6 +425,9 @@ struct ata_channel {
 
 	/* Number of sata PMP ports, if any */
 	int ch_satapmp_nports;
+
+	/* Recovery buffer */
+	uint8_t ch_recovery[DEV_BSIZE];
 };
 
 /*
@@ -505,8 +509,8 @@ int	ata_read_log_ext_ncq(struct ata_driv
 #define CMD_ERR   1
 #define CMD_AGAIN 2
 
-struct ata_xfer *ata_get_xfer_ext(struct ata_channel *, bool, int8_t);
-#define ata_get_xfer(chp) ata_get_xfer_ext((chp), true, 0);
+struct ata_xfer *ata_get_xfer_ext(struct ata_channel *, int, uint8_t);
+#define ata_get_xfer(chp) ata_get_xfer_ext((chp), C_WAIT, 0);
 void	ata_free_xfer(struct ata_channel *, struct ata_xfer *);
 void	ata_deactivate_xfer(struct ata_channel *, struct ata_xfer *);
 void	ata_exec_xfer(struct ata_channel *, struct ata_xfer *);

Reply via email to