diff options
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 84 |
1 files changed, 71 insertions, 13 deletions
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index 977ac8d..1cae0fd 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -123,7 +123,8 @@ typedef enum { DA_Q_NO_RC16 = 0x10, DA_Q_NO_UNMAP = 0x20, DA_Q_RETRY_BUSY = 0x40, - DA_Q_SMR_DM = 0x80 + DA_Q_SMR_DM = 0x80, + DA_Q_STRICT_UNMAP = 0x100 } da_quirks; #define DA_Q_BIT_STRING \ @@ -135,7 +136,8 @@ typedef enum { "\005NO_RC16" \ "\006NO_UNMAP" \ "\007RETRY_BUSY" \ - "\008SMR_DM" + "\010SMR_DM" \ + "\011STRICT_UNMAP" typedef enum { DA_CCB_PROBE_RC = 0x01, @@ -310,6 +312,8 @@ struct da_softc { u_int maxio; uint32_t unmap_max_ranges; uint32_t unmap_max_lba; /* Max LBAs in UNMAP req */ + uint32_t unmap_gran; + uint32_t unmap_gran_align; uint64_t ws_max_blks; da_delete_methods delete_method_pref; da_delete_methods delete_method; @@ -468,9 +472,10 @@ static struct da_quirk_entry da_quirk_table[] = /* * VMware returns BUSY status when storage has transient * connectivity problems, so better wait. + * Also VMware returns odd errors on misaligned UNMAPs. */ {T_DIRECT, SIP_MEDIA_FIXED, "VMware*", "*", "*"}, - /*quirks*/ DA_Q_RETRY_BUSY + /*quirks*/ DA_Q_RETRY_BUSY | DA_Q_STRICT_UNMAP }, /* USB mass storage devices supported by umass(4) */ { @@ -2395,6 +2400,8 @@ daregister(struct cam_periph *periph, void *arg) softc->flags |= DA_FLAG_PACK_REMOVABLE; softc->unmap_max_ranges = UNMAP_MAX_RANGES; softc->unmap_max_lba = UNMAP_RANGE_MAX; + softc->unmap_gran = 0; + softc->unmap_gran_align = 0; softc->ws_max_blks = WS16_MAX_BLKS; softc->trim_max_ranges = ATA_TRIM_MAX_RANGES; softc->rotating = 1; @@ -3504,11 +3511,11 @@ 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; + struct scsi_unmap_desc *d = (void *)&buf[UNMAP_HEAD_SIZE]; uint64_t lba, lastlba = (uint64_t)-1; uint64_t totalcount = 0; uint64_t count; - uint32_t lastcount = 0, c; - uint32_t off, ranges = 0; + uint32_t c, lastcount = 0, ranges = 0; /* * Currently this doesn't take the UNMAP @@ -3541,13 +3548,39 @@ da_delete_unmap(struct cam_periph *periph, union ccb *ccb, struct bio *bp) /* Try to extend the previous range. */ if (lba == lastlba) { c = omin(count, UNMAP_RANGE_MAX - lastcount); + lastlba += c; lastcount += c; - off = ((ranges - 1) * UNMAP_RANGE_SIZE) + - UNMAP_HEAD_SIZE; - scsi_ulto4b(lastcount, &buf[off + 8]); + scsi_ulto4b(lastcount, d[ranges - 1].length); count -= c; - lba +=c; + lba += c; totalcount += c; + } else if ((softc->quirks & DA_Q_STRICT_UNMAP) && + softc->unmap_gran != 0) { + /* Align length of the previous range. */ + if ((c = lastcount % softc->unmap_gran) != 0) { + if (lastcount <= c) { + totalcount -= lastcount; + lastlba = (uint64_t)-1; + lastcount = 0; + ranges--; + } else { + totalcount -= c; + lastlba -= c; + lastcount -= c; + scsi_ulto4b(lastcount, d[ranges - 1].length); + } + } + /* Align beginning of the new range. */ + c = (lba - softc->unmap_gran_align) % softc->unmap_gran; + if (c != 0) { + c = softc->unmap_gran - c; + if (count <= c) { + count = 0; + } else { + lba += c; + count -= c; + } + } } while (count > 0) { @@ -3562,16 +3595,15 @@ da_delete_unmap(struct cam_periph *periph, union ccb *ccb, struct bio *bp) 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]); + scsi_u64to8b(lba, d[ranges].lba); + scsi_ulto4b(c, d[ranges].length); lba += c; totalcount += c; ranges++; count -= c; + lastlba = lba; lastcount = c; } - lastlba = lba; bp1 = cam_iosched_next_trim(softc->cam_iosched); if (bp1 == NULL) break; @@ -3582,6 +3614,16 @@ da_delete_unmap(struct cam_periph *periph, union ccb *ccb, struct bio *bp) break; } } while (1); + + /* Align length of the last range. */ + if ((softc->quirks & DA_Q_STRICT_UNMAP) && softc->unmap_gran != 0 && + (c = lastcount % softc->unmap_gran) != 0) { + if (lastcount <= c) + ranges--; + else + scsi_ulto4b(lastcount - c, d[ranges - 1].length); + } + scsi_ulto2b(ranges * 16 + 6, &buf[0]); scsi_ulto2b(ranges * 16, &buf[2]); @@ -4454,6 +4496,10 @@ dadone(struct cam_periph *periph, union ccb *done_ccb) block_limits->max_unmap_lba_cnt); uint32_t max_unmap_blk_cnt = scsi_4btoul( block_limits->max_unmap_blk_cnt); + uint32_t unmap_gran = scsi_4btoul( + block_limits->opt_unmap_grain); + uint32_t unmap_gran_align = scsi_4btoul( + block_limits->unmap_grain_align); uint64_t ws_max_blks = scsi_8btou64( block_limits->max_write_same_length); @@ -4471,6 +4517,14 @@ dadone(struct cam_periph *periph, union ccb *done_ccb) softc->unmap_max_lba = max_unmap_lba_cnt; softc->unmap_max_ranges = min(max_unmap_blk_cnt, UNMAP_MAX_RANGES); + if (unmap_gran > 1) { + softc->unmap_gran = unmap_gran; + if (unmap_gran_align & 0x80000000) { + softc->unmap_gran_align = + unmap_gran_align & + 0x7fffffff; + } + } } else { /* * Unexpected UNMAP limits which means the @@ -5380,6 +5434,10 @@ dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector, } else if (softc->quirks & DA_Q_4K) { dp->stripesize = 4096; dp->stripeoffset = 0; + } else if (softc->unmap_gran != 0) { + dp->stripesize = block_len * softc->unmap_gran; + dp->stripeoffset = (dp->stripesize - block_len * + softc->unmap_gran_align) % dp->stripesize; } else { dp->stripesize = 0; dp->stripeoffset = 0; |