svn commit: r249944 - in user/adrian/net80211_tx/sys: cam/ata cam/scsi geom sys
Adrian Chadd
adrian at FreeBSD.org
Fri Apr 26 17:08:08 UTC 2013
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 ***
More information about the svn-src-user
mailing list