Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 6 Mar 2015 00:23:24 +0000 (UTC)
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r279678 - in projects/iosched/sys: cam cam/ata dev/ahci
Message-ID:  <201503060023.t260NOA8076981@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: imp
Date: Fri Mar  6 00:23:23 2015
New Revision: 279678
URL: https://svnweb.freebsd.org/changeset/base/279678

Log:
  Implement "NCQ TRIM" by using Send First Party DMA commands with a
  Dataset Management subcommand and setting the TRIM bit. For drives
  that support this method of TRIM, better latency is expected because
  TRIMs no longer act as a gating event forcing the queue to drain
  before they are executed and holding off new commands while the TRIM
  finishes. We still impose a one-at-a-time limit of the TRIMs, though
  that could be relaxed (though you start running into fairness issues
  if you have a lot of TRIMs competing with other I/Os), so that
  optimization is left for the future.
  
  Add a flag for supporting the NCQ DSM TRIM extention to the ata_cmd
  structure.
  
  Add a flag to adversize that the SIM knows how to do said kludge.
  
  When NCQ DSM Trim fails, fall back to normal DSM Trim. However, treat
  the original trim as advisory and don't reissue it. The stack is too
  fragile at this point in the code to risk it.

Modified:
  projects/iosched/sys/cam/ata/ata_all.h
  projects/iosched/sys/cam/ata/ata_da.c
  projects/iosched/sys/cam/cam_ccb.h
  projects/iosched/sys/dev/ahci/ahci.c

Modified: projects/iosched/sys/cam/ata/ata_all.h
==============================================================================
--- projects/iosched/sys/cam/ata/ata_all.h	Thu Mar  5 21:41:58 2015	(r279677)
+++ projects/iosched/sys/cam/ata/ata_all.h	Fri Mar  6 00:23:23 2015	(r279678)
@@ -46,6 +46,7 @@ struct ata_cmd {
 #define		CAM_ATAIO_CONTROL	0x04	/* Control, not a command */
 #define		CAM_ATAIO_NEEDRESULT	0x08	/* Request requires result. */
 #define		CAM_ATAIO_DMA		0x10	/* DMA command */
+#define		CAM_ATAIO_AUX_HACK	0x20	/* Kludge to make FPDMA DSM TRIM work */
 
 	u_int8_t	command;
 	u_int8_t	features;

Modified: projects/iosched/sys/cam/ata/ata_da.c
==============================================================================
--- projects/iosched/sys/cam/ata/ata_da.c	Thu Mar  5 21:41:58 2015	(r279677)
+++ projects/iosched/sys/cam/ata/ata_da.c	Fri Mar  6 00:23:23 2015	(r279678)
@@ -87,7 +87,8 @@ typedef enum {
 	ADA_FLAG_CAN_CFA        = 0x0400,
 	ADA_FLAG_CAN_POWERMGT   = 0x0800,
 	ADA_FLAG_CAN_DMA48	= 0x1000,
-	ADA_FLAG_DIRTY		= 0x2000
+	ADA_FLAG_DIRTY		= 0x2000,
+	ADA_FLAG_CAN_NCQ_TRIM	= 0x4000	/* CAN_TRIM also set */
 } ada_flags;
 
 typedef enum {
@@ -1018,10 +1019,22 @@ adaasync(void *callback_arg, u_int32_t c
 		else
 			softc->flags &= ~ADA_FLAG_CAN_NCQ;
 		if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
-		    (cgd.inq_flags & SID_DMA))
+		    (cgd.inq_flags & SID_DMA)) {
 			softc->flags |= ADA_FLAG_CAN_TRIM;
-		else
-			softc->flags &= ~ADA_FLAG_CAN_TRIM;
+			/*
+			 * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do
+			 * NCQ trims, if we support trims at all. We also need support from
+			 * the sim do do things properly. Perhaps we should look at log 13
+			 * dword 0 bit 0 and dword 1 bit 0 are set too...
+			 */
+			if (/* (cpi.hba_misc & PIM_NCQ_KLUDGE) != 0 && */ /* Don't know how to do this here */
+			    (cgd.ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 &&
+			    (softc->flags & ADA_FLAG_CAN_TRIM) != 0)
+				softc->flags |= ADA_FLAG_CAN_NCQ_TRIM;
+			else
+				softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM;
+		} else
+			softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM);
 
 		cam_periph_async(periph, code, path, arg);
 		break;
@@ -1293,6 +1306,17 @@ adaregister(struct cam_periph *periph, v
 		softc->disk->d_delmaxsize = maxio;
 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0)
 		softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
+	/*
+	 * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do
+	 * NCQ trims, if we support trims at all. We also need support from
+	 * the sim do do things properly. Perhaps we should look at log 13
+	 * dword 0 bit 0 and dword 1 bit 0 are set too...
+	 */
+	if ((cpi.hba_misc & PIM_NCQ_KLUDGE) != 0 &&
+	    (cgd->ident_data.satacapabilities2 &
+		ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 &&
+	    (softc->flags & ADA_FLAG_CAN_TRIM) != 0)
+		softc->flags |= ADA_FLAG_CAN_NCQ_TRIM;
 	strlcpy(softc->disk->d_descr, cgd->ident_data.model,
 	    MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
 	strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
@@ -1410,10 +1434,9 @@ adaregister(struct cam_periph *periph, v
 	return(CAM_REQ_CMP);
 }
 
-static void
-ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
+static int
+ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req)
 {
-	struct trim_request *req = &softc->trim_req;
 	uint64_t lastlba = (uint64_t)-1;
 	int c, lastcount = 0, off, ranges = 0;
 
@@ -1466,6 +1489,17 @@ ada_dsmtrim(struct ada_softc *softc, str
 		    (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX)
 			break;
 	} while (1);
+
+	return (ranges);
+}
+
+static void
+ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
+{
+	struct trim_request *req = &softc->trim_req;
+	int ranges;
+
+	ranges = ada_dsmtrim_req_create(softc, bp, req);
 	cam_fill_ataio(ataio,
 	    ada_retry_count,
 	    adadone,
@@ -1481,6 +1515,30 @@ ada_dsmtrim(struct ada_softc *softc, str
 }
 
 static void
+ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
+{
+	struct trim_request *req = &softc->trim_req;
+	int ranges;
+
+	ranges = ada_dsmtrim_req_create(softc, bp, req);
+	cam_fill_ataio(ataio,
+	    ada_retry_count,
+	    adadone,
+	    CAM_DIR_OUT,
+	    0,
+	    req->data,
+	    ((ranges + ATA_DSM_BLK_RANGES - 1) /
+	    ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
+	    ada_default_timeout * 1000);
+	ata_ncq_cmd(ataio,
+	    ATA_SEND_FPDMA_QUEUED,
+	    0,
+	    (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES);
+	ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM;
+	ataio->cmd.flags |= CAM_ATAIO_AUX_HACK;
+}
+
+static void
 ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
 {
 	struct trim_request *req = &softc->trim_req;
@@ -1523,7 +1581,9 @@ adastart(struct cam_periph *periph, unio
 		/* Run TRIM if not running yet. */
 		if (!softc->trim_running &&
 		    (bp = bioq_first(&softc->trim_queue)) != 0) {
-			if (softc->flags & ADA_FLAG_CAN_TRIM) {
+			if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) {
+				ada_ncq_dsmtrim(softc, bp, ataio);
+			} else if (softc->flags & ADA_FLAG_CAN_TRIM) {
 				ada_dsmtrim(softc, bp, ataio);
 			} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
 			    !(softc->flags & ADA_FLAG_CAN_48BIT)) {
@@ -1749,6 +1809,7 @@ adadone(struct cam_periph *periph, union
 		int error;
 
 		cam_periph_lock(periph);
+		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
 			error = adaerror(done_ccb, 0, 0);
 			if (error == ERESTART) {
@@ -1762,12 +1823,24 @@ adadone(struct cam_periph *periph, union
 						 /*reduction*/0,
 						 /*timeout*/0,
 						 /*getcount_only*/0);
+			/*
+			 * If we get an error on an NCQ DSM TRIM, fall back
+			 * to a non-NCQ DSM TRIM forever. Please note that if
+			 * CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too.
+			 * However, for this one trim, we treat it as advisory
+			 * and return success up the stack.
+			 */
+			if (state == ADA_CCB_TRIM &&
+			    error != 0 &&
+			    (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) {
+				softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM;
+				error = 0;
+			}
 		} else {
 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
 				panic("REQ_CMP with QFRZN");
 			error = 0;
 		}
-		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
 		bp->bio_error = error;
 		if (error != 0) {
 			bp->bio_resid = bp->bio_bcount;

Modified: projects/iosched/sys/cam/cam_ccb.h
==============================================================================
--- projects/iosched/sys/cam/cam_ccb.h	Thu Mar  5 21:41:58 2015	(r279677)
+++ projects/iosched/sys/cam/cam_ccb.h	Fri Mar  6 00:23:23 2015	(r279678)
@@ -573,6 +573,7 @@ typedef enum {
 } pi_tmflag;
 
 typedef enum {
+	PIM_NCQ_KLUDGE  = 0x200, /* Supports the sata ncq trim kludge */
 	PIM_EXTLUNS	= 0x100,/* 64bit extended LUNs supported */
 	PIM_SCANHILO	= 0x80,	/* Bus scans from high ID to low ID */
 	PIM_NOREMOVE	= 0x40,	/* Removeable devices not included in scan */

Modified: projects/iosched/sys/dev/ahci/ahci.c
==============================================================================
--- projects/iosched/sys/dev/ahci/ahci.c	Thu Mar  5 21:41:58 2015	(r279677)
+++ projects/iosched/sys/dev/ahci/ahci.c	Fri Mar  6 00:23:23 2015	(r279678)
@@ -2361,6 +2361,9 @@ ahci_setup_fis(struct ahci_channel *ch, 
 			fis[13] = ccb->ataio.cmd.sector_count_exp;
 		}
 		fis[15] = ATA_A_4BIT;
+		/* Gross and vile hack -- makes ncq trim work w/o changing ataio size */
+		if (ccb->ataio.cmd.flags & CAM_ATAIO_AUX_HACK)
+			fis[16] = 1;
 	} else {
 		fis[15] = ccb->ataio.cmd.control;
 	}
@@ -2618,7 +2621,7 @@ ahciaction(struct cam_sim *sim, union cc
 		if (ch->caps & AHCI_CAP_SPM)
 			cpi->hba_inquiry |= PI_SATAPM;
 		cpi->target_sprt = 0;
-		cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED;
+		cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED | PIM_NCQ_KLUDGE;
 		cpi->hba_eng_cnt = 0;
 		if (ch->caps & AHCI_CAP_SPM)
 			cpi->max_target = 15;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201503060023.t260NOA8076981>