summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2013-07-12 00:50:25 +0000
committerscottl <scottl@FreeBSD.org>2013-07-12 00:50:25 +0000
commit1a38eadae75759280e35b93ed5683f224eaed451 (patch)
tree22b0e9ab76bbebd44c9d7bf8dea25492c760c66e /sys/cam
parent7d9a4be39ec0ff9260a5edfd1c95c06f5f52ade1 (diff)
downloadFreeBSD-src-1a38eadae75759280e35b93ed5683f224eaed451.zip
FreeBSD-src-1a38eadae75759280e35b93ed5683f224eaed451.tar.gz
Refactor the various delete methods out of dastart(). Cleans up a bunch
of style and adds more modularity and clarity. Obtained from: Netflix MFC after: 3 days
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/scsi/scsi_da.c493
1 files changed, 262 insertions, 231 deletions
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 481567d..37b8356 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 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;
@@ -1801,6 +1818,10 @@ dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method)
softc->delete_method = delete_method;
softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method);
+ if (delete_method <= DA_DELETE_MAX)
+ softc->delete_func = da_delete_functions[delete_method];
+ else
+ softc->delete_func = NULL;
if (softc->delete_method > DA_DELETE_DISABLE)
softc->disk->d_flags |= DISKFLAG_CANDELETE;
@@ -2160,7 +2181,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. */
@@ -2180,237 +2201,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. */
@@ -2703,6 +2500,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)
{
OpenPOWER on IntegriCloud