Module Name:    src
Committed By:   jdolecek
Date:           Mon Jun 12 21:38:50 UTC 2017

Modified Files:
        src/sys/dev/ic [jdolecek-ncq]: mvsata.c

Log Message:
add support for NCQ on Gen IIe hardware

mvsata_quetag_get()/put() pairs adjusted to work also for non-DMA code paths,
i.e. during probe; replaced wdcintr() call with local routine which uses
currently active command tag, rather than assuming always tag 0 on that
code path

~55MB/s -> ~80MB/s sequential read using fio(1) on a test HDD, quite nice


To generate a diff of this commit:
cvs rdiff -u -r1.35.6.8 -r1.35.6.9 src/sys/dev/ic/mvsata.c

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/ic/mvsata.c
diff -u src/sys/dev/ic/mvsata.c:1.35.6.8 src/sys/dev/ic/mvsata.c:1.35.6.9
--- src/sys/dev/ic/mvsata.c:1.35.6.8	Sat Jun 10 13:25:51 2017
+++ src/sys/dev/ic/mvsata.c	Mon Jun 12 21:38:50 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: mvsata.c,v 1.35.6.8 2017/06/10 13:25:51 jdolecek Exp $	*/
+/*	$NetBSD: mvsata.c,v 1.35.6.9 2017/06/12 21:38:50 jdolecek Exp $	*/
 /*
  * Copyright (c) 2008 KIYOHARA Takashi
  * All rights reserved.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.35.6.8 2017/06/10 13:25:51 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.35.6.9 2017/06/12 21:38:50 jdolecek Exp $");
 
 #include "opt_mvsata.h"
 
@@ -159,6 +159,8 @@ static void mvsata_bdma_start(struct mvs
 #endif
 #endif
 
+static int mvsata_nondma_handle(struct mvsata_port *);
+
 static int mvsata_port_init(struct mvsata_hc *, int);
 static int mvsata_wdc_reg_init(struct mvsata_port *, struct wdc_regs *);
 #ifndef MVSATA_WITHOUTDMA
@@ -289,6 +291,7 @@ mvsata_attach(struct mvsata_softc *sc, s
 		_fix_phy = mvsata_fix_phy_gen2;
 #ifndef MVSATA_WITHOUTDMA
 		edma_setup_crqb = mvsata_edma_setup_crqb_gen2e;
+		sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_NCQ;
 #endif
 		break;
 	}
@@ -401,6 +404,7 @@ mvsata_intr(struct mvsata_hc *mvhc)
 	if (cause & SATAHC_IC_SAINTCOAL)
 		MVSATA_HC_WRITE_4(mvhc, SATAHC_IC, ~SATAHC_IC_SAINTCOAL);
 	cause &= ~SATAHC_IC_SAINTCOAL;
+
 	for (port = 0; port < sc->sc_port; port++) {
 		mvport = mvhc->hc_ports[port];
 
@@ -413,7 +417,7 @@ mvsata_intr(struct mvsata_hc *mvhc)
 		}
 
 		if (cause & SATAHC_IC_SADEVINTERRUPT(port)) {
-			wdcintr(&mvport->port_ata_channel);
+			(void) mvsata_nondma_handle(mvport);
 			MVSATA_HC_WRITE_4(mvhc, SATAHC_IC,
 			    ~SATAHC_IC_SADEVINTERRUPT(port));
 			handled = 1;
@@ -423,6 +427,35 @@ mvsata_intr(struct mvsata_hc *mvhc)
 	return handled;
 }
 
+static int
+mvsata_nondma_handle(struct mvsata_port *mvport)
+{
+	struct ata_channel *chp = &mvport->port_ata_channel;
+	struct ata_xfer *xfer;
+	int ret, quetag;
+
+	/*
+	 * The chip doesn't support several pending non-DMA commands,
+	 * and the ata middle layer never issues several non-NCQ commands,
+	 * so there must be exactly one active command at this moment.
+	 */
+	for (quetag = 0; quetag < MVSATA_EDMAQ_LEN; quetag++) {
+		if ((mvport->port_quetagidx & __BIT(quetag)) == 0)
+			continue;
+
+		break;
+	}
+	KASSERT(quetag < MVSATA_EDMAQ_LEN);
+
+	xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, quetag);
+	chp->ch_flags &= ~ATACH_IRQ_WAIT;
+	KASSERT(xfer->c_intr != NULL);
+	ret = xfer->c_intr(chp, xfer, 1);
+	if (ret == 0) /* irq was not for us, still waiting for irq */
+		chp->ch_flags |= ATACH_IRQ_WAIT;
+	return (ret);
+}
+
 int
 mvsata_error(struct mvsata_port *mvport)
 {
@@ -528,6 +561,7 @@ static int
 mvsata_bio(struct ata_drive_datas *drvp, struct ata_xfer *xfer)
 {
 	struct ata_channel *chp = drvp->chnl_softc;
+	struct mvsata_port *mvport = (struct mvsata_port *)chp;
 	struct atac_softc *atac = chp->ch_atac;
 	struct ata_bio *ata_bio = &xfer->c_bio;
 
@@ -535,6 +569,8 @@ mvsata_bio(struct ata_drive_datas *drvp,
 	    ", bcount=%ld\n", device_xname(atac->atac_dev), chp->ch_channel,
 	    drvp->drive, ata_bio->blkno, ata_bio->bcount));
 
+	mvsata_quetag_get(mvport, xfer->c_slot);
+
 	if (atac->atac_cap & ATAC_CAP_NOIRQ)
 		ata_bio->flags |= ATA_POLL;
 	if (ata_bio->flags & ATA_POLL)
@@ -636,9 +672,7 @@ static int
 mvsata_exec_command(struct ata_drive_datas *drvp, struct ata_xfer *xfer)
 {
 	struct ata_channel *chp = drvp->chnl_softc;
-#ifdef MVSATA_DEBUG
 	struct mvsata_port *mvport = (struct mvsata_port *)chp;
-#endif
 	struct ata_command *ata_c = &xfer->c_ata_c;
 	int rv, s;
 
@@ -649,6 +683,8 @@ mvsata_exec_command(struct ata_drive_dat
 	    drvp->drive, ata_c->bcount, ata_c->r_lba, ata_c->r_count,
 	    ata_c->r_features, ata_c->r_device, ata_c->r_command));
 
+	mvsata_quetag_get(mvport, xfer->c_slot);
+
 	if (ata_c->flags & AT_POLL)
 		xfer->c_flags |= C_POLL;
 	if (ata_c->flags & AT_WAIT)
@@ -747,8 +783,9 @@ mvsata_atapi_scsipi_request(struct scsip
 	struct scsipi_xfer *sc_xfer;
 	struct mvsata_softc *sc = device_private(adapt->adapt_dev);
 	struct atac_softc *atac = &sc->sc_wdcdev.sc_atac;
+	struct ata_channel *chp = atac->atac_channels[chan->chan_channel];
 	struct ata_xfer *xfer;
-	int channel = chan->chan_channel;
+	struct mvsata_port *mvport = (struct mvsata_port *)chp;
 	int drive, s;
 
         switch (req) {
@@ -762,13 +799,15 @@ mvsata_atapi_scsipi_request(struct scsip
 			scsipi_done(sc_xfer);
 			return;
 		}
-		xfer = ata_get_xfer(atac->atac_channels[channel]);
+		xfer = ata_get_xfer(chp);
 		if (xfer == NULL) {
 			sc_xfer->error = XS_RESOURCE_SHORTAGE;
 			scsipi_done(sc_xfer);
 			return;
 		}
 
+		mvsata_quetag_get(mvport, xfer->c_slot);
+
 		if (sc_xfer->xs_control & XS_CTL_POLL)
 			xfer->c_flags |= C_POLL;
 		xfer->c_drive = drive;
@@ -781,7 +820,7 @@ mvsata_atapi_scsipi_request(struct scsip
 		xfer->c_kill_xfer = mvsata_atapi_kill_xfer;
 		xfer->c_dscpoll = 0;
 		s = splbio();
-		ata_exec_xfer(atac->atac_channels[channel], xfer);
+		ata_exec_xfer(chp, xfer);
 #ifdef DIAGNOSTIC
 		if ((sc_xfer->xs_control & XS_CTL_POLL) != 0 &&
 		    (sc_xfer->xs_status & XS_STS_DONE) == 0)
@@ -972,9 +1011,12 @@ mvsata_setup_channel(struct ata_channel 
 			splx(s);
 		}
 
-		if (drvp->drive_flags & (ATA_DRIVE_UDMA | ATA_DRIVE_DMA))
-			if (drvp->drive_type == ATA_DRIVET_ATA)
+		if (drvp->drive_flags & (ATA_DRIVE_UDMA | ATA_DRIVE_DMA)) {
+			if (drvp->drive_flags & ATA_DRIVE_NCQ)
+				edma_mode = ncq;
+			else if (drvp->drive_type == ATA_DRIVET_ATA)
 				edma_mode = dma;
+		}
 	}
 
 	DPRINTF(("EDMA %sactive mode\n", (edma_mode == nodma) ? "not " : ""));
@@ -1406,6 +1448,7 @@ mvsata_bio_kill_xfer(struct ata_channel 
 		panic("mvsata_bio_kill_xfer");
 	}
 	ata_bio->r_error = WDCE_ABRT;
+	mvsata_quetag_put(mvport, xfer->c_slot);
 	(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer);
 }
 
@@ -1432,6 +1475,7 @@ mvsata_bio_done(struct ata_channel *chp,
 	ata_bio->bcount = xfer->c_bcount;
 
 	/* mark controller inactive and free xfer */
+	mvsata_quetag_put(mvport, xfer->c_slot);
 	ata_deactivate_xfer(chp, xfer);
 
 	if (ata_waitdrain_check(chp, drive))
@@ -1731,6 +1775,7 @@ mvsata_wdc_cmd_kill_xfer(struct ata_chan
 		    "mvsata_cmd_kill_xfer: unknown reason %d\n", reason);
 		panic("mvsata_cmd_kill_xfer");
 	}
+	mvsata_quetag_put(mvport, xfer->c_slot);
 	mvsata_wdc_cmd_done_end(chp, xfer);
 }
 
@@ -1793,6 +1838,7 @@ mvsata_wdc_cmd_done(struct ata_channel *
 		}
 	}
 
