summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/ata/ata_da.c8
-rw-r--r--sys/cam/cam_periph.c77
-rw-r--r--sys/cam/cam_xpt.c11
-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
8 files changed, 406 insertions, 278 deletions
diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c
index 721d5b5..6e9c462 100644
--- a/sys/cam/ata/ata_da.c
+++ b/sys/cam/ata/ata_da.c
@@ -396,6 +396,14 @@ static struct ada_quirk_entry ada_quirk_table[] =
},
{
/*
+ * OCZ Vertex 4 SSDs
+ * 4k optimised & trim only works in 4k requests + 4k aligned
+ */
+ { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX4*", "*" },
+ /*quirks*/ADA_Q_4K
+ },
+ {
+ /*
* Samsung 830 Series SSDs
* 4k optimised
*/
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index f3300db..ef0f5a7 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -75,7 +75,7 @@ static int camperiphscsistatuserror(union ccb *ccb,
int *openings,
u_int32_t *relsim_flags,
u_int32_t *timeout,
- int *print,
+ u_int32_t *action,
const char **action_string);
static int camperiphscsisenseerror(union ccb *ccb,
union ccb **orig_ccb,
@@ -84,7 +84,7 @@ static int camperiphscsisenseerror(union ccb *ccb,
int *openings,
u_int32_t *relsim_flags,
u_int32_t *timeout,
- int *print,
+ u_int32_t *action,
const char **action_string);
static int nperiph_drivers;
@@ -1274,7 +1274,7 @@ static int
camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb,
cam_flags camflags, u_int32_t sense_flags,
int *openings, u_int32_t *relsim_flags,
- u_int32_t *timeout, int *print, const char **action_string)
+ u_int32_t *timeout, u_int32_t *action, const char **action_string)
{
int error;
@@ -1293,7 +1293,7 @@ camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb,
openings,
relsim_flags,
timeout,
- print,
+ action,
action_string);
break;
case SCSI_STATUS_QUEUE_FULL:
@@ -1348,7 +1348,7 @@ camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb,
}
*timeout = 0;
error = ERESTART;
- *print = 0;
+ *action &= ~SSQ_PRINT_SENSE;
break;
}
/* FALLTHROUGH */
@@ -1380,7 +1380,7 @@ static int
camperiphscsisenseerror(union ccb *ccb, union ccb **orig,
cam_flags camflags, u_int32_t sense_flags,
int *openings, u_int32_t *relsim_flags,
- u_int32_t *timeout, int *print, const char **action_string)
+ u_int32_t *timeout, u_int32_t *action, const char **action_string)
{
struct cam_periph *periph;
union ccb *orig_ccb = ccb;
@@ -1403,7 +1403,7 @@ camperiphscsisenseerror(union ccb *ccb, union ccb **orig,
* imperitive that we don't violate this assumption.
*/
error = ERESTART;
- *print = 0;
+ *action &= ~SSQ_PRINT_SENSE;
} else {
scsi_sense_action err_action;
struct ccb_getdev cgd;
@@ -1575,7 +1575,7 @@ camperiphscsisenseerror(union ccb *ccb, union ccb **orig,
}
sense_error_done:
- *print = ((err_action & SSQ_PRINT_SENSE) != 0);
+ *action = err_action;
}
return (error);
}
@@ -1589,32 +1589,32 @@ int
cam_periph_error(union ccb *ccb, cam_flags camflags,
u_int32_t sense_flags, union ccb *save_ccb)
{
- union ccb *orig_ccb;
+ struct cam_path *newpath;
+ union ccb *orig_ccb, *scan_ccb;
struct cam_periph *periph;
const char *action_string;
cam_status status;
- int frozen, error, openings, print, lost_device;
- int error_code, sense_key, asc, ascq;
- u_int32_t relsim_flags, timeout;
+ int frozen, error, openings;
+ u_int32_t action, relsim_flags, timeout;
- print = 1;
+ action = SSQ_PRINT_SENSE;
periph = xpt_path_periph(ccb->ccb_h.path);
action_string = NULL;
status = ccb->ccb_h.status;
frozen = (status & CAM_DEV_QFRZN) != 0;
status &= CAM_STATUS_MASK;
- openings = relsim_flags = timeout = lost_device = 0;
+ openings = relsim_flags = timeout = 0;
orig_ccb = ccb;
switch (status) {
case CAM_REQ_CMP:
error = 0;
- print = 0;
+ action &= ~SSQ_PRINT_SENSE;
break;
case CAM_SCSI_STATUS_ERROR:
error = camperiphscsistatuserror(ccb, &orig_ccb,
camflags, sense_flags, &openings, &relsim_flags,
- &timeout, &print, &action_string);
+ &timeout, &action, &action_string);
break;
case CAM_AUTOSENSE_FAIL:
error = EIO; /* we have to kill the command */
@@ -1645,8 +1645,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
/* FALLTHROUGH */
case CAM_DEV_NOT_THERE:
error = ENXIO;
- print = 0;
- lost_device = 1;
+ action = SSQ_LOST;
break;
case CAM_REQ_INVALID:
case CAM_PATH_INVALID:
@@ -1677,7 +1676,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
action_string = "Retry was blocked";
} else {
error = ERESTART;
- print = 0;
+ action &= ~SSQ_PRINT_SENSE;
}
break;
case CAM_RESRC_UNAVAIL:
@@ -1716,12 +1715,12 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
if ((sense_flags & SF_PRINT_ALWAYS) ||
CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO))
- print = 1;
+ action |= SSQ_PRINT_SENSE;
else if (sense_flags & SF_NO_PRINT)
- print = 0;
- if (print)
+ action &= ~SSQ_PRINT_SENSE;
+ if ((action & SSQ_PRINT_SENSE) != 0)
cam_error_print(orig_ccb, CAM_ESF_ALL, CAM_EPF_ALL);
- if (error != 0 && print) {
+ if (error != 0 && (action & SSQ_PRINT_SENSE) != 0) {
if (error != ERESTART) {
if (action_string == NULL)
action_string = "Unretryable error";
@@ -1733,8 +1732,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
xpt_print(ccb->ccb_h.path, "Retrying command\n");
}
- if (lost_device) {
- struct cam_path *newpath;
+ if ((action & SSQ_LOST) != 0) {
lun_id_t lun_id;
/*
@@ -1743,10 +1741,10 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
* then we only get rid of the device(s) specified by the
* path in the original CCB.
*/
- if (status == CAM_DEV_NOT_THERE)
- lun_id = xpt_path_lun_id(ccb->ccb_h.path);
- else
+ if (status == CAM_SEL_TIMEOUT)
lun_id = CAM_LUN_WILDCARD;
+ else
+ lun_id = xpt_path_lun_id(ccb->ccb_h.path);
/* Should we do more if we can't create the path?? */
if (xpt_create_path(&newpath, periph,
@@ -1761,12 +1759,29 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
xpt_async(AC_LOST_DEVICE, newpath, NULL);
xpt_free_path(newpath);
}
+ }
/* Broadcast UNIT ATTENTIONs to all periphs. */
- } else if (scsi_extract_sense_ccb(ccb,
- &error_code, &sense_key, &asc, &ascq) &&
- sense_key == SSD_KEY_UNIT_ATTENTION) {
+ if ((action & SSQ_UA) != 0)
xpt_async(AC_UNIT_ATTENTION, orig_ccb->ccb_h.path, orig_ccb);
+
+ /* Rescan target on "Reported LUNs data has changed" */
+ if ((action & SSQ_RESCAN) != 0) {
+ if (xpt_create_path(&newpath, NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
+
+ scan_ccb = xpt_alloc_ccb_nowait();
+ if (scan_ccb != NULL) {
+ scan_ccb->ccb_h.path = newpath;
+ scan_ccb->ccb_h.func_code = XPT_SCAN_TGT;
+ scan_ccb->crcn.flags = 0;
+ xpt_rescan(scan_ccb);
+ } else
+ xpt_print(newpath,
+ "Can't allocate CCB to rescan target\n");
+ }
}
/* Attempt a retry */
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index 3511265..8f2c93e 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -3900,10 +3900,13 @@ xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus)
xpt_async(AC_PATH_REGISTERED, path, &cpi);
/* Initiate bus rescan. */
scan_ccb = xpt_alloc_ccb_nowait();
- scan_ccb->ccb_h.path = path;
- scan_ccb->ccb_h.func_code = XPT_SCAN_BUS;
- scan_ccb->crcn.flags = 0;
- xpt_rescan(scan_ccb);
+ if (scan_ccb != NULL) {
+ scan_ccb->ccb_h.path = path;
+ scan_ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ scan_ccb->crcn.flags = 0;
+ xpt_rescan(scan_ccb);
+ } else
+ xpt_print(path, "Can't allocate CCB to scan bus\n");
} else
xpt_free_path(path);
return (CAM_SUCCESS);
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