diff options
Diffstat (limited to 'sys/cam')
-rw-r--r-- | sys/cam/ata/ata_da.c | 8 | ||||
-rw-r--r-- | sys/cam/cam_periph.c | 77 | ||||
-rw-r--r-- | sys/cam/cam_xpt.c | 11 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 9 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 3 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_ch.c | 74 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 498 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_xpt.c | 4 |
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; |