+	mvsata_quetag_put(mvport, xfer->c_slot);
 	ata_deactivate_xfer(chp, xfer);
 
 	if (ata_c->flags & AT_POLL) {
@@ -2246,6 +2292,7 @@ mvsata_atapi_kill_xfer(struct ata_channe
 		    "mvsata_atapi_kill_xfer: unknown reason %d\n", reason);
 		panic("mvsata_atapi_kill_xfer");
 	}
+	mvsata_quetag_put(mvport, xfer->c_slot);
 	ata_free_xfer(chp, xfer);
 	scsipi_done(sc_xfer);
 }
@@ -2356,6 +2403,7 @@ mvsata_atapi_phase_complete(struct ata_x
 static void
 mvsata_atapi_done(struct ata_channel *chp, struct ata_xfer *xfer)
 {
+	struct mvsata_port *mvport = (struct mvsata_port *)chp;
 	struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
 	int drive = xfer->c_drive;
 
@@ -2364,7 +2412,7 @@ mvsata_atapi_done(struct ata_channel *ch
 	    xfer->c_drive, (u_int)xfer->c_flags));
 
 	/* mark controller inactive and free the command */
-
+	mvsata_quetag_put(mvport, xfer->c_slot);
 	ata_deactivate_xfer(chp, xfer);
 
 	if (ata_waitdrain_check(chp, drive))
@@ -2420,17 +2468,14 @@ mvsata_edma_enqueue(struct mvsata_port *
 	MVSATA_EDMAQ_INC(next);
 	if (next == erqqop) {
 		/* queue full */
-		return EBUSY; /* XXX slot */
+		return EBUSY;
 	}
-	mvsata_quetag_get(mvport, xfer->c_slot);
 	DPRINTFN(2, ("    erqqip=%d, quetag=%d\n", erqqip, xfer->c_slot));
 
 	rv = mvsata_dma_bufload(mvport, xfer->c_slot, databuf, ata_bio->nbytes,
 	    ata_bio->flags);
-	if (rv != 0) {
-		mvsata_quetag_put(mvport, xfer->c_slot);
+	if (rv != 0)
 		return rv;
-	}
 
 	/* setup EDMA Physical Region Descriptors (ePRD) Table Data */
 	data_dmamap = mvport->port_reqtbl[xfer->c_slot].data_dmamap;
@@ -2543,6 +2588,7 @@ mvsata_edma_handle(struct mvsata_port *m
 #endif
 		crpb = mvport->port_crpb + erpqop;
 		quetag = CRPB_CHOSTQUETAG(le16toh(crpb->id));
+
 		xfer = ata_queue_hwslot_to_xfer(chp->ch_queue, quetag);
 
 		bus_dmamap_sync(mvport->port_dmat, mvport->port_eprd_dmamap,
@@ -2562,7 +2608,6 @@ mvsata_edma_handle(struct mvsata_port *m
 			ata_bio->error = ERR_DMA;
 
 		mvsata_dma_bufunload(mvport, quetag, ata_bio->flags);
-		mvsata_quetag_put(mvport, quetag);
 		MVSATA_EDMAQ_INC(erpqop);
 
 #if 1	/* XXXX: flags clears here, because necessary the atabus layer. */
@@ -2676,7 +2721,7 @@ mvsata_edma_rqq_remove(struct mvsata_por
 			    mvport->port_reqtbl[i].eprd_offset,
 			    MVSATA_EPRD_MAX_SIZE, BUS_DMASYNC_POSTWRITE);
 			mvsata_dma_bufunload(mvport, i, xfer->c_bio.flags);
-			mvsata_quetag_put(mvport, i);
+			/* quetag freed by caller later */
 			continue;
 		}
 
@@ -2715,16 +2760,11 @@ mvsata_bdma_init(struct mvsata_port *mvp
 	    device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc,
 	    mvport->port, sc_xfer->datalen, sc_xfer->xs_control));
 
-	mvsata_quetag_get(mvport, xfer->c_slot);
-	DPRINTFN(2, ("    quetag=%d\n", xfer->c_slot));
-
 	rv = mvsata_dma_bufload(mvport, xfer->c_slot, databuf,
 	    sc_xfer->datalen,
 	    sc_xfer->xs_control & XS_CTL_DATA_IN ? ATA_READ : 0);
-	if (rv != 0) {
-		mvsata_quetag_put(mvport, xfer->c_slot);
+	if (rv != 0)
 		return rv;
-	}
 
 	/* setup EDMA Physical Region Descriptors (ePRD) Table Data */
 	data_dmamap = mvport->port_reqtbl[xfer->c_slot].data_dmamap;
@@ -2841,7 +2881,7 @@ mvsata_port_init(struct mvsata_hc *mvhc,
 	chp = &mvport->port_ata_channel;
 	chp->ch_channel = channel;
 	chp->ch_atac = &sc->sc_wdcdev.sc_atac;
-	chp->ch_queue = ata_queue_alloc(1);	// XXX MVSATA_EDMAQ_LEN
+	chp->ch_queue = ata_queue_alloc(32);
 	sc->sc_ata_channels[channel] = chp;
 
 	rv = mvsata_wdc_reg_init(mvport, sc->sc_wdcdev.regs + channel);
@@ -3248,7 +3288,7 @@ mvsata_edma_disable(struct mvsata_port *
 				delay(1000);
 		}
 		if (ms == timeout) {
-			aprint_error("%s:%d:%d: unable to stop EDMA\n",
+			aprint_error("%s:%d:%d: unable to disable EDMA\n",
 			    device_xname(MVSATA_DEV2(mvport)),
 			    mvport->port_hc->hc, mvport->port);
 			return EBUSY;
@@ -3268,7 +3308,7 @@ mvsata_edma_disable(struct mvsata_port *
 				delay(1000);
 		}
 		if (ms == timeout) {
-			aprint_error("%s:%d:%d: unable to stop EDMA\n",
+			aprint_error("%s:%d:%d: unable to re-enable EDMA\n",
 			    device_xname(MVSATA_DEV2(mvport)),
 			    mvport->port_hc->hc, mvport->port);
 			return EBUSY;
@@ -3640,7 +3680,7 @@ mvsata_edma_setup_crqb_gen2e(struct mvsa
 	eprd_addr = mvport->port_eprd_dmamap->dm_segs[0].ds_addr +
 	    mvport->port_reqtbl[xfer->c_slot].eprd_offset;
 	rw = (xfer->c_bio.flags & ATA_READ) ? CRQB_CDIR_READ : CRQB_CDIR_WRITE;
-	ctrlflg = (rw | CRQB_CDEVICEQUETAG(0) | /* XXX slot */
+	ctrlflg = (rw | CRQB_CDEVICEQUETAG(xfer->c_slot) |
 	    CRQB_CPMPORT(xfer->c_drive) |
 	    CRQB_CPRDMODE_EPRD | CRQB_CHOSTQUETAG_GEN2(xfer->c_slot));
 

Reply via email to