svn commit: r251457 - stable/8/sys/cam/scsi
Steven Hartland
smh at FreeBSD.org
Thu Jun 6 09:45:23 UTC 2013
Author: smh
Date: Thu Jun 6 09:45:22 2013
New Revision: 251457
URL: http://svnweb.freebsd.org/changeset/base/251457
Log:
MFC r230053:
BIO_DELETE for SCSI da devices
MFC r239655:
Fix scsi_da's BIO_DELETE->SCSI_UNMAP translation
Modified:
stable/8/sys/cam/scsi/scsi_all.c
stable/8/sys/cam/scsi/scsi_all.h
stable/8/sys/cam/scsi/scsi_da.c
Directory Properties:
stable/8/sys/ (props changed)
stable/8/sys/cam/ (props changed)
Modified: stable/8/sys/cam/scsi/scsi_all.c
==============================================================================
--- stable/8/sys/cam/scsi/scsi_all.c Thu Jun 6 09:23:21 2013 (r251456)
+++ stable/8/sys/cam/scsi/scsi_all.c Thu Jun 6 09:45:22 2013 (r251457)
@@ -361,6 +361,8 @@ static struct op_table_entry scsi_op_cod
{ 0x40, D | T | L | P | W | R | O | M | S | C, "CHANGE DEFINITION" },
/* 41 O WRITE SAME(10) */
{ 0x41, D, "WRITE SAME(10)" },
+ /* 42 O UNMAP */
+ { 0x42, D, "UNMAP" },
/* 42 O READ SUB-CHANNEL */
{ 0x42, R, "READ SUB-CHANNEL" },
/* 43 O READ TOC/PMA/ATIP */
@@ -4318,6 +4320,104 @@ scsi_read_write(struct ccb_scsiio *csio,
timeout);
}
+void
+scsi_write_same(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int8_t byte2,
+ int minimum_cmd_size, u_int64_t lba, u_int32_t block_count,
+ u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len,
+ u_int32_t timeout)
+{
+ u_int8_t cdb_len;
+ if ((minimum_cmd_size < 16) &&
+ ((block_count & 0xffff) == block_count) &&
+ ((lba & 0xffffffff) == lba)) {
+ /*
+ * Need a 10 byte cdb.
+ */
+ struct scsi_write_same_10 *scsi_cmd;
+
+ scsi_cmd = (struct scsi_write_same_10 *)&csio->cdb_io.cdb_bytes;
+ scsi_cmd->opcode = WRITE_SAME_10;
+ scsi_cmd->byte2 = byte2;
+ scsi_ulto4b(lba, scsi_cmd->addr);
+ scsi_cmd->group = 0;
+ scsi_ulto2b(block_count, scsi_cmd->length);
+ scsi_cmd->control = 0;
+ cdb_len = sizeof(*scsi_cmd);
+
+ CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE,
+ ("10byte: %x%x%x%x:%x%x: %d\n", scsi_cmd->addr[0],
+ scsi_cmd->addr[1], scsi_cmd->addr[2],
+ scsi_cmd->addr[3], scsi_cmd->length[0],
+ scsi_cmd->length[1], dxfer_len));
+ } else {
+ /*
+ * 16 byte CDB. We'll only get here if the LBA is larger
+ * than 2^32, or if the user asks for a 16 byte command.
+ */
+ struct scsi_write_same_16 *scsi_cmd;
+
+ scsi_cmd = (struct scsi_write_same_16 *)&csio->cdb_io.cdb_bytes;
+ scsi_cmd->opcode = WRITE_SAME_16;
+ scsi_cmd->byte2 = byte2;
+ scsi_u64to8b(lba, scsi_cmd->addr);
+ scsi_ulto4b(block_count, scsi_cmd->length);
+ scsi_cmd->group = 0;
+ scsi_cmd->control = 0;
+ cdb_len = sizeof(*scsi_cmd);
+
+ CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE,
+ ("16byte: %x%x%x%x%x%x%x%x:%x%x%x%x: %d\n",
+ scsi_cmd->addr[0], scsi_cmd->addr[1],
+ scsi_cmd->addr[2], scsi_cmd->addr[3],
+ scsi_cmd->addr[4], scsi_cmd->addr[5],
+ scsi_cmd->addr[6], scsi_cmd->addr[7],
+ scsi_cmd->length[0], scsi_cmd->length[1],
+ scsi_cmd->length[2], scsi_cmd->length[3],
+ dxfer_len));
+ }
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_OUT,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ cdb_len,
+ timeout);
+}
+
+void
+scsi_unmap(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int8_t byte2,
+ u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len,
+ u_int32_t timeout)
+{
+ struct scsi_unmap *scsi_cmd;
+
+ scsi_cmd = (struct scsi_unmap *)&csio->cdb_io.cdb_bytes;
+ scsi_cmd->opcode = UNMAP;
+ scsi_cmd->byte2 = byte2;
+ scsi_ulto4b(0, scsi_cmd->reserved);
+ scsi_cmd->group = 0;
+ scsi_ulto2b(dxfer_len, scsi_cmd->length);
+ scsi_cmd->control = 0;
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_OUT,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
void
scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
Modified: stable/8/sys/cam/scsi/scsi_all.h
==============================================================================
--- stable/8/sys/cam/scsi/scsi_all.h Thu Jun 6 09:23:21 2013 (r251456)
+++ stable/8/sys/cam/scsi/scsi_all.h Thu Jun 6 09:45:22 2013 (r251457)
@@ -512,6 +512,41 @@ struct scsi_rw_16
u_int8_t control;
};
+struct scsi_write_same_10
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SWS_LBDATA 0x02
+#define SWS_PBDATA 0x04
+#define SWS_UNMAP 0x08
+#define SWS_ANCHOR 0x10
+ uint8_t addr[4];
+ uint8_t group;
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_write_same_16
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t addr[8];
+ uint8_t length[4];
+ uint8_t group;
+ uint8_t control;
+};
+
+struct scsi_unmap
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SU_ANCHOR 0x01
+ uint8_t reserved[4];
+ uint8_t group;
+ uint8_t length[2];
+ uint8_t control;
+};
+
struct scsi_start_stop_unit
{
u_int8_t opcode;
@@ -595,6 +630,8 @@ struct ata_pass_16 {
#define WRITE_BUFFER 0x3B
#define READ_BUFFER 0x3C
#define CHANGE_DEFINITION 0x40
+#define WRITE_SAME_10 0x41
+#define UNMAP 0x42
#define LOG_SELECT 0x4C
#define LOG_SENSE 0x4D
#define MODE_SELECT_10 0x55
@@ -602,6 +639,7 @@ struct ata_pass_16 {
#define ATA_PASS_16 0x85
#define READ_16 0x88
#define WRITE_16 0x8A
+#define WRITE_SAME_16 0x93
#define SERVICE_ACTION_IN 0x9E
#define REPORT_LUNS 0xA0
#define ATA_PASS_12 0xA1
@@ -1287,6 +1325,20 @@ void scsi_read_write(struct ccb_scsiio *
u_int32_t dxfer_len, u_int8_t sense_len,
u_int32_t timeout);
+void scsi_write_same(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int8_t byte2,
+ int minimum_cmd_size, u_int64_t lba,
+ u_int32_t block_count, u_int8_t *data_ptr,
+ u_int32_t dxfer_len, u_int8_t sense_len,
+ u_int32_t timeout);
+
+void scsi_unmap(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int8_t byte2,
+ u_int8_t *data_ptr, u_int16_t dxfer_len,
+ u_int8_t sense_len, u_int32_t timeout);
+
void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, int start, int load_eject,
Modified: stable/8/sys/cam/scsi/scsi_da.c
==============================================================================
--- stable/8/sys/cam/scsi/scsi_da.c Thu Jun 6 09:23:21 2013 (r251456)
+++ stable/8/sys/cam/scsi/scsi_da.c Thu Jun 6 09:45:22 2013 (r251457)
@@ -81,8 +81,7 @@ typedef enum {
DA_FLAG_RETRY_UA = 0x080,
DA_FLAG_OPEN = 0x100,
DA_FLAG_SCTX_INIT = 0x200,
- DA_FLAG_CAN_RC16 = 0x400,
- DA_FLAG_CAN_LBPME = 0x800
+ DA_FLAG_CAN_RC16 = 0x400
} da_flags;
typedef enum {
@@ -99,10 +98,24 @@ typedef enum {
DA_CCB_BUFFER_IO = 0x03,
DA_CCB_WAITING = 0x04,
DA_CCB_DUMP = 0x05,
+ DA_CCB_DELETE = 0x06,
DA_CCB_TYPE_MASK = 0x0F,
DA_CCB_RETRY_UA = 0x10
} da_ccb_state;
+typedef enum {
+ DA_DELETE_NONE,
+ DA_DELETE_DISABLE,
+ DA_DELETE_ZERO,
+ DA_DELETE_WS10,
+ DA_DELETE_WS16,
+ DA_DELETE_UNMAP,
+ DA_DELETE_MAX = DA_DELETE_UNMAP
+} da_delete_methods;
+
+static const char *da_delete_method_names[] =
+ { "NONE", "DISABLE", "ZERO", "WS10", "WS16", "UNMAP" };
+
/* Offsets into our private area for storing information */
#define ccb_state ppriv_field0
#define ccb_bp ppriv_ptr1
@@ -117,8 +130,12 @@ struct disk_params {
u_int stripeoffset;
};
+#define UNMAP_MAX_RANGES 512
+
struct da_softc {
struct bio_queue_head bio_queue;
+ struct bio_queue_head delete_queue;
+ struct bio_queue_head delete_run_queue;
SLIST_ENTRY(da_softc) links;
LIST_HEAD(, ccb_hdr) pending_ccbs;
da_state state;
@@ -128,6 +145,10 @@ struct da_softc {
int error_inject;
int ordered_tag_count;
int outstanding_cmds;
+ int unmap_max_ranges;
+ int unmap_max_lba;
+ int delete_running;
+ da_delete_methods delete_method;
struct disk_params params;
struct disk *disk;
union ccb saved_ccb;
@@ -136,6 +157,7 @@ struct da_softc {
struct sysctl_oid *sysctl_tree;
struct callout sendordered_c;
uint64_t wwpn;
+ uint8_t unmap_buf[UNMAP_MAX_RANGES * 16 + 8];
};
struct da_quirk_entry {
@@ -833,6 +855,7 @@ static void daasync(void *callback_arg,
struct cam_path *path, void *arg);
static void dasysctlinit(void *context, int pending);
static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS);
+static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS);
static periph_ctor_t daregister;
static periph_dtor_t dacleanup;
static periph_start_t dastart;
@@ -952,6 +975,10 @@ daopen(struct disk *dp)
softc->disk->d_fwheads = softc->params.heads;
softc->disk->d_devstat->block_size = softc->params.secsize;
softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE;
+ if (softc->delete_method > DA_DELETE_DISABLE)
+ softc->disk->d_flags |= DISKFLAG_CANDELETE;
+ else
+ softc->disk->d_flags &= ~DISKFLAG_CANDELETE;
if ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0 &&
(softc->quirks & DA_Q_NO_PREVENT) == 0)
@@ -1051,6 +1078,26 @@ daclose(struct disk *dp)
return (0);
}
+static void
+daschedule(struct cam_periph *periph)
+{
+ struct da_softc *softc = (struct da_softc *)periph->softc;
+ uint32_t prio;
+
+ /* Check if cam_periph_getccb() was called. */
+ prio = periph->immediate_priority;
+
+ /* Check if we have more work to do. */
+ if (bioq_first(&softc->bio_queue) ||
+ (!softc->delete_running && bioq_first(&softc->delete_queue))) {
+ prio = CAM_PRIORITY_NORMAL;
+ }
+
+ /* Schedule CCB if any of above is true. */
+ if (prio != CAM_PRIORITY_NONE)
+ xpt_schedule(periph, prio);
+}
+
/*
* Actually translate the requested transfer into one the physical driver
* can understand. The transfer is described by a buf and will include
@@ -1085,12 +1132,18 @@ dastrategy(struct bio *bp)
/*
* Place it in the queue of disk activities for this disk
*/
- bioq_disksort(&softc->bio_queue, bp);
+ if (bp->bio_cmd == BIO_DELETE) {
+ if (bp->bio_bcount == 0)
+ biodone(bp);
+ else
+ bioq_disksort(&softc->delete_queue, bp);
+ } else
+ bioq_disksort(&softc->bio_queue, bp);
/*
* Schedule ourselves for performing the work.
*/
- xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+ daschedule(periph);
cam_periph_unlock(periph);
return;
@@ -1234,6 +1287,7 @@ daoninvalidate(struct cam_periph *periph
* with XPT_ABORT_CCB.
*/
bioq_flush(&softc->bio_queue, NULL, ENXIO);
+ bioq_flush(&softc->delete_queue, NULL, ENXIO);
disk_gone(softc->disk);
xpt_print(periph->path, "lost device - %d outstanding, %d refs\n",
@@ -1365,6 +1419,10 @@ dasysctlinit(void *context, int pending)
* the fly.
*/
SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+ OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW,
+ &softc->delete_method, 0, dadeletemethodsysctl, "A",
+ "BIO_DELETE execution method");
+ SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW,
&softc->minimum_cmd_size, 0, dacmdsizesysctl, "I",
"Minimum CDB size");
@@ -1428,6 +1486,32 @@ dacmdsizesysctl(SYSCTL_HANDLER_ARGS)
return (0);
}
+static int
+dadeletemethodsysctl(SYSCTL_HANDLER_ARGS)
+{
+ char buf[16];
+ int error;
+ const char *p;
+ int i, value;
+
+ value = *(int *)arg1;
+ if (value < 0 || value > DA_DELETE_MAX)
+ p = "UNKNOWN";
+ else
+ p = da_delete_method_names[value];
+ strncpy(buf, p, sizeof(buf));
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ for (i = 0; i <= DA_DELETE_MAX; i++) {
+ if (strcmp(buf, da_delete_method_names[i]) != 0)
+ continue;
+ *(int *)arg1 = i;
+ return (0);
+ }
+ return (EINVAL);
+}
+
static cam_status
daregister(struct cam_periph *periph, void *arg)
{
@@ -1460,8 +1544,12 @@ daregister(struct cam_periph *periph, vo
LIST_INIT(&softc->pending_ccbs);
softc->state = DA_STATE_PROBE;
bioq_init(&softc->bio_queue);
+ bioq_init(&softc->delete_queue);
+ bioq_init(&softc->delete_run_queue);
if (SID_IS_REMOVABLE(&cgd->inq_data))
softc->flags |= DA_FLAG_PACK_REMOVABLE;
+ softc->unmap_max_ranges = UNMAP_MAX_RANGES;
+ softc->unmap_max_lba = 1024*1024*2;
periph->softc = softc;
@@ -1599,13 +1687,10 @@ dastart(struct cam_periph *periph, union
switch (softc->state) {
case DA_STATE_NORMAL:
{
- /* Pull a buffer from the queue and get going on it */
- struct bio *bp;
+ struct bio *bp, *bp1;
+ uint8_t tag_code;
- /*
- * See if there is a buf with work for us to do..
- */
- bp = bioq_first(&softc->bio_queue);
+ /* Execute immediate CCB if waiting. */
if (periph->immediate_priority <= periph->pinfo.priority) {
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("queuing for immediate ccb\n"));
@@ -1614,84 +1699,186 @@ dastart(struct cam_periph *periph, union
periph_links.sle);
periph->immediate_priority = CAM_PRIORITY_NONE;
wakeup(&periph->ccb_list);
- } else if (bp == NULL) {
- xpt_release_ccb(start_ccb);
- } else {
- u_int8_t tag_code;
+ /* May have more work to do, so ensure we stay scheduled */
+ daschedule(periph);
+ break;
+ }
- bioq_remove(&softc->bio_queue, bp);
+ /* Run BIO_DELETE if not running yet. */
+ if (!softc->delete_running &&
+ (bp = bioq_first(&softc->delete_queue)) != NULL) {
+ uint64_t lba;
+ u_int count;
+
+ if (softc->delete_method == DA_DELETE_UNMAP) {
+ uint8_t *buf = softc->unmap_buf;
+ uint64_t lastlba = (uint64_t)-1;
+ uint32_t lastcount = 0;
+ int blocks = 0, off, ranges = 0;
+
+ 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) {
+ lastcount += count;
+ off = (ranges - 1) * 16 + 8;
+ scsi_ulto4b(lastcount, &buf[off + 8]);
+ } else if (count > 0) {
+ off = ranges * 16 + 8;
+ scsi_u64to8b(lba, &buf[off + 0]);
+ scsi_ulto4b(count, &buf[off + 8]);
+ lastcount = count;
+ ranges++;
+ }
+ blocks += count;
+ lastlba = lba + count;
+ bp1 = bioq_first(&softc->delete_queue);
+ if (bp1 == NULL ||
+ ranges >= softc->unmap_max_ranges ||
+ blocks + 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_ZERO ||
+ softc->delete_method == DA_DELETE_WS10 ||
+ softc->delete_method == DA_DELETE_WS16) {
+ 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;
+ bp1 = bioq_first(&softc->delete_queue);
+ if (bp1 == NULL ||
+ lba + count != bp1->bio_pblkno ||
+ count + bp1->bio_bcount /
+ softc->params.secsize > 0xffff)
+ 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 */
+ }
+ }
- if ((bp->bio_flags & BIO_ORDERED) != 0
- || (softc->flags & DA_FLAG_NEED_OTAG) != 0) {
- softc->flags &= ~DA_FLAG_NEED_OTAG;
- softc->ordered_tag_count++;
- tag_code = MSG_ORDERED_Q_TAG;
- } else {
- tag_code = MSG_SIMPLE_Q_TAG;
- }
- switch (bp->bio_cmd) {
- case BIO_READ:
- case BIO_WRITE:
- scsi_read_write(&start_ccb->csio,
- /*retries*/da_retry_count,
- /*cbfcnp*/dadone,
- /*tag_action*/tag_code,
- /*read_op*/bp->bio_cmd
- == BIO_READ,
- /*byte2*/0,
- softc->minimum_cmd_size,
- /*lba*/bp->bio_pblkno,
- /*block_count*/bp->bio_bcount /
- softc->params.secsize,
- /*data_ptr*/ bp->bio_data,
- /*dxfer_len*/ bp->bio_bcount,
- /*sense_len*/SSD_FULL_SIZE,
- da_default_timeout * 1000);
- break;
- case BIO_FLUSH:
- /*
- * BIO_FLUSH doesn't currently communicate
- * range data, so we synchronize the cache
- * over the whole disk. We also force
- * ordered tag semantics the flush applies
- * to all previously queued I/O.
- */
- scsi_synchronize_cache(&start_ccb->csio,
- /*retries*/1,
- /*cbfcnp*/dadone,
- MSG_ORDERED_Q_TAG,
- /*begin_lba*/0,
- /*lb_count*/0,
- SSD_FULL_SIZE,
- da_default_timeout*1000);
- break;
- }
- start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO;
+ /* Run regular command. */
+ bp = bioq_takefirst(&softc->bio_queue);
+ if (bp == NULL) {
+ xpt_release_ccb(start_ccb);
+ break;
+ }
+
+ if ((bp->bio_flags & BIO_ORDERED) != 0 ||
+ (softc->flags & DA_FLAG_NEED_OTAG) != 0) {
+ softc->flags &= ~DA_FLAG_NEED_OTAG;
+ softc->ordered_tag_count++;
+ tag_code = MSG_ORDERED_Q_TAG;
+ } else {
+ tag_code = MSG_SIMPLE_Q_TAG;
+ }
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ case BIO_WRITE:
+ scsi_read_write(&start_ccb->csio,
+ /*retries*/da_retry_count,
+ /*cbfcnp*/dadone,
+ /*tag_action*/tag_code,
+ /*read_op*/bp->bio_cmd
+ == BIO_READ,
+ /*byte2*/0,
+ softc->minimum_cmd_size,
+ /*lba*/bp->bio_pblkno,
+ /*block_count*/bp->bio_bcount /
+ softc->params.secsize,
+ /*data_ptr*/ bp->bio_data,
+ /*dxfer_len*/ bp->bio_bcount,
+ /*sense_len*/SSD_FULL_SIZE,
+ da_default_timeout * 1000);
+ break;
+ case BIO_FLUSH:
/*
- * Block out any asyncronous callbacks
- * while we touch the pending ccb list.
+ * BIO_FLUSH doesn't currently communicate
+ * range data, so we synchronize the cache
+ * over the whole disk. We also force
+ * ordered tag semantics the flush applies
+ * to all previously queued I/O.
*/
- LIST_INSERT_HEAD(&softc->pending_ccbs,
- &start_ccb->ccb_h, periph_links.le);
- softc->outstanding_cmds++;
-
- /* We expect a unit attention from this device */
- if ((softc->flags & DA_FLAG_RETRY_UA) != 0) {
- start_ccb->ccb_h.ccb_state |= DA_CCB_RETRY_UA;
- softc->flags &= ~DA_FLAG_RETRY_UA;
- }
-
- start_ccb->ccb_h.ccb_bp = bp;
- bp = bioq_first(&softc->bio_queue);
-
- xpt_action(start_ccb);
+ scsi_synchronize_cache(&start_ccb->csio,
+ /*retries*/1,
+ /*cbfcnp*/dadone,
+ MSG_ORDERED_Q_TAG,
+ /*begin_lba*/0,
+ /*lb_count*/0,
+ SSD_FULL_SIZE,
+ da_default_timeout*1000);
+ break;
}
-
- if (bp != NULL) {
- /* Have more work to do, so ensure we stay scheduled */
- xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+ start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO;
+
+out:
+ /*
+ * Block out any asyncronous callbacks
+ * while we touch the pending ccb list.
+ */
+ LIST_INSERT_HEAD(&softc->pending_ccbs,
+ &start_ccb->ccb_h, periph_links.le);
+ softc->outstanding_cmds++;
+
+ /* We expect a unit attention from this device */
+ if ((softc->flags & DA_FLAG_RETRY_UA) != 0) {
+ start_ccb->ccb_h.ccb_state |= DA_CCB_RETRY_UA;
+ softc->flags &= ~DA_FLAG_RETRY_UA;
}
+
+ start_ccb->ccb_h.ccb_bp = bp;
+ xpt_action(start_ccb);
+
+ /* May have more work to do, so ensure we stay scheduled */
+ daschedule(periph);
break;
}
case DA_STATE_PROBE:
@@ -1757,9 +1944,42 @@ cmd6workaround(union ccb *ccb)
struct scsi_rw_10 *cmd10;
struct da_softc *softc;
u_int8_t *cdb;
+ struct bio *bp;
int frozen;
cdb = ccb->csio.cdb_io.cdb_bytes;
+ softc = (struct da_softc *)xpt_path_periph(ccb->ccb_h.path)->softc;
+
+ if (ccb->ccb_h.ccb_state == DA_CCB_DELETE) {
+ if (softc->delete_method == DA_DELETE_UNMAP) {
+ xpt_print(ccb->ccb_h.path, "UNMAP is not supported, "
+ "switching to WRITE SAME(16) with UNMAP.\n");
+ softc->delete_method = DA_DELETE_WS16;
+ } else if (softc->delete_method == DA_DELETE_WS16) {
+ xpt_print(ccb->ccb_h.path,
+ "WRITE SAME(16) with UNMAP is not supported, "
+ "disabling BIO_DELETE.\n");
+ softc->delete_method = DA_DELETE_DISABLE;
+ } else if (softc->delete_method == DA_DELETE_WS10) {
+ xpt_print(ccb->ccb_h.path,
+ "WRITE SAME(10) with UNMAP is not supported, "
+ "disabling BIO_DELETE.\n");
+ softc->delete_method = DA_DELETE_DISABLE;
+ } else if (softc->delete_method == DA_DELETE_ZERO) {
+ xpt_print(ccb->ccb_h.path,
+ "WRITE SAME(10) is not supported, "
+ "disabling BIO_DELETE.\n");
+ softc->delete_method = DA_DELETE_DISABLE;
+ } else
+ softc->delete_method = DA_DELETE_DISABLE;
+ while ((bp = bioq_takefirst(&softc->delete_run_queue))
+ != NULL)
+ bioq_disksort(&softc->delete_queue, bp);
+ bioq_insert_tail(&softc->delete_queue,
+ (struct bio *)ccb->ccb_h.ccb_bp);
+ ccb->ccb_h.ccb_bp = NULL;
+ return (0);
+ }
/* Translation only possible if CDB is an array and cmd is R/W6 */
if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0 ||
@@ -1768,8 +1988,7 @@ cmd6workaround(union ccb *ccb)
xpt_print(ccb->ccb_h.path, "READ(6)/WRITE(6) not supported, "
"increasing minimum_cmd_size to 10.\n");
- softc = (struct da_softc *)xpt_path_periph(ccb->ccb_h.path)->softc;
- softc->minimum_cmd_size = 10;
+ softc->minimum_cmd_size = 10;
bcopy(cdb, &cmd6, sizeof(struct scsi_rw_6));
cmd10 = (struct scsi_rw_10 *)cdb;
@@ -1810,8 +2029,9 @@ dadone(struct cam_periph *periph, union
csio = &done_ccb->csio;
switch (csio->ccb_h.ccb_state & DA_CCB_TYPE_MASK) {
case DA_CCB_BUFFER_IO:
+ case DA_CCB_DELETE:
{
- struct bio *bp;
+ struct bio *bp, *bp1;
bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
@@ -1831,6 +2051,7 @@ dadone(struct cam_periph *periph, union
*/
return;
}
+ bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
if (error != 0) {
int queued_error;
@@ -1858,10 +2079,12 @@ dadone(struct cam_periph *periph, union
}
bioq_flush(&softc->bio_queue, NULL,
queued_error);
- bp->bio_error = error;
- bp->bio_resid = bp->bio_bcount;
- bp->bio_flags |= BIO_ERROR;
- } else {
+ if (bp != NULL) {
+ bp->bio_error = error;
+ bp->bio_resid = bp->bio_bcount;
+ bp->bio_flags |= BIO_ERROR;
+ }
+ } else if (bp != NULL) {
bp->bio_resid = csio->resid;
bp->bio_error = 0;
if (bp->bio_resid != 0)
@@ -1873,7 +2096,7 @@ dadone(struct cam_periph *periph, union
/*reduction*/0,
/*timeout*/0,
/*getcount_only*/0);
- } else {
+ } else if (bp != NULL) {
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
panic("REQ_CMP with QFRZN");
bp->bio_resid = csio->resid;
@@ -1902,7 +2125,22 @@ dadone(struct cam_periph *periph, union
softc->outstanding_cmds);
}
- biodone(bp);
+ if ((csio->ccb_h.ccb_state & DA_CCB_TYPE_MASK) ==
+ DA_CCB_DELETE) {
+ while ((bp1 = bioq_takefirst(&softc->delete_run_queue))
+ != NULL) {
+ bp1->bio_resid = bp->bio_resid;
+ bp1->bio_error = bp->bio_error;
+ if (bp->bio_flags & BIO_ERROR)
+ bp1->bio_flags |= BIO_ERROR;
+ biodone(bp1);
+ }
+ softc->delete_running = 0;
+ if (bp != NULL)
+ biodone(bp);
+ daschedule(periph);
+ } else if (bp != NULL)
+ biodone(bp);
break;
}
case DA_CCB_PROBE:
@@ -1972,10 +2210,9 @@ dadone(struct cam_periph *periph, union
} else {
dasetgeom(periph, block_size, maxsector,
lbppbe, lalba & SRC16_LALBA);
- if (lalba & SRC16_LBPME)
- softc->flags |= DA_FLAG_CAN_LBPME;
- else
- softc->flags &= ~DA_FLAG_CAN_LBPME;
+ if ((lalba & SRC16_LBPME) &&
+ softc->delete_method == DA_DELETE_NONE)
+ softc->delete_method = DA_DELETE_UNMAP;
dp = &softc->params;
snprintf(announce_buf, sizeof(announce_buf),
"%juMB (%ju %u byte sectors: %dH %dS/T "
@@ -2362,10 +2599,9 @@ done:
} else {
dasetgeom(periph, block_len, maxsector,
lbppbe, lalba & SRC16_LALBA);
- if (lalba & SRC16_LBPME)
- softc->flags |= DA_FLAG_CAN_LBPME;
- else
- softc->flags &= ~DA_FLAG_CAN_LBPME;
+ if ((lalba & SRC16_LBPME) &&
+ softc->delete_method == DA_DELETE_NONE)
+ softc->delete_method = DA_DELETE_UNMAP;
}
}
More information about the svn-src-all
mailing list