Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 Apr 2013 17:08:08 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r249944 - in user/adrian/net80211_tx/sys: cam/ata cam/scsi geom sys
Message-ID:  <201304261708.r3QH88wa066942@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Fri Apr 26 17:08:07 2013
New Revision: 249944
URL: http://svnweb.freebsd.org/changeset/base/249944

Log:
  Merge from HEAD.

Modified:
  user/adrian/net80211_tx/sys/cam/ata/ata_da.c
  user/adrian/net80211_tx/sys/cam/scsi/scsi_all.c
  user/adrian/net80211_tx/sys/cam/scsi/scsi_all.h
  user/adrian/net80211_tx/sys/cam/scsi/scsi_da.c
  user/adrian/net80211_tx/sys/cam/scsi/scsi_xpt.c
  user/adrian/net80211_tx/sys/geom/geom_disk.c
  user/adrian/net80211_tx/sys/geom/geom_disk.h
  user/adrian/net80211_tx/sys/sys/param.h
Directory Properties:
  user/adrian/net80211_tx/   (props changed)
  user/adrian/net80211_tx/sys/   (props changed)

Modified: user/adrian/net80211_tx/sys/cam/ata/ata_da.c
==============================================================================
--- user/adrian/net80211_tx/sys/cam/ata/ata_da.c	Fri Apr 26 17:06:36 2013	(r249943)
+++ user/adrian/net80211_tx/sys/cam/ata/ata_da.c	Fri Apr 26 17:08:07 2013	(r249944)
@@ -117,10 +117,10 @@ struct disk_params {
 };
 
 #define TRIM_MAX_BLOCKS	8
-#define TRIM_MAX_RANGES	(TRIM_MAX_BLOCKS * 64)
+#define TRIM_MAX_RANGES	(TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES)
 #define TRIM_MAX_BIOS	(TRIM_MAX_RANGES * 4)
 struct trim_request {
-	uint8_t		data[TRIM_MAX_RANGES * 8];
+	uint8_t		data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE];
 	struct bio	*bps[TRIM_MAX_BIOS];
 };
 
@@ -1109,8 +1109,8 @@ adaregister(struct cam_periph *periph, v
 		softc->trim_max_ranges = TRIM_MAX_RANGES;
 		if (cgd->ident_data.max_dsm_blocks != 0) {
 			softc->trim_max_ranges =
-			    min(cgd->ident_data.max_dsm_blocks * 64,
-				softc->trim_max_ranges);
+			    min(cgd->ident_data.max_dsm_blocks *
+				ATA_DSM_BLK_RANGES, softc->trim_max_ranges);
 		}
 	}
 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
@@ -1155,7 +1155,11 @@ adaregister(struct cam_periph *periph, v
 	snprintf(announce_buf, sizeof(announce_buf),
 	    "kern.cam.ada.%d.write_cache", periph->unit_number);
 	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
-	softc->sort_io_queue = -1;
+	/* Disable queue sorting for non-rotatational media by default */
+	if (cgd->ident_data.media_rotation_rate == 1)
+		softc->sort_io_queue = 0;
+	else
+		softc->sort_io_queue = -1;
 	adagetparams(periph, cgd);
 	softc->disk = disk_alloc();
 	softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
@@ -1186,10 +1190,17 @@ adaregister(struct cam_periph *periph, v
 	softc->disk->d_flags = 0;
 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
 		softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
-	if ((softc->flags & ADA_FLAG_CAN_TRIM) ||
-	    ((softc->flags & ADA_FLAG_CAN_CFA) &&
-	    !(softc->flags & ADA_FLAG_CAN_48BIT)))
+	if (softc->flags & ADA_FLAG_CAN_TRIM) {
+		softc->disk->d_flags |= DISKFLAG_CANDELETE;
+		softc->disk->d_delmaxsize = softc->params.secsize *
+					    ATA_DSM_RANGE_MAX *
+					    softc->trim_max_ranges;
+	} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
+	    !(softc->flags & ADA_FLAG_CAN_48BIT)) {
 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
+		softc->disk->d_delmaxsize = 256 * softc->params.secsize;
+	} else
+		softc->disk->d_delmaxsize = maxio;
 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0)
 		softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
 	strlcpy(softc->disk->d_descr, cgd->ident_data.model,
@@ -1354,9 +1365,9 @@ adastart(struct cam_periph *periph, unio
 
 				/* Try to extend the previous range. */
 				if (lba == lastlba) {
-					c = min(count, 0xffff - lastcount);
+					c = min(count, ATA_DSM_RANGE_MAX - lastcount);
 					lastcount += c;
-					off = (ranges - 1) * 8;
+					off = (ranges - 1) * ATA_DSM_RANGE_SIZE;
 					req->data[off + 6] = lastcount & 0xff;
 					req->data[off + 7] =
 					    (lastcount >> 8) & 0xff;
@@ -1365,8 +1376,8 @@ adastart(struct cam_periph *periph, unio
 				}
 
 				while (count > 0) {
-					c = min(count, 0xffff);
-					off = ranges * 8;
+					c = min(count, ATA_DSM_RANGE_MAX);
+					off = ranges * ATA_DSM_RANGE_SIZE;
 					req->data[off + 0] = lba & 0xff;
 					req->data[off + 1] = (lba >> 8) & 0xff;
 					req->data[off + 2] = (lba >> 16) & 0xff;
@@ -1379,6 +1390,11 @@ adastart(struct cam_periph *periph, unio
 					count -= c;
 					lastcount = c;
 					ranges++;
+					/*
+					 * Its the caller's responsibility to ensure the
+					 * request will fit so we don't need to check for
+					 * overrun here
+					 */
 				}
 				lastlba = lba;
 				req->bps[bps++] = bp1;
@@ -1386,7 +1402,8 @@ adastart(struct cam_periph *periph, unio
 				if (bps >= TRIM_MAX_BIOS ||
 				    bp1 == NULL ||
 				    bp1->bio_bcount / softc->params.secsize >
-				    (softc->trim_max_ranges - ranges) * 0xffff)
+				    (softc->trim_max_ranges - ranges) *
+				    ATA_DSM_RANGE_MAX)
 					break;
 			} while (1);
 			cam_fill_ataio(ataio,
@@ -1395,10 +1412,12 @@ adastart(struct cam_periph *periph, unio
 			    CAM_DIR_OUT,
 			    0,
 			    req->data,
-			    ((ranges + 63) / 64) * 512,
+			    ((ranges + ATA_DSM_BLK_RANGES - 1) /
+			        ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
 			    ada_default_timeout * 1000);
 			ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT,
-			    ATA_DSM_TRIM, 0, (ranges + 63) / 64);
+			    ATA_DSM_TRIM, 0, (ranges + ATA_DSM_BLK_RANGES -
+			    1) / ATA_DSM_BLK_RANGES);
 			start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM;
 			goto out;
 		}

Modified: user/adrian/net80211_tx/sys/cam/scsi/scsi_all.c
==============================================================================
--- user/adrian/net80211_tx/sys/cam/scsi/scsi_all.c	Fri Apr 26 17:06:36 2013	(r249943)
+++ user/adrian/net80211_tx/sys/cam/scsi/scsi_all.c	Fri Apr 26 17:08:07 2013	(r249944)
@@ -40,6 +40,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/systm.h>
 #include <sys/libkern.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
 #include <sys/sysctl.h>
 #else
 #include <errno.h>
@@ -55,7 +58,13 @@ __FBSDID("$FreeBSD$");
 #include <cam/scsi/scsi_all.h>
 #include <sys/ata.h>
 #include <sys/sbuf.h>
-#ifndef _KERNEL
+
+#ifdef _KERNEL
+#include <cam/cam_periph.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_xpt_internal.h>
+#else
 #include <camlib.h>
 #include <stddef.h>
 
@@ -5853,6 +5862,57 @@ scsi_write_same(struct ccb_scsiio *csio,
 }
 
 void
+scsi_ata_identify(struct ccb_scsiio *csio, u_int32_t retries,
+		  void (*cbfcnp)(struct cam_periph *, union ccb *),
+		  u_int8_t tag_action, u_int8_t *data_ptr,
+		  u_int16_t dxfer_len, u_int8_t sense_len,
+		  u_int32_t timeout)
+{
+	scsi_ata_pass_16(csio,
+			 retries,
+			 cbfcnp,
+			 /*flags*/CAM_DIR_IN,
+			 tag_action,
+			 /*protocol*/AP_PROTO_PIO_IN,
+			 /*ata_flags*/AP_FLAG_TDIR_FROM_DEV|
+				AP_FLAG_BYT_BLOK_BYTES|AP_FLAG_TLEN_SECT_CNT,
+			 /*features*/0,
+			 /*sector_count*/dxfer_len,
+			 /*lba*/0,
+			 /*command*/ATA_ATA_IDENTIFY,
+			 /*control*/0,
+			 data_ptr,
+			 dxfer_len,
+			 sense_len,
+			 timeout);
+}
+
+void
+scsi_ata_trim(struct ccb_scsiio *csio, u_int32_t retries,
+	      void (*cbfcnp)(struct cam_periph *, union ccb *),
+	      u_int8_t tag_action, u_int16_t block_count,
+	      u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len,
+	      u_int32_t timeout)
+{
+	scsi_ata_pass_16(csio,
+			 retries,
+			 cbfcnp,
+			 /*flags*/CAM_DIR_OUT,
+			 tag_action,
+			 /*protocol*/AP_EXTEND|AP_PROTO_DMA,
+			 /*ata_flags*/AP_FLAG_TLEN_SECT_CNT|AP_FLAG_BYT_BLOK_BLOCKS,
+			 /*features*/ATA_DSM_TRIM,
+			 /*sector_count*/block_count,
+			 /*lba*/0,
+			 /*command*/ATA_DATA_SET_MANAGEMENT,
+			 /*control*/0,
+			 data_ptr,
+			 dxfer_len,
+			 sense_len,
+			 timeout);
+}
+
+void
 scsi_ata_pass_16(struct ccb_scsiio *csio, u_int32_t retries,
 		 void (*cbfcnp)(struct cam_periph *, union ccb *),
 		 u_int32_t flags, u_int8_t tag_action,
@@ -6206,6 +6266,28 @@ scsi_devid_match(uint8_t *lhs, size_t lh
 }
 
 #ifdef _KERNEL
+int
+scsi_vpd_supported_page(struct cam_periph *periph, uint8_t page_id)
+{
+	struct cam_ed *device;
+	struct scsi_vpd_supported_pages *vpds;
+	int i, num_pages;
+
+	device = periph->path->device;
+	vpds = (struct scsi_vpd_supported_pages *)device->supported_vpds;
+
+	if (vpds != NULL) {
+		num_pages = device->supported_vpds_len -
+		    SVPD_SUPPORTED_PAGES_HDR_LEN;
+		for (i = 0; i < num_pages; i++) {
+			if (vpds->page_list[i] == page_id)
+				return (1);
+		}
+	}
+
+	return (0);
+}
+
 static void
 init_scsi_delay(void)
 {

Modified: user/adrian/net80211_tx/sys/cam/scsi/scsi_all.h
==============================================================================
--- user/adrian/net80211_tx/sys/cam/scsi/scsi_all.h	Fri Apr 26 17:06:36 2013	(r249943)
+++ user/adrian/net80211_tx/sys/cam/scsi/scsi_all.h	Fri Apr 26 17:08:07 2013	(r249944)
@@ -1429,6 +1429,85 @@ struct scsi_diag_page {
 	uint8_t params[0];
 };
 
+/*
+ * Block Device Characteristics VPD Page based on
+ * T10/1799-D Revision 31
+ */
+struct scsi_vpd_block_characteristics
+{
+	u_int8_t device;
+	u_int8_t page_code;
+#define SVPD_BDC			0xB1
+	u_int8_t page_length[2];
+	u_int8_t medium_rotation_rate[2];
+#define SVPD_BDC_RATE_NOT_REPORTED	0x00
+#define SVPD_BDC_RATE_NONE_ROTATING	0x01
+	u_int8_t reserved1;
+	u_int8_t nominal_form_factor;
+#define SVPD_BDC_FORM_NOT_REPORTED	0x00
+#define SVPD_BDC_FORM_5_25INCH		0x01
+#define SVPD_BDC_FORM_3_5INCH		0x02
+#define SVPD_BDC_FORM_2_5INCH		0x03
+#define SVPD_BDC_FORM_1_5INCH		0x04
+#define SVPD_BDC_FORM_LESSTHAN_1_5INCH	0x05
+	u_int8_t reserved2[56];
+};
+
+/*
+ * Logical Block Provisioning VPD Page based on
+ * T10/1799-D Revision 31
+ */
+struct scsi_vpd_logical_block_prov
+{
+	u_int8_t device;
+	u_int8_t page_code;
+#define	SVPD_LBP		0xB2
+	u_int8_t page_length[2];
+#define SVPD_LBP_PL_BASIC	0x04
+	u_int8_t threshold_exponent;
+	u_int8_t flags;
+#define SVPD_LBP_UNMAP		0x80
+#define SVPD_LBP_WS16		0x40
+#define SVPD_LBP_WS10		0x20
+#define SVPD_LBP_RZ		0x04
+#define SVPD_LBP_ANC_SUP	0x02
+#define SVPD_LBP_DP		0x01
+	u_int8_t prov_type;
+#define SVPD_LBP_RESOURCE	0x01
+#define SVPD_LBP_THIN		0x02
+	u_int8_t reserved;
+	/*
+	 * Provisioning Group Descriptor can be here if SVPD_LBP_DP is set
+	 * Its size can be determined from page_length - 4
+	 */
+};
+
+/*
+ * Block Limits VDP Page based on
+ * T10/1799-D Revision 31
+ */
+struct scsi_vpd_block_limits
+{
+	u_int8_t device;
+	u_int8_t page_code;
+#define	SVPD_BLOCK_LIMITS	0xB0
+	u_int8_t page_length[2];
+#define SVPD_BL_PL_BASIC	0x10
+#define SVPD_BL_PL_TP		0x3C
+	u_int8_t reserved1;
+	u_int8_t max_cmp_write_len;
+	u_int8_t opt_txfer_len_grain[2];
+	u_int8_t max_txfer_len[4];
+	u_int8_t opt_txfer_len[4];
+	u_int8_t max_prefetch[4];
+	u_int8_t max_unmap_lba_cnt[4];
+	u_int8_t max_unmap_blk_cnt[4];
+	u_int8_t opt_unmap_grain[4];
+	u_int8_t unmap_grain_align[4];
+	u_int8_t max_write_same_length[8];
+	u_int8_t reserved2[20];
+};
+
 struct scsi_read_capacity
 {
 	u_int8_t opcode;
@@ -2203,6 +2282,8 @@ int		scsi_sense_sbuf(struct ccb_scsiio *
 char *		scsi_sense_string(struct ccb_scsiio *csio,
 				  char *str, int str_len);
 void		scsi_sense_print(struct ccb_scsiio *csio);
+int 		scsi_vpd_supported_page(struct cam_periph *periph,
+					uint8_t page_id);
 #else /* _KERNEL */
 int		scsi_command_string(struct cam_device *device,
 				    struct ccb_scsiio *csio, struct sbuf *sb);
@@ -2396,6 +2477,18 @@ void scsi_write_same(struct ccb_scsiio *
 		     u_int32_t dxfer_len, u_int8_t sense_len,
 		     u_int32_t timeout);
 
+void scsi_ata_identify(struct ccb_scsiio *csio, u_int32_t retries,
+		       void (*cbfcnp)(struct cam_periph *, union ccb *),
+		       u_int8_t tag_action, u_int8_t *data_ptr,
+		       u_int16_t dxfer_len, u_int8_t sense_len,
+		       u_int32_t timeout);
+
+void scsi_ata_trim(struct ccb_scsiio *csio, u_int32_t retries,
+	           void (*cbfcnp)(struct cam_periph *, union ccb *),
+	           u_int8_t tag_action, u_int16_t block_count,
+	           u_int8_t *data_ptr, u_int16_t dxfer_len,
+	           u_int8_t sense_len, u_int32_t timeout);
+
 void scsi_ata_pass_16(struct ccb_scsiio *csio, u_int32_t retries,
 		      void (*cbfcnp)(struct cam_periph *, union ccb *),
 		      u_int32_t flags, u_int8_t tag_action,

Modified: user/adrian/net80211_tx/sys/cam/scsi/scsi_da.c
==============================================================================
--- user/adrian/net80211_tx/sys/cam/scsi/scsi_da.c	Fri Apr 26 17:06:36 2013	(r249943)
+++ user/adrian/net80211_tx/sys/cam/scsi/scsi_da.c	Fri Apr 26 17:08:07 2013	(r249944)
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/eventhandler.h>
 #include <sys/malloc.h>
 #include <sys/cons.h>
+#include <sys/endian.h>
 #include <geom/geom.h>
 #include <geom/geom_disk.h>
 #endif /* _KERNEL */
@@ -67,8 +68,12 @@ __FBSDID("$FreeBSD$");
 
 #ifdef _KERNEL
 typedef enum {
-	DA_STATE_PROBE,
-	DA_STATE_PROBE2,
+	DA_STATE_PROBE_RC,
+	DA_STATE_PROBE_RC16,
+	DA_STATE_PROBE_LBP,
+	DA_STATE_PROBE_BLK_LIMITS,
+	DA_STATE_PROBE_BDC,
+	DA_STATE_PROBE_ATA,
 	DA_STATE_NORMAL
 } da_state;
 
@@ -96,29 +101,47 @@ typedef enum {
 } da_quirks;
 
 typedef enum {
-	DA_CCB_PROBE		= 0x01,
-	DA_CCB_PROBE2		= 0x02,
-	DA_CCB_BUFFER_IO	= 0x03,
-	DA_CCB_WAITING		= 0x04,
-	DA_CCB_DUMP		= 0x05,
-	DA_CCB_DELETE		= 0x06,
-	DA_CCB_TUR		= 0x07,
+	DA_CCB_PROBE_RC		= 0x01,
+	DA_CCB_PROBE_RC16	= 0x02,
+	DA_CCB_PROBE_LBP	= 0x03,
+	DA_CCB_PROBE_BLK_LIMITS	= 0x04,
+	DA_CCB_PROBE_BDC	= 0x05,
+	DA_CCB_PROBE_ATA	= 0x06,
+	DA_CCB_BUFFER_IO	= 0x07,
+	DA_CCB_WAITING		= 0x08,
+	DA_CCB_DUMP		= 0x0A,
+	DA_CCB_DELETE		= 0x0B,
+ 	DA_CCB_TUR		= 0x0C,
 	DA_CCB_TYPE_MASK	= 0x0F,
 	DA_CCB_RETRY_UA		= 0x10
 } da_ccb_state;
 
+/*
+ * Order here is important for method choice
+ *
+ * We prefer ATA_TRIM as tests run against a Sandforce 2281 SSD attached to
+ * LSI 2008 (mps) controller (FW: v12, Drv: v14) resulted 20% quicker deletes
+ * using ATA_TRIM than the corresponding UNMAP results for a real world mysql
+ * import taking 5mins.
+ *
+ */
 typedef enum {
 	DA_DELETE_NONE,
 	DA_DELETE_DISABLE,
-	DA_DELETE_ZERO,
-	DA_DELETE_WS10,
-	DA_DELETE_WS16,
+	DA_DELETE_ATA_TRIM,
 	DA_DELETE_UNMAP,
-	DA_DELETE_MAX = DA_DELETE_UNMAP
+	DA_DELETE_WS16,
+	DA_DELETE_WS10,
+	DA_DELETE_ZERO,
+	DA_DELETE_MIN = DA_DELETE_UNMAP,
+	DA_DELETE_MAX = DA_DELETE_ZERO
 } da_delete_methods;
 
 static const char *da_delete_method_names[] =
-    { "NONE", "DISABLE", "ZERO", "WS10", "WS16", "UNMAP" };
+    { "NONE", "DISABLE", "UNMAP", "ATA_TRIM", "WS16", "WS10", "ZERO" };
+static const char *da_delete_method_desc[] =
+    { "NONE", "DISABLED", "UNMAP", "ATA TRIM", "WRITE SAME(16) with UNMAP",
+      "WRITE SAME(10) with UNMAP", "ZERO" };
 
 /* Offsets into our private area for storing information */
 #define ccb_state	ppriv_field0
@@ -134,7 +157,17 @@ struct disk_params {
 	u_int     stripeoffset;
 };
 
-#define UNMAP_MAX_RANGES	512
+#define UNMAP_RANGE_MAX		0xffffffff
+#define UNMAP_HEAD_SIZE		8
+#define UNMAP_RANGE_SIZE	16
+#define UNMAP_MAX_RANGES	2048 /* Protocol Max is 4095 */
+#define UNMAP_BUF_SIZE		((UNMAP_MAX_RANGES * UNMAP_RANGE_SIZE) + \
+				UNMAP_HEAD_SIZE)
+
+#define WS10_MAX_BLKS		0xffff
+#define WS16_MAX_BLKS		0xffffffff
+#define ATA_TRIM_MAX_RANGES	((UNMAP_BUF_SIZE / \
+	(ATA_DSM_RANGE_SIZE * ATA_DSM_BLK_SIZE)) * ATA_DSM_BLK_SIZE)
 
 struct da_softc {
 	struct	 bio_queue_head bio_queue;
@@ -150,11 +183,14 @@ struct da_softc {
 	int	 error_inject;
 	int	 ordered_tag_count;
 	int	 outstanding_cmds;
-	int	 unmap_max_ranges;
-	int	 unmap_max_lba;
+	int	 trim_max_ranges;
 	int	 delete_running;
 	int	 tur;
-	da_delete_methods	 delete_method;
+	int	 delete_available;	/* Delete methods possibly available */
+	uint32_t		unmap_max_ranges;
+	uint32_t		unmap_max_lba;
+	uint64_t		ws_max_blks;
+	da_delete_methods	delete_method;
 	struct	 disk_params params;
 	struct	 disk *disk;
 	union	 ccb saved_ccb;
@@ -163,11 +199,18 @@ struct da_softc {
 	struct sysctl_oid	*sysctl_tree;
 	struct callout		sendordered_c;
 	uint64_t wwpn;
-	uint8_t	 unmap_buf[UNMAP_MAX_RANGES * 16 + 8];
+	uint8_t	 unmap_buf[UNMAP_BUF_SIZE];
 	struct scsi_read_capacity_data_long rcaplong;
 	struct callout		mediapoll_c;
 };
 
+#define dadeleteflag(softc, delete_method, enable)			\
+	if (enable) {							\
+		softc->delete_available |= (1 << delete_method);	\
+	} else {							\
+		softc->delete_available &= ~(1 << delete_method);	\
+	}
+
 struct da_quirk_entry {
 	struct scsi_inquiry_pattern inq_pat;
 	da_quirks quirks;
@@ -868,8 +911,14 @@ static	void		daasync(void *callback_arg,
 static	void		dasysctlinit(void *context, int pending);
 static	int		dacmdsizesysctl(SYSCTL_HANDLER_ARGS);
 static	int		dadeletemethodsysctl(SYSCTL_HANDLER_ARGS);
+static	int		dadeletemaxsysctl(SYSCTL_HANDLER_ARGS);
 static	void		dadeletemethodset(struct da_softc *softc,
 					  da_delete_methods delete_method);
+static	off_t		dadeletemaxsize(struct da_softc *softc,
+					da_delete_methods delete_method);
+static	void		dadeletemethodchoose(struct da_softc *softc,
+					     da_delete_methods default_method);
+
 static	periph_ctor_t	daregister;
 static	periph_dtor_t	dacleanup;
 static	periph_start_t	dastart;
@@ -1492,6 +1541,10 @@ dasysctlinit(void *context, int pending)
 		softc, 0, dadeletemethodsysctl, "A",
 		"BIO_DELETE execution method");
 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+		OID_AUTO, "delete_max", CTLTYPE_U64 | CTLFLAG_RW,
+		softc, 0, dadeletemaxsysctl, "Q",
+		"Maximum BIO_DELETE size");
+	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
 		OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW,
 		&softc->minimum_cmd_size, 0, dacmdsizesysctl, "I",
 		"Minimum CDB size");
@@ -1537,6 +1590,29 @@ dasysctlinit(void *context, int pending)
 }
 
 static int
+dadeletemaxsysctl(SYSCTL_HANDLER_ARGS)
+{
+	int error;
+	uint64_t value;
+	struct da_softc *softc;
+
+	softc = (struct da_softc *)arg1;
+
+	value = softc->disk->d_delmaxsize;
+	error = sysctl_handle_64(oidp, &value, 0, req);
+	if ((error != 0) || (req->newptr == NULL))
+		return (error);
+
+	/* only accept values smaller than the calculated value */
+	if (value > softc->disk->d_delmaxsize) {
+		return (EINVAL);
+	}
+	softc->disk->d_delmaxsize = value;
+
+	return (0);
+}
+
+static int
 dacmdsizesysctl(SYSCTL_HANDLER_ARGS)
 {
 	int error, value;
@@ -1574,6 +1650,7 @@ dadeletemethodset(struct da_softc *softc
 
 
 	softc->delete_method = delete_method;
+	softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method);
 
 	if (softc->delete_method > DA_DELETE_DISABLE)
 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
@@ -1581,6 +1658,53 @@ dadeletemethodset(struct da_softc *softc
 		softc->disk->d_flags &= ~DISKFLAG_CANDELETE;
 }
 
+static off_t
+dadeletemaxsize(struct da_softc *softc, da_delete_methods delete_method)
+{
+	off_t sectors;
+
+	switch(delete_method) {
+	case DA_DELETE_UNMAP:
+		sectors = (off_t)softc->unmap_max_lba * softc->unmap_max_ranges;
+		break;
+	case DA_DELETE_ATA_TRIM:
+		sectors = (off_t)ATA_DSM_RANGE_MAX * softc->trim_max_ranges;
+		break;
+	case DA_DELETE_WS16:
+		sectors = (off_t)min(softc->ws_max_blks, WS16_MAX_BLKS);
+		break;
+	case DA_DELETE_ZERO:
+	case DA_DELETE_WS10:
+		sectors = (off_t)min(softc->ws_max_blks, WS10_MAX_BLKS);
+		break;
+	default:
+		return 0;
+	}
+
+	return (off_t)softc->params.secsize *
+	    min(sectors, (off_t)softc->params.sectors);
+}
+
+static void
+dadeletemethodchoose(struct da_softc *softc, da_delete_methods default_method)
+{
+	int i, delete_method;
+
+	delete_method = default_method;
+
+	/*
+	 * Use the pre-defined order to choose the best
+	 * performing delete.
+	 */
+	for (i = DA_DELETE_MIN; i <= DA_DELETE_MAX; i++) {
+		if (softc->delete_available & (1 << i)) {
+			dadeletemethodset(softc, i);
+			return;
+		}
+	}
+	dadeletemethodset(softc, delete_method);
+}
+
 static int
 dadeletemethodsysctl(SYSCTL_HANDLER_ARGS)
 {
@@ -1634,14 +1758,16 @@ daregister(struct cam_periph *periph, vo
 	}
 
 	LIST_INIT(&softc->pending_ccbs);
-	softc->state = DA_STATE_PROBE;
+	softc->state = DA_STATE_PROBE_RC;
 	bioq_init(&softc->bio_queue);
 	bioq_init(&softc->delete_queue);
 	bioq_init(&softc->delete_run_queue);
 	if (SID_IS_REMOVABLE(&cgd->inq_data))
 		softc->flags |= DA_FLAG_PACK_REMOVABLE;
 	softc->unmap_max_ranges = UNMAP_MAX_RANGES;
-	softc->unmap_max_lba = 1024*1024*2;
+	softc->unmap_max_lba = UNMAP_RANGE_MAX;
+	softc->ws_max_blks = WS16_MAX_BLKS;
+	softc->trim_max_ranges = ATA_TRIM_MAX_RANGES;
 	softc->sort_io_queue = -1;
 
 	periph->softc = softc;
@@ -1718,7 +1844,7 @@ daregister(struct cam_periph *periph, vo
 	/* Predict whether device may support READ CAPACITY(16). */
 	if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3) {
 		softc->flags |= DA_FLAG_CAN_RC16;
-		softc->state = DA_STATE_PROBE2;
+		softc->state = DA_STATE_PROBE_RC16;
 	}
 
 	/*
@@ -1820,6 +1946,7 @@ dastart(struct cam_periph *periph, union
 
 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("dastart\n"));
 
+skipstate:
 	switch (softc->state) {
 	case DA_STATE_NORMAL:
 	{
@@ -1844,13 +1971,36 @@ dastart(struct cam_periph *periph, union
 		if (!softc->delete_running &&
 		    (bp = bioq_first(&softc->delete_queue)) != NULL) {
 		    uint64_t lba;
-		    u_int count;
+		    uint64_t count; /* forward compat with WS32 */
+
+		    /*
+		     * In each of the methods below, while its the caller's
+		     * responsibility to ensure the request will fit into a
+		     * single device request, we might have changed the delete
+		     * method due to the device incorrectly advertising either
+		     * its supported methods or limits.
+		     * 
+		     * To prevent this causing further issues we validate the
+		     * against the methods limits, and warn which would
+		     * otherwise be unnecessary.
+		     */
 
 		    if (softc->delete_method == DA_DELETE_UNMAP) {
 			uint8_t *buf = softc->unmap_buf;
 			uint64_t lastlba = (uint64_t)-1;
-			uint32_t lastcount = 0;
-			int blocks = 0, off, ranges = 0;
+			uint32_t lastcount = 0, c;
+			uint64_t totalcount = 0;
+			uint32_t off, ranges = 0;
+
+			/*
+			 * Currently this doesn't take the UNMAP
+			 * Granularity and Granularity Alignment
+			 * fields into account.
+			 *
+			 * This could result in both unoptimal unmap
+			 * requests as as well as UNMAP calls unmapping
+			 * fewer LBA's than requested.
+			 */
 
 			softc->delete_running = 1;
 			bzero(softc->unmap_buf, sizeof(softc->unmap_buf));
@@ -1864,22 +2014,44 @@ dastart(struct cam_periph *periph, union
 
 				/* Try to extend the previous range. */
 				if (lba == lastlba) {
-					lastcount += count;
-					off = (ranges - 1) * 16 + 8;
+					c = min(count, softc->unmap_max_lba -
+						lastcount);
+					lastcount += c;
+					off = ((ranges - 1) * UNMAP_RANGE_SIZE) +
+					      UNMAP_HEAD_SIZE;
 					scsi_ulto4b(lastcount, &buf[off + 8]);
-				} else if (count > 0) {
-					off = ranges * 16 + 8;
+					count -= c;
+					lba +=c;
+					totalcount += c;
+				}
+
+				while (count > 0) {
+					c = min(count, softc->unmap_max_lba);
+					if (totalcount + c > softc->unmap_max_lba ||
+					    ranges >= softc->unmap_max_ranges) {
+						xpt_print(periph->path,
+						  "%s issuing short delete %ld > %ld"
+						  "|| %d >= %d",
+						  da_delete_method_desc[softc->delete_method],
+						  totalcount + c, softc->unmap_max_lba,
+						  ranges, softc->unmap_max_ranges);
+						break;
+					}
+					off = (ranges * UNMAP_RANGE_SIZE) +
+					      UNMAP_HEAD_SIZE;
 					scsi_u64to8b(lba, &buf[off + 0]);
-					scsi_ulto4b(count, &buf[off + 8]);
-					lastcount = count;
+					scsi_ulto4b(c, &buf[off + 8]);
+					lba += c;
+					totalcount += c;
 					ranges++;
+					count -= c;
+					lastcount = c;
 				}
-				blocks += count;
-				lastlba = lba + count;
+				lastlba = lba;
 				bp1 = bioq_first(&softc->delete_queue);
 				if (bp1 == NULL ||
 				    ranges >= softc->unmap_max_ranges ||
-				    blocks + bp1->bio_bcount /
+				    totalcount + bp1->bio_bcount /
 				     softc->params.secsize > softc->unmap_max_lba)
 					break;
 			} while (1);
@@ -1897,9 +2069,92 @@ dastart(struct cam_periph *periph, union
 					da_default_timeout * 1000);
 			start_ccb->ccb_h.ccb_state = DA_CCB_DELETE;
 			goto out;
+		    } else if (softc->delete_method == DA_DELETE_ATA_TRIM) {
+				uint8_t *buf = softc->unmap_buf;
+				uint64_t lastlba = (uint64_t)-1;
+				uint32_t lastcount = 0, c, requestcount;
+				int ranges = 0, off, block_count;
+
+				softc->delete_running = 1;
+				bzero(softc->unmap_buf, sizeof(softc->unmap_buf));
+				bp1 = bp;
+				do {
+					bioq_remove(&softc->delete_queue, bp1);
+					if (bp1 != bp)
+						bioq_insert_tail(&softc->delete_run_queue, bp1);
+					lba = bp1->bio_pblkno;
+					count = bp1->bio_bcount / softc->params.secsize;
+					requestcount = count;
+
+					/* Try to extend the previous range. */
+					if (lba == lastlba) {
+						c = min(count, ATA_DSM_RANGE_MAX - lastcount);
+						lastcount += c;
+						off = (ranges - 1) * 8;
+						buf[off + 6] = lastcount & 0xff;
+						buf[off + 7] = (lastcount >> 8) & 0xff;
+						count -= c;
+						lba += c;
+					}
+
+					while (count > 0) {
+						c = min(count, ATA_DSM_RANGE_MAX);
+						off = ranges * 8;
+
+						buf[off + 0] = lba & 0xff;
+						buf[off + 1] = (lba >> 8) & 0xff;
+						buf[off + 2] = (lba >> 16) & 0xff;
+						buf[off + 3] = (lba >> 24) & 0xff;
+						buf[off + 4] = (lba >> 32) & 0xff;
+						buf[off + 5] = (lba >> 40) & 0xff;
+						buf[off + 6] = c & 0xff;
+						buf[off + 7] = (c >> 8) & 0xff;
+						lba += c;
+						ranges++;
+						count -= c;
+						lastcount = c;
+						if (count != 0 && ranges == softc->trim_max_ranges) {
+							xpt_print(periph->path,
+							  "%s issuing short delete %ld > %ld",
+							  da_delete_method_desc[softc->delete_method],
+							  requestcount,
+							  (softc->trim_max_ranges - ranges) *
+							  ATA_DSM_RANGE_MAX);
+							break;
+						}
+					}
+					lastlba = lba;
+					bp1 = bioq_first(&softc->delete_queue);
+					if (bp1 == NULL ||
+					    bp1->bio_bcount / softc->params.secsize >
+					    (softc->trim_max_ranges - ranges) *
+						    ATA_DSM_RANGE_MAX)
+						break;
+				} while (1);
+
+				block_count = (ranges + ATA_DSM_BLK_RANGES - 1) /
+					      ATA_DSM_BLK_RANGES;
+				scsi_ata_trim(&start_ccb->csio,
+						/*retries*/da_retry_count,
+						/*cbfcnp*/dadone,
+						/*tag_action*/MSG_SIMPLE_Q_TAG,
+						block_count,
+						/*data_ptr*/buf,
+						/*dxfer_len*/block_count * ATA_DSM_BLK_SIZE,
+						/*sense_len*/SSD_FULL_SIZE,
+						da_default_timeout * 1000);
+				start_ccb->ccb_h.ccb_state = DA_CCB_DELETE;
+				goto out;
 		    } else if (softc->delete_method == DA_DELETE_ZERO ||
 			       softc->delete_method == DA_DELETE_WS10 ||
 			       softc->delete_method == DA_DELETE_WS16) {
+			/*
+			 * We calculate ws_max_blks here based off d_delmaxsize instead
+			 * of using softc->ws_max_blks as it is absolute max for the
+			 * device not the protocol max which may well be lower
+			 */
+			uint64_t ws_max_blks;
+			ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize;
 			softc->delete_running = 1;
 			lba = bp->bio_pblkno;
 			count = 0;
@@ -1909,11 +2164,19 @@ dastart(struct cam_periph *periph, union
 				if (bp1 != bp)
 					bioq_insert_tail(&softc->delete_run_queue, bp1);
 				count += bp1->bio_bcount / softc->params.secsize;
+				if (count > ws_max_blks) {
+					count = min(count, ws_max_blks);
+					xpt_print(periph->path,
+					  "%s issuing short delete %ld > %ld",
+					  da_delete_method_desc[softc->delete_method],
+					  count, ws_max_blks);
+					break;
+				}
 				bp1 = bioq_first(&softc->delete_queue);
 				if (bp1 == NULL ||
 				    lba + count != bp1->bio_pblkno ||
 				    count + bp1->bio_bcount /
-				     softc->params.secsize > 0xffff)
+				     softc->params.secsize > ws_max_blks)
 					break;
 			} while (1);
 
@@ -2037,7 +2300,7 @@ out:
 		daschedule(periph);
 		break;
 	}
-	case DA_STATE_PROBE:
+	case DA_STATE_PROBE_RC:
 	{
 		struct scsi_read_capacity_data *rcap;
 
@@ -2056,11 +2319,11 @@ out:
 				   SSD_FULL_SIZE,
 				   /*timeout*/5000);
 		start_ccb->ccb_h.ccb_bp = NULL;
-		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE;
+		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_RC;
 		xpt_action(start_ccb);
 		break;
 	}
-	case DA_STATE_PROBE2:
+	case DA_STATE_PROBE_RC16:
 	{
 		struct scsi_read_capacity_data_long *rcaplong;
 
@@ -2083,8 +2346,143 @@ out:
 				      /*sense_len*/ SSD_FULL_SIZE,
 				      /*timeout*/ da_default_timeout * 1000);
 		start_ccb->ccb_h.ccb_bp = NULL;
-		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE2;
-		xpt_action(start_ccb);	
+		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_RC16;
+		xpt_action(start_ccb);
+		break;
+	}
+	case DA_STATE_PROBE_LBP:
+	{
+		struct scsi_vpd_logical_block_prov *lbp;
+
+		if (!scsi_vpd_supported_page(periph, SVPD_LBP)) {
+			/*
+			 * If we get here we don't support any SBC-3 delete
+			 * methods with UNMAP as the Logical Block Provisioning
+			 * VPD page support is required for devices which
+			 * support it according to T10/1799-D Revision 31
+			 * however older revisions of the spec don't mandate
+			 * this so we currently don't remove these methods
+			 * from the available set.
+			 */
+			softc->state = DA_STATE_PROBE_BLK_LIMITS;
+			goto skipstate;
+		}
+
+		lbp = (struct scsi_vpd_logical_block_prov *)
+			malloc(sizeof(*lbp), M_SCSIDA, M_NOWAIT|M_ZERO);
+
+		if (lbp == NULL) {
+			printf("dastart: Couldn't malloc lbp data\n");
+			/* da_free_periph??? */
+			break;
+		}
+
+		scsi_inquiry(&start_ccb->csio,
+			     /*retries*/da_retry_count,
+			     /*cbfcnp*/dadone,
+			     /*tag_action*/MSG_SIMPLE_Q_TAG,
+			     /*inq_buf*/(u_int8_t *)lbp,
+			     /*inq_len*/sizeof(*lbp),
+			     /*evpd*/TRUE,
+			     /*page_code*/SVPD_LBP,
+			     /*sense_len*/SSD_MIN_SIZE,
+			     /*timeout*/da_default_timeout * 1000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_LBP;
+		xpt_action(start_ccb);
+		break;
+	}
+	case DA_STATE_PROBE_BLK_LIMITS:
+	{
+		struct scsi_vpd_block_limits *block_limits;
+
+		if (!scsi_vpd_supported_page(periph, SVPD_BLOCK_LIMITS)) {
+			/* Not supported skip to next probe */
+			softc->state = DA_STATE_PROBE_ATA;
+			goto skipstate;
+		}
+
+		block_limits = (struct scsi_vpd_block_limits *)
+			malloc(sizeof(*block_limits), M_SCSIDA, M_NOWAIT|M_ZERO);
+
+		if (block_limits == NULL) {
+			printf("dastart: Couldn't malloc block_limits data\n");
+			/* da_free_periph??? */
+			break;
+		}
+
+		scsi_inquiry(&start_ccb->csio,
+			     /*retries*/da_retry_count,
+			     /*cbfcnp*/dadone,
+			     /*tag_action*/MSG_SIMPLE_Q_TAG,
+			     /*inq_buf*/(u_int8_t *)block_limits,
+			     /*inq_len*/sizeof(*block_limits),
+			     /*evpd*/TRUE,
+			     /*page_code*/SVPD_BLOCK_LIMITS,
+			     /*sense_len*/SSD_MIN_SIZE,
+			     /*timeout*/da_default_timeout * 1000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_BLK_LIMITS;
+		xpt_action(start_ccb);
+		break;
+	}
+	case DA_STATE_PROBE_BDC:
+	{
+		struct scsi_vpd_block_characteristics *bdc;
+
+		if (!scsi_vpd_supported_page(periph, SVPD_BDC)) {
+			softc->state = DA_STATE_PROBE_ATA;
+			goto skipstate;
+		}
+
+		bdc = (struct scsi_vpd_block_characteristics *)
+			malloc(sizeof(*bdc), M_SCSIDA, M_NOWAIT|M_ZERO);
+
+		if (bdc == NULL) {
+			printf("dastart: Couldn't malloc bdc data\n");
+			/* da_free_periph??? */
+			break;
+		}
+
+		scsi_inquiry(&start_ccb->csio,
+			     /*retries*/da_retry_count,
+			     /*cbfcnp*/dadone,
+			     /*tag_action*/MSG_SIMPLE_Q_TAG,
+			     /*inq_buf*/(u_int8_t *)bdc,
+			     /*inq_len*/sizeof(*bdc),
+			     /*evpd*/TRUE,
+			     /*page_code*/SVPD_BDC,
+			     /*sense_len*/SSD_MIN_SIZE,
+			     /*timeout*/da_default_timeout * 1000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_BDC;
+		xpt_action(start_ccb);
+		break;
+	}
+	case DA_STATE_PROBE_ATA:
+	{
+		struct ata_params *ata_params;
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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