summaryrefslogtreecommitdiffstats
path: root/sys/cam/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cam/scsi')
-rw-r--r--sys/cam/scsi/scsi_all.c9
-rw-r--r--sys/cam/scsi/scsi_all.h3
-rw-r--r--sys/cam/scsi/scsi_ch.c74
-rw-r--r--sys/cam/scsi/scsi_da.c498
-rw-r--r--sys/cam/scsi/scsi_xpt.c4
5 files changed, 345 insertions, 243 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index 40c3ef0..324dc9a 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -665,6 +665,10 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
if (pd_type == T_RBC)
pd_type = T_DIRECT;
+ /* Map NODEVICE to Direct Access Device to handle REPORT LUNS, etc. */
+ if (pd_type == T_NODEVICE)
+ pd_type = T_DIRECT;
+
opmask = 1 << pd_type;
for (j = 0; j < num_tables; j++) {
@@ -1645,7 +1649,7 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x24, 0x08, SS_RDEF, /* XXX TBD */
"Invalid XCDB") },
/* DTLPWROMAEBKVF */
- { SST(0x25, 0x00, SS_FATAL | ENXIO,
+ { SST(0x25, 0x00, SS_FATAL | ENXIO | SSQ_LOST,
"Logical unit not supported") },
/* DTLPWROMAEBKVF */
{ SST(0x26, 0x00, SS_FATAL | EINVAL,
@@ -2163,7 +2167,7 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x3F, 0x0D, SS_RDEF,
"Volume set reassigned") },
/* DTLPWROMAE */
- { SST(0x3F, 0x0E, SS_RDEF, /* XXX TBD */
+ { SST(0x3F, 0x0E, SS_RDEF | SSQ_RESCAN ,
"Reported LUNs data has changed") },
/* DTLPWROMAEBKVF */
{ SST(0x3F, 0x0F, SS_RDEF, /* XXX TBD */
@@ -3263,6 +3267,7 @@ scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data,
action |= SS_RETRY|SSQ_DECREMENT_COUNT|
SSQ_PRINT_SENSE;
}
+ action |= SSQ_UA;
}
}
if ((action & SS_MASK) >= SS_START &&
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index 4c024a9..72d9d15 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -88,6 +88,9 @@ typedef enum {
* and text.
*/
SSQ_PRINT_SENSE = 0x0800,
+ SSQ_UA = 0x1000, /* Broadcast UA. */
+ SSQ_RESCAN = 0x2000, /* Rescan target for LUNs. */
+ SSQ_LOST = 0x4000, /* Destroy the LUNs. */
SSQ_MASK = 0xff00
} scsi_sense_action_qualifier;
diff --git a/sys/cam/scsi/scsi_ch.c b/sys/cam/scsi/scsi_ch.c
index 02bbd60..2aad04c 100644
--- a/sys/cam/scsi/scsi_ch.c
+++ b/sys/cam/scsi/scsi_ch.c
@@ -102,7 +102,7 @@ static const u_int32_t CH_TIMEOUT_MODE_SENSE = 6000;
static const u_int32_t CH_TIMEOUT_MOVE_MEDIUM = 100000;
static const u_int32_t CH_TIMEOUT_EXCHANGE_MEDIUM = 100000;
static const u_int32_t CH_TIMEOUT_POSITION_TO_ELEMENT = 100000;
-static const u_int32_t CH_TIMEOUT_READ_ELEMENT_STATUS = 10000;
+static const u_int32_t CH_TIMEOUT_READ_ELEMENT_STATUS = 60000;
static const u_int32_t CH_TIMEOUT_SEND_VOLTAG = 10000;
static const u_int32_t CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS = 500000;
@@ -122,12 +122,14 @@ typedef enum {
typedef enum {
CH_Q_NONE = 0x00,
- CH_Q_NO_DBD = 0x01
+ CH_Q_NO_DBD = 0x01,
+ CH_Q_NO_DVCID = 0x02
} ch_quirks;
#define CH_Q_BIT_STRING \
"\020" \
- "\001NO_DBD"
+ "\001NO_DBD" \
+ "\002NO_DVCID"
#define ccb_state ppriv_field0
#define ccb_bp ppriv_ptr1
@@ -396,6 +398,14 @@ chregister(struct cam_periph *periph, void *arg)
periph->softc = softc;
softc->quirks = CH_Q_NONE;
+ /*
+ * The DVCID and CURDATA bits were not introduced until the SMC
+ * spec. If this device claims SCSI-2 or earlier support, then it
+ * very likely does not support these bits.
+ */
+ if (cgd->inq_data.version <= SCSI_REV_2)
+ softc->quirks |= CH_Q_NO_DVCID;
+
bzero(&cpi, sizeof(cpi));
xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
cpi.ccb_h.func_code = XPT_PATH_INQ;
@@ -1208,6 +1218,8 @@ chgetelemstatus(struct cam_periph *periph, int scsi_version, u_long cmd,
caddr_t data = NULL;
size_t size, desclen;
int avail, i, error = 0;
+ int curdata, dvcid, sense_flags;
+ int try_no_dvcid = 0;
struct changer_element_status *user_data = NULL;
struct ch_softc *softc;
union ccb *ccb;
@@ -1239,14 +1251,31 @@ chgetelemstatus(struct cam_periph *periph, int scsi_version, u_long cmd,
cam_periph_lock(periph);
ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+ sense_flags = SF_RETRY_UA;
+ if (softc->quirks & CH_Q_NO_DVCID) {
+ dvcid = 0;
+ curdata = 0;
+ } else {
+ dvcid = 1;
+ curdata = 1;
+ /*
+ * Don't print anything for an Illegal Request, because
+ * these flags can cause some changers to complain. We'll
+ * retry without them if we get an error.
+ */
+ sense_flags |= SF_QUIET_IR;
+ }
+
+retry_einval:
+
scsi_read_element_status(&ccb->csio,
/* retries */ 1,
/* cbfcnp */ chdone,
/* tag_action */ MSG_SIMPLE_Q_TAG,
/* voltag */ want_voltags,
/* sea */ softc->sc_firsts[chet],
- /* dvcid */ 1,
- /* curdata */ 1,
+ /* curdata */ curdata,
+ /* dvcid */ dvcid,
/* count */ 1,
/* data_ptr */ data,
/* dxfer_len */ 1024,
@@ -1254,9 +1283,38 @@ chgetelemstatus(struct cam_periph *periph, int scsi_version, u_long cmd,
/* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
- /*sense_flags*/ SF_RETRY_UA,
+ /*sense_flags*/ sense_flags,
softc->device_stats);
+ /*
+ * An Illegal Request sense key (only used if there is no asc/ascq)
+ * or 0x24,0x00 for an ASC/ASCQ both map to EINVAL. If dvcid or
+ * curdata are set (we set both or neither), try turning them off
+ * and see if the command is successful.
+ */
+ if ((error == EINVAL)
+ && (dvcid || curdata)) {
+ dvcid = 0;
+ curdata = 0;
+ error = 0;
+ /* At this point we want to report any Illegal Request */
+ sense_flags &= ~SF_QUIET_IR;
+ try_no_dvcid = 1;
+ goto retry_einval;
+ }
+
+ /*
+ * In this case, we tried a read element status with dvcid and
+ * curdata set, and it failed. We retried without those bits, and
+ * it succeeded. Suggest to the user that he set a quirk, so we
+ * don't go through the retry process the first time in the future.
+ * This should only happen on changers that claim SCSI-3 or higher,
+ * but don't support these bits.
+ */
+ if ((try_no_dvcid != 0)
+ && (error == 0))
+ softc->quirks |= CH_Q_NO_DVCID;
+
if (error)
goto done;
cam_periph_unlock(periph);
@@ -1284,8 +1342,8 @@ chgetelemstatus(struct cam_periph *periph, int scsi_version, u_long cmd,
/* voltag */ want_voltags,
/* sea */ softc->sc_firsts[chet]
+ cesr->cesr_element_base,
- /* dvcid */ 1,
- /* curdata */ 1,
+ /* curdata */ curdata,
+ /* dvcid */ dvcid,
/* count */ cesr->cesr_element_count,
/* data_ptr */ data,
/* dxfer_len */ size,
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 7988309..a2b802c 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -144,6 +144,22 @@ typedef enum {
DA_DELETE_MAX = DA_DELETE_ZERO
} da_delete_methods;
+typedef void da_delete_func_t (struct cam_periph *periph, union ccb *ccb,
+ struct bio *bp);
+static da_delete_func_t da_delete_trim;
+static da_delete_func_t da_delete_unmap;
+static da_delete_func_t da_delete_ws;
+
+static const void * da_delete_functions[] = {
+ NULL,
+ NULL,
+ da_delete_trim,
+ da_delete_unmap,
+ da_delete_ws,
+ da_delete_ws,
+ da_delete_ws
+};
+
static const char *da_delete_method_names[] =
{ "NONE", "DISABLE", "ATA_TRIM", "UNMAP", "WS16", "WS10", "ZERO" };
static const char *da_delete_method_desc[] =
@@ -198,6 +214,7 @@ struct da_softc {
uint32_t unmap_max_lba;
uint64_t ws_max_blks;
da_delete_methods delete_method;
+ da_delete_func_t *delete_func;
struct disk_params params;
struct disk *disk;
union ccb saved_ccb;
@@ -1031,6 +1048,14 @@ static struct da_quirk_entry da_quirk_table[] =
},
{
/*
+ * OCZ Vertex 4 SSDs
+ * 4k optimised & trim only works in 4k requests + 4k aligned
+ */
+ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "OCZ-VERTEX4*", "*" },
+ /*quirks*/DA_Q_4K
+ },
+ {
+ /*
* Samsung 830 Series SSDs
* 4k optimised & trim only works in 4k requests + 4k aligned
*/
@@ -1793,6 +1818,7 @@ dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method)
softc->delete_method = delete_method;
softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method);
+ softc->delete_func = da_delete_functions[delete_method];
if (softc->delete_method > DA_DELETE_DISABLE)
softc->disk->d_flags |= DISKFLAG_CANDELETE;
@@ -2152,7 +2178,7 @@ skipstate:
switch (softc->state) {
case DA_STATE_NORMAL:
{
- struct bio *bp, *bp1;
+ struct bio *bp;
uint8_t tag_code;
/* Execute immediate CCB if waiting. */
@@ -2172,237 +2198,13 @@ skipstate:
/* Run BIO_DELETE if not running yet. */
if (!softc->delete_running &&
(bp = bioq_first(&softc->delete_queue)) != NULL) {
- uint64_t lba;
- 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, 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));
- 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;
-
- /* Try to extend the previous range. */
- if (lba == lastlba) {
- 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]);
- 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(c, &buf[off + 8]);
- lba += c;
- totalcount += c;
- ranges++;
- count -= c;
- lastcount = c;
- }
- lastlba = lba;
- bp1 = bioq_first(&softc->delete_queue);
- if (bp1 == NULL ||
- ranges >= softc->unmap_max_ranges ||
- totalcount + bp1->bio_bcount /
- softc->params.secsize > softc->unmap_max_lba)
- break;
- } while (1);
- scsi_ulto2b(ranges * 16 + 6, &buf[0]);
- scsi_ulto2b(ranges * 16, &buf[2]);
-
- scsi_unmap(&start_ccb->csio,
- /*retries*/da_retry_count,
- /*cbfcnp*/dadone,
- /*tag_action*/MSG_SIMPLE_Q_TAG,
- /*byte2*/0,
- /*data_ptr*/ buf,
- /*dxfer_len*/ ranges * 16 + 8,
- /*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_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;
+ if (softc->delete_func != NULL) {
+ softc->delete_func(periph, start_ccb, bp);
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;
- bp1 = bp;
- do {
- bioq_remove(&softc->delete_queue, bp1);
- 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 > ws_max_blks)
- break;
- } while (1);
-
- scsi_write_same(&start_ccb->csio,
- /*retries*/da_retry_count,
- /*cbfcnp*/dadone,
- /*tag_action*/MSG_SIMPLE_Q_TAG,
- /*byte2*/softc->delete_method ==
- DA_DELETE_ZERO ? 0 : SWS_UNMAP,
- softc->delete_method ==
- DA_DELETE_WS16 ? 16 : 10,
- /*lba*/lba,
- /*block_count*/count,
- /*data_ptr*/ __DECONST(void *,
- zero_region),
- /*dxfer_len*/ softc->params.secsize,
- /*sense_len*/SSD_FULL_SIZE,
- da_default_timeout * 1000);
- start_ccb->ccb_h.ccb_state = DA_CCB_DELETE;
- goto out;
- } else {
- bioq_flush(&softc->delete_queue, NULL, 0);
- /* FALLTHROUGH */
- }
+ } else {
+ bioq_flush(&softc->delete_queue, NULL, 0);
+ /* FALLTHROUGH */
+ }
}
/* Run regular command. */
@@ -2695,6 +2497,240 @@ out:
}
}
+/*
+ * 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.
+ */
+static void
+da_delete_unmap(struct cam_periph *periph, union ccb *ccb, struct bio *bp)
+{
+ struct da_softc *softc = (struct da_softc *)periph->softc;;
+ struct bio *bp1;
+ uint8_t *buf = softc->unmap_buf;
+ uint64_t lba, lastlba = (uint64_t)-1;
+ uint64_t totalcount = 0;
+ uint64_t count;
+ uint32_t lastcount = 0, c;
+ 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));
+ 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;
+
+ /* Try to extend the previous range. */
+ if (lba == lastlba) {
+ 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]);
+ 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(c, &buf[off + 8]);
+ lba += c;
+ totalcount += c;
+ ranges++;
+ count -= c;
+ lastcount = c;
+ }
+ lastlba = lba;
+ bp1 = bioq_first(&softc->delete_queue);
+ if (bp1 == NULL || ranges >= softc->unmap_max_ranges ||
+ totalcount + bp1->bio_bcount /
+ softc->params.secsize > softc->unmap_max_lba)
+ break;
+ } while (1);
+ scsi_ulto2b(ranges * 16 + 6, &buf[0]);
+ scsi_ulto2b(ranges * 16, &buf[2]);
+
+ scsi_unmap(&ccb->csio,
+ /*retries*/da_retry_count,
+ /*cbfcnp*/dadone,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*byte2*/0,
+ /*data_ptr*/ buf,
+ /*dxfer_len*/ ranges * 16 + 8,
+ /*sense_len*/SSD_FULL_SIZE,
+ da_default_timeout * 1000);
+ ccb->ccb_h.ccb_state = DA_CCB_DELETE;
+}
+
+static void
+da_delete_trim(struct cam_periph *periph, union ccb *ccb, struct bio *bp)
+{
+ struct da_softc *softc = (struct da_softc *)periph->softc;
+ struct bio *bp1;
+ uint8_t *buf = softc->unmap_buf;
+ uint64_t lastlba = (uint64_t)-1;
+ uint64_t count;
+ uint64_t lba;
+ 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(&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);
+ ccb->ccb_h.ccb_state = DA_CCB_DELETE;
+}
+
+/*
+ * 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
+ */
+static void
+da_delete_ws(struct cam_periph *periph, union ccb *ccb, struct bio *bp)
+{
+ struct da_softc *softc;
+ struct bio *bp1;
+ uint64_t ws_max_blks;
+ uint64_t lba;
+ uint64_t count; /* forward compat with WS32 */
+
+ softc = (struct da_softc *)periph->softc;
+ ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize;
+ softc->delete_running = 1;
+ lba = bp->bio_pblkno;
+ count = 0;
+ bp1 = bp;
+ do {
+ bioq_remove(&softc->delete_queue, bp1);
+ 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 > ws_max_blks)
+ break;
+ } while (1);
+
+ scsi_write_same(&ccb->csio,
+ /*retries*/da_retry_count,
+ /*cbfcnp*/dadone,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*byte2*/softc->delete_method ==
+ DA_DELETE_ZERO ? 0 : SWS_UNMAP,
+ softc->delete_method == DA_DELETE_WS16 ? 16 : 10,
+ /*lba*/lba,
+ /*block_count*/count,
+ /*data_ptr*/ __DECONST(void *, zero_region),
+ /*dxfer_len*/ softc->params.secsize,
+ /*sense_len*/SSD_FULL_SIZE,
+ da_default_timeout * 1000);
+ ccb->ccb_h.ccb_state = DA_CCB_DELETE;
+}
+
static int
cmd6workaround(union ccb *ccb)
{
diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c
index 9269f36..609742b 100644
--- a/sys/cam/scsi/scsi_xpt.c
+++ b/sys/cam/scsi/scsi_xpt.c
@@ -1881,8 +1881,8 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
if ((work_ccb->cpi.hba_inquiry &
(PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) &&
!(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) &&
- !timevalisset(&request_ccb->ccb_h.path->bus->last_reset)) {
- reset_ccb = xpt_alloc_ccb_nowait();
+ !timevalisset(&request_ccb->ccb_h.path->bus->last_reset) &&
+ (reset_ccb = xpt_alloc_ccb_nowait()) != NULL) {
xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path,
CAM_PRIORITY_NONE);
reset_ccb->ccb_h.func_code = XPT_RESET_BUS;
OpenPOWER on IntegriCloud