summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2014-07-30 07:18:32 +0000
committermav <mav@FreeBSD.org>2014-07-30 07:18:32 +0000
commitdd34ac08592be4eb2f5b1d76dd16366f63d168e8 (patch)
treeeec0d458347df7afe5760e6c44a8137c0ff8c3fe
parent1c2891173ec9683e5288cfb1ca9e521d469173d6 (diff)
downloadFreeBSD-src-dd34ac08592be4eb2f5b1d76dd16366f63d168e8.zip
FreeBSD-src-dd34ac08592be4eb2f5b1d76dd16366f63d168e8.tar.gz
MFC r268767:
Add support for VMWare dialect of EXTENDED COPY command, aka VAAI Clone. This allows to clone VMs and move them between LUNs inside one storage host without generating extra network traffic to the initiator and back, and without being limited by network bandwidth. LUNs participating in copy operation should have UNIQUE NAA or EUI IDs set. For LUNs without these IDs VMWare will use traditional copy operations. Beware: the above LUN IDs explicitly set to values non-unique from the VM cluster point of view may cause data corruption if wrong LUN is addressed! Sponsored by: iXsystems, Inc.
-rw-r--r--sys/cam/ctl/ctl.c94
-rw-r--r--sys/cam/ctl/ctl_cmd_table.c156
-rw-r--r--sys/cam/ctl/ctl_frontend.c2
-rw-r--r--sys/cam/ctl/ctl_frontend.h1
-rw-r--r--sys/cam/ctl/ctl_frontend_iscsi.c2
-rw-r--r--sys/cam/ctl/ctl_private.h15
-rw-r--r--sys/cam/ctl/ctl_ser_table.c2
-rw-r--r--sys/cam/ctl/ctl_tpc.c1370
-rw-r--r--sys/cam/ctl/ctl_tpc.h38
-rw-r--r--sys/cam/ctl/ctl_tpc_local.c387
-rw-r--r--sys/cam/ctl/scsi_ctl.c3
-rw-r--r--sys/cam/scsi/scsi_all.h350
-rw-r--r--sys/conf/files2
-rw-r--r--sys/modules/ctl/Makefile2
-rw-r--r--usr.sbin/ctladm/ctladm.88
15 files changed, 2385 insertions, 47 deletions
diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c
index c05d416..bea2293 100644
--- a/sys/cam/ctl/ctl.c
+++ b/sys/cam/ctl/ctl.c
@@ -322,10 +322,10 @@ SYSCTL_INT(_kern_cam_ctl, OID_AUTO, verbose, CTLFLAG_RWTUN,
/*
* Supported pages (0x00), Serial number (0x80), Device ID (0x83),
- * SCSI Ports (0x88), Block limits (0xB0) and
+ * SCSI Ports (0x88), Third-party Copy (0x8F), Block limits (0xB0) and
* Logical Block Provisioning (0xB2)
*/
-#define SCSI_EVPD_NUM_SUPPORTED_PAGES 6
+#define SCSI_EVPD_NUM_SUPPORTED_PAGES 7
static void ctl_isc_event_handler(ctl_ha_channel chanel, ctl_ha_event event,
int param);
@@ -351,8 +351,6 @@ static int ctl_ioctl_fill_ooa(struct ctl_lun *lun, uint32_t *cur_fill_num,
struct ctl_ooa_entry *kern_entries);
static int ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
struct thread *td);
-uint32_t ctl_get_resindex(struct ctl_nexus *nexus);
-uint32_t ctl_port_idx(int port_num);
static uint32_t ctl_map_lun(int port_num, uint32_t lun);
static uint32_t ctl_map_lun_back(int port_num, uint32_t lun);
#ifdef unused
@@ -4600,6 +4598,7 @@ ctl_alloc_lun(struct ctl_softc *ctl_softc, struct ctl_lun *ctl_lun,
TAILQ_INIT(&lun->ooa_queue);
TAILQ_INIT(&lun->blocked_queue);
STAILQ_INIT(&lun->error_list);
+ ctl_tpc_init(lun);
/*
* Initialize the mode page index.
@@ -4751,6 +4750,7 @@ ctl_free_lun(struct ctl_lun *lun)
atomic_subtract_int(&lun->be_lun->be->num_luns, 1);
lun->be_lun->lun_shutdown(lun->be_lun->be_lun);
+ ctl_tpc_shutdown(lun);
mtx_destroy(&lun->lun_lock);
free(lun->lun_devid, M_CTL);
if (lun->flags & CTL_LUN_MALLOCED)
@@ -9823,10 +9823,12 @@ ctl_inquiry_evpd_supported(struct ctl_scsiio *ctsio, int alloc_len)
pages->page_list[2] = SVPD_DEVICE_ID;
/* SCSI Ports */
pages->page_list[3] = SVPD_SCSI_PORTS;
+ /* Third-party Copy */
+ pages->page_list[4] = SVPD_SCSI_TPC;
/* Block limits */
- pages->page_list[4] = SVPD_BLOCK_LIMITS;
+ pages->page_list[5] = SVPD_BLOCK_LIMITS;
/* Logical Block Provisioning */
- pages->page_list[5] = SVPD_LBP;
+ pages->page_list[6] = SVPD_LBP;
ctsio->scsi_status = SCSI_STATUS_OK;
@@ -10025,7 +10027,7 @@ ctl_inquiry_evpd_scsi_ports(struct ctl_scsiio *ctsio, int alloc_len)
struct scsi_vpd_port_designation_cont *pdc;
struct ctl_lun *lun;
struct ctl_port *port;
- int data_len, num_target_ports, id_len, g, pg, p;
+ int data_len, num_target_ports, iid_len, id_len, g, pg, p;
int num_target_port_groups, single;
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
@@ -10036,6 +10038,7 @@ ctl_inquiry_evpd_scsi_ports(struct ctl_scsiio *ctsio, int alloc_len)
else
num_target_port_groups = NUM_TARGET_PORT_GROUPS;
num_target_ports = 0;
+ iid_len = 0;
id_len = 0;
mtx_lock(&softc->ctl_lock);
STAILQ_FOREACH(port, &softc->port_list, links) {
@@ -10046,6 +10049,8 @@ ctl_inquiry_evpd_scsi_ports(struct ctl_scsiio *ctsio, int alloc_len)
CTL_MAX_LUNS)
continue;
num_target_ports++;
+ if (port->init_devid)
+ iid_len += port->init_devid->len;
if (port->port_devid)
id_len += port->port_devid->len;
}
@@ -10053,7 +10058,7 @@ ctl_inquiry_evpd_scsi_ports(struct ctl_scsiio *ctsio, int alloc_len)
data_len = sizeof(struct scsi_vpd_scsi_ports) + num_target_port_groups *
num_target_ports * (sizeof(struct scsi_vpd_port_designation) +
- sizeof(struct scsi_vpd_port_designation_cont)) + id_len;
+ sizeof(struct scsi_vpd_port_designation_cont)) + iid_len + id_len;
ctsio->kern_data_ptr = malloc(data_len, M_CTL, M_WAITOK | M_ZERO);
sp = (struct scsi_vpd_scsi_ports *)ctsio->kern_data_ptr;
ctsio->kern_sg_entries = 0;
@@ -10102,19 +10107,22 @@ ctl_inquiry_evpd_scsi_ports(struct ctl_scsiio *ctsio, int alloc_len)
continue;
p = port->targ_port % CTL_MAX_PORTS + g * CTL_MAX_PORTS;
scsi_ulto2b(p, pd->relative_port_id);
- scsi_ulto2b(0, pd->initiator_transportid_length);
+ if (port->init_devid && g == pg) {
+ iid_len = port->init_devid->len;
+ memcpy(pd->initiator_transportid,
+ port->init_devid->data, port->init_devid->len);
+ } else
+ iid_len = 0;
+ scsi_ulto2b(iid_len, pd->initiator_transportid_length);
pdc = (struct scsi_vpd_port_designation_cont *)
- &pd->initiator_transportid[0];
+ (&pd->initiator_transportid[iid_len]);
if (port->port_devid && g == pg) {
id_len = port->port_devid->len;
- scsi_ulto2b(port->port_devid->len,
- pdc->target_port_descriptors_length);
memcpy(pdc->target_port_descriptors,
port->port_devid->data, port->port_devid->len);
- } else {
+ } else
id_len = 0;
- scsi_ulto2b(0, pdc->target_port_descriptors_length);
- }
+ scsi_ulto2b(id_len, pdc->target_port_descriptors_length);
pd = (struct scsi_vpd_port_designation *)
((uint8_t *)pdc->target_port_descriptors + id_len);
}
@@ -10263,6 +10271,9 @@ ctl_inquiry_evpd(struct ctl_scsiio *ctsio)
case SVPD_SCSI_PORTS:
retval = ctl_inquiry_evpd_scsi_ports(ctsio, alloc_len);
break;
+ case SVPD_SCSI_TPC:
+ retval = ctl_inquiry_evpd_tpc(ctsio, alloc_len);
+ break;
case SVPD_BLOCK_LIMITS:
retval = ctl_inquiry_evpd_block_limits(ctsio, alloc_len);
break;
@@ -10293,7 +10304,7 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
struct ctl_lun *lun;
char *val;
uint32_t alloc_len;
- int is_fc;
+ ctl_port_type port_type;
ctl_softc = control_softc;
@@ -10302,11 +10313,10 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
* We treat the ioctl front end, and any SCSI adapters, as packetized
* SCSI front ends.
*/
- if (ctl_softc->ctl_ports[ctl_port_idx(ctsio->io_hdr.nexus.targ_port)]->port_type !=
- CTL_PORT_FC)
- is_fc = 0;
- else
- is_fc = 1;
+ port_type = ctl_softc->ctl_ports[
+ ctl_port_idx(ctsio->io_hdr.nexus.targ_port)]->port_type;
+ if (port_type == CTL_PORT_IOCTL || port_type == CTL_PORT_INTERNAL)
+ port_type = CTL_PORT_SCSI;
lun = ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
cdb = (struct scsi_inquiry *)ctsio->cdb;
@@ -10385,7 +10395,7 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
inq_ptr->device = (SID_QUAL_BAD_LU << 5) | T_NODEVICE;
/* RMB in byte 2 is 0 */
- inq_ptr->version = SCSI_REV_SPC3;
+ inq_ptr->version = SCSI_REV_SPC4;
/*
* According to SAM-3, even if a device only supports a single
@@ -10410,17 +10420,18 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
CTL_DEBUG_PRINT(("additional_length = %d\n",
inq_ptr->additional_length));
- inq_ptr->spc3_flags = SPC3_SID_TPGS_IMPLICIT;
+ inq_ptr->spc3_flags = SPC3_SID_3PC;
+ if (!ctl_is_single)
+ inq_ptr->spc3_flags |= SPC3_SID_TPGS_IMPLICIT;
/* 16 bit addressing */
- if (is_fc == 0)
+ if (port_type == CTL_PORT_SCSI)
inq_ptr->spc2_flags = SPC2_SID_ADDR16;
/* XXX set the SID_MultiP bit here if we're actually going to
respond on multiple ports */
inq_ptr->spc2_flags |= SPC2_SID_MultiP;
/* 16 bit data bus, synchronous transfers */
- /* XXX these flags don't apply for FC */
- if (is_fc == 0)
+ if (port_type == CTL_PORT_SCSI)
inq_ptr->flags = SID_WBus16 | SID_Sync;
/*
* XXX KDM do we want to support tagged queueing on the control
@@ -10481,33 +10492,36 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
* and Selection) and Information Unit transfers on both the
* control and array devices.
*/
- if (is_fc == 0)
+ if (port_type == CTL_PORT_SCSI)
inq_ptr->spi3data = SID_SPI_CLOCK_DT_ST | SID_SPI_QAS |
SID_SPI_IUS;
- /* SAM-3 */
- scsi_ulto2b(0x0060, inq_ptr->version1);
- /* SPC-3 (no version claimed) XXX should we claim a version? */
- scsi_ulto2b(0x0300, inq_ptr->version2);
- if (is_fc) {
+ /* SAM-5 (no version claimed) */
+ scsi_ulto2b(0x00A0, inq_ptr->version1);
+ /* SPC-4 (no version claimed) */
+ scsi_ulto2b(0x0460, inq_ptr->version2);
+ if (port_type == CTL_PORT_FC) {
/* FCP-2 ANSI INCITS.350:2003 */
scsi_ulto2b(0x0917, inq_ptr->version3);
- } else {
+ } else if (port_type == CTL_PORT_SCSI) {
/* SPI-4 ANSI INCITS.362:200x */
scsi_ulto2b(0x0B56, inq_ptr->version3);
+ } else if (port_type == CTL_PORT_ISCSI) {
+ /* iSCSI (no version claimed) */
+ scsi_ulto2b(0x0960, inq_ptr->version3);
+ } else if (port_type == CTL_PORT_SAS) {
+ /* SAS (no version claimed) */
+ scsi_ulto2b(0x0BE0, inq_ptr->version3);
}
if (lun == NULL) {
- /* SBC-2 (no version claimed) XXX should we claim a version? */
- scsi_ulto2b(0x0320, inq_ptr->version4);
+ /* SBC-3 (no version claimed) */
+ scsi_ulto2b(0x04C0, inq_ptr->version4);
} else {
switch (lun->be_lun->lun_type) {
case T_DIRECT:
- /*
- * SBC-2 (no version claimed) XXX should we claim a
- * version?
- */
- scsi_ulto2b(0x0320, inq_ptr->version4);
+ /* SBC-3 (no version claimed) */
+ scsi_ulto2b(0x04C0, inq_ptr->version4);
break;
case T_PROCESSOR:
default:
diff --git a/sys/cam/ctl/ctl_cmd_table.c b/sys/cam/ctl/ctl_cmd_table.c
index a11ff72..1240f2a 100644
--- a/sys/cam/ctl/ctl_cmd_table.c
+++ b/sys/cam/ctl/ctl_cmd_table.c
@@ -190,6 +190,156 @@ const struct ctl_cmd_entry ctl_cmd_table_5f[32] =
/* 08-1f */
};
+/* 83 EXTENDED COPY */
+const struct ctl_cmd_entry ctl_cmd_table_83[32] =
+{
+/* 00 EXTENDED COPY (LID1) */
+{ctl_extended_copy_lid1, CTL_SERIDX_RD_CAP, CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_FLAG_DATA_OUT,
+ CTL_LUN_PAT_NONE,
+ 16, { 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
+
+/* 01 EXTENDED COPY (LID4) */
+{ctl_extended_copy_lid4, CTL_SERIDX_RD_CAP, CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_FLAG_DATA_OUT,
+ CTL_LUN_PAT_NONE,
+ 16, { 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
+
+/* 02 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 03 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 04 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 05 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 06 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 07 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 08 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 09 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 0A */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 0B */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 0C */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 0D */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 0E */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 0F */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 10 POPULATE TOKEN */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 11 WRITE USING TOKEN */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 12 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 13 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 14 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 15 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 16 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 17 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 18 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 19 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 1A */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 1B */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 1C COPY OPERATION ABORT */
+{ctl_copy_operation_abort, CTL_SERIDX_RD_CAP, CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_FLAG_DATA_NONE,
+ CTL_LUN_PAT_NONE,
+ 16, { 0x1c, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07}},
+};
+
+/* 84 RECEIVE COPY STATUS */
+const struct ctl_cmd_entry ctl_cmd_table_84[32] =
+{
+/* 00 RECEIVE COPY STATUS (LID1) */
+{ctl_receive_copy_status_lid1, CTL_SERIDX_RD_CAP,
+ CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_FLAG_DATA_IN,
+ CTL_LUN_PAT_NONE,
+ 16, {0x00, 0xff, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
+
+/* 01 RECEIVE COPY DATA (LID1) */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 02 */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 03 RECEIVE COPY OPERATING PARAMETERS */
+{ctl_receive_copy_operating_parameters, CTL_SERIDX_RD_CAP,
+ CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_CMD_FLAG_OK_ON_STOPPED |
+ CTL_CMD_FLAG_OK_ON_INOPERABLE |
+ CTL_CMD_FLAG_OK_ON_SECONDARY |
+ CTL_FLAG_DATA_IN,
+ CTL_LUN_PAT_NONE,
+ 16, {0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
+
+/* 04 RECEIVE COPY FAILURE DETAILS (LID1) */
+{ctl_receive_copy_failure_details, CTL_SERIDX_RD_CAP,
+ CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_FLAG_DATA_IN,
+ CTL_LUN_PAT_NONE,
+ 16, {0x04, 0xff, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
+
+/* 05 RECEIVE COPY STATUS (LID4) */
+{ctl_receive_copy_status_lid4, CTL_SERIDX_RD_CAP,
+ CTL_CMD_FLAG_OK_ON_BOTH |
+ CTL_FLAG_DATA_IN,
+ CTL_LUN_PAT_NONE,
+ 16, {0x05, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
+
+/* 06 RECEIVE COPY DATA (LID4)*/
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 07 RECEIVE ROD TOKEN INFORMATION */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+
+/* 08 REPORT ALL ROD TOKENS */
+{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+};
+
/* 9E SERVICE ACTION IN(16) */
const struct ctl_cmd_entry ctl_cmd_table_9e[32] =
{
@@ -844,10 +994,12 @@ const struct ctl_cmd_entry ctl_cmd_table[256] =
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 83 EXTENDED COPY */
-{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+{__DECONST(ctl_opfunc *, ctl_cmd_table_83), CTL_SERIDX_INVLD, CTL_CMD_FLAG_SA5,
+ CTL_LUN_PAT_NONE},
/* 84 RECEIVE COPY RESULTS */
-{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+{__DECONST(ctl_opfunc *, ctl_cmd_table_84), CTL_SERIDX_INVLD, CTL_CMD_FLAG_SA5,
+ CTL_LUN_PAT_NONE},
/* 85 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
diff --git a/sys/cam/ctl/ctl_frontend.c b/sys/cam/ctl/ctl_frontend.c
index ecd5cba..34b4a9e 100644
--- a/sys/cam/ctl/ctl_frontend.c
+++ b/sys/cam/ctl/ctl_frontend.c
@@ -234,6 +234,8 @@ ctl_port_deregister(struct ctl_port *port)
port->port_devid = NULL;
free(port->target_devid, M_CTL);
port->target_devid = NULL;
+ free(port->init_devid, M_CTL);
+ port->init_devid = NULL;
for (i = 0; i < port->max_initiators; i++)
free(port->wwpn_iid[i].name, M_CTL);
free(port->wwpn_iid, M_CTL);
diff --git a/sys/cam/ctl/ctl_frontend.h b/sys/cam/ctl/ctl_frontend.h
index f252678..825ff50 100644
--- a/sys/cam/ctl/ctl_frontend.h
+++ b/sys/cam/ctl/ctl_frontend.h
@@ -242,6 +242,7 @@ struct ctl_port {
ctl_options_t options; /* passed to CTL */
struct ctl_devid *port_devid; /* passed to CTL */
struct ctl_devid *target_devid; /* passed to CTL */
+ struct ctl_devid *init_devid; /* passed to CTL */
STAILQ_ENTRY(ctl_port) fe_links; /* used by CTL */
STAILQ_ENTRY(ctl_port) links; /* used by CTL */
};
diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c
index 63cb723..9b23713 100644
--- a/sys/cam/ctl/ctl_frontend_iscsi.c
+++ b/sys/cam/ctl/ctl_frontend_iscsi.c
@@ -2707,7 +2707,7 @@ cfiscsi_scsi_command_done(union ctl_io *io)
* Do not return status for aborted commands.
* There are exceptions, but none supported by CTL yet.
*/
- if (io->io_hdr.status == CTL_CMD_ABORTED &&
+ if ((io->io_hdr.flags & CTL_FLAG_ABORT) &&
(io->io_hdr.flags & CTL_FLAG_ABORT_STATUS) == 0) {
ctl_free_io(io);
icl_pdu_free(request);
diff --git a/sys/cam/ctl/ctl_private.h b/sys/cam/ctl/ctl_private.h
index a05f994..33a1515 100644
--- a/sys/cam/ctl/ctl_private.h
+++ b/sys/cam/ctl/ctl_private.h
@@ -373,6 +373,7 @@ struct ctl_devid {
*/
#define NUM_TARGET_PORT_GROUPS 2
+struct tpc_list;
struct ctl_lun {
struct mtx lun_lock;
struct ctl_id target;
@@ -403,6 +404,7 @@ struct ctl_lun {
uint8_t res_type;
uint8_t write_buffer[524288];
struct ctl_devid *lun_devid;
+ TAILQ_HEAD(tpc_lists, tpc_list) tpc_lists;
};
typedef enum {
@@ -467,6 +469,8 @@ struct ctl_softc {
extern const struct ctl_cmd_entry ctl_cmd_table[256];
uint32_t ctl_get_initindex(struct ctl_nexus *nexus);
+uint32_t ctl_get_resindex(struct ctl_nexus *nexus);
+uint32_t ctl_port_idx(int port_num);
int ctl_pool_create(struct ctl_softc *ctl_softc, ctl_pool_type pool_type,
uint32_t total_ctl_io, struct ctl_io_pool **npool);
void ctl_pool_free(struct ctl_io_pool *pool);
@@ -498,6 +502,17 @@ int ctl_report_supported_tmf(struct ctl_scsiio *ctsio);
int ctl_report_timestamp(struct ctl_scsiio *ctsio);
int ctl_isc(struct ctl_scsiio *ctsio);
+void ctl_tpc_init(struct ctl_lun *lun);
+void ctl_tpc_shutdown(struct ctl_lun *lun);
+int ctl_inquiry_evpd_tpc(struct ctl_scsiio *ctsio, int alloc_len);
+int ctl_receive_copy_status_lid1(struct ctl_scsiio *ctsio);
+int ctl_receive_copy_failure_details(struct ctl_scsiio *ctsio);
+int ctl_receive_copy_status_lid4(struct ctl_scsiio *ctsio);
+int ctl_receive_copy_operating_parameters(struct ctl_scsiio *ctsio);
+int ctl_extended_copy_lid1(struct ctl_scsiio *ctsio);
+int ctl_extended_copy_lid4(struct ctl_scsiio *ctsio);
+int ctl_copy_operation_abort(struct ctl_scsiio *ctsio);
+
#endif /* _KERNEL */
#endif /* _CTL_PRIVATE_H_ */
diff --git a/sys/cam/ctl/ctl_ser_table.c b/sys/cam/ctl/ctl_ser_table.c
index 6628ea8..c680fb5 100644
--- a/sys/cam/ctl/ctl_ser_table.c
+++ b/sys/cam/ctl/ctl_ser_table.c
@@ -69,7 +69,7 @@ ctl_serialize_table[CTL_SERIDX_COUNT][CTL_SERIDX_COUNT] = {
/*MD_SEL */{ bK, bK, bK, bK, bK, bK, bK, pS, pS, bK, pS, bK, bK},
/*RQ_SNS */{ pS, pS, pS, pS, pS, pS, bK, pS, pS, bK, pS, bK, bK},
/*INQ */{ pS, pS, pS, pS, pS, pS, bK, pS, pS, pS, pS, bK, bK},
-/*RD_CAP */{ pS, pS, pS, pS, pS, pS, bK, pS, pS, bK, pS, bK, bK},
+/*RD_CAP */{ pS, pS, pS, pS, pS, pS, bK, pS, pS, pS, pS, bK, bK},
/*RES */{ bK, bK, bK, bK, bK, bK, bK, pS, bK, bK, bK, bK, bK},
/*LOG_SNS */{ pS, pS, pS, pS, pS, bK, bK, pS, pS, bK, pS, bK, bK},
/*FORMAT */{ pS, bK, bK, bK, bK, bK, pS, pS, bK, bK, bK, bK, bK},
diff --git a/sys/cam/ctl/ctl_tpc.c b/sys/cam/ctl/ctl_tpc.c
new file mode 100644
index 0000000..d5baa01a
--- /dev/null
+++ b/sys/cam/ctl/ctl_tpc.c
@@ -0,0 +1,1370 @@
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <machine/atomic.h>
+
+#include <cam/cam.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+#include <cam/ctl/ctl_io.h>
+#include <cam/ctl/ctl.h>
+#include <cam/ctl/ctl_frontend.h>
+#include <cam/ctl/ctl_frontend_internal.h>
+#include <cam/ctl/ctl_util.h>
+#include <cam/ctl/ctl_backend.h>
+#include <cam/ctl/ctl_ioctl.h>
+#include <cam/ctl/ctl_ha.h>
+#include <cam/ctl/ctl_private.h>
+#include <cam/ctl/ctl_debug.h>
+#include <cam/ctl/ctl_scsi_all.h>
+#include <cam/ctl/ctl_tpc.h>
+#include <cam/ctl/ctl_error.h>
+
+#define TPC_MAX_CSCDS 64
+#define TPC_MAX_SEGS 64
+#define TPC_MAX_SEG 0
+#define TPC_MAX_LIST 8192
+#define TPC_MAX_INLINE 0
+#define TPC_MAX_LISTS 255
+#define TPC_MAX_IO_SIZE (1024 * 1024)
+
+MALLOC_DEFINE(M_CTL_TPC, "ctltpc", "CTL TPC");
+
+typedef enum {
+ TPC_ERR_RETRY = 0x000,
+ TPC_ERR_FAIL = 0x001,
+ TPC_ERR_MASK = 0x0ff,
+ TPC_ERR_NO_DECREMENT = 0x100
+} tpc_error_action;
+
+struct tpc_list;
+TAILQ_HEAD(runl, tpc_io);
+struct tpc_io {
+ union ctl_io *io;
+ uint64_t lun;
+ struct tpc_list *list;
+ struct runl run;
+ TAILQ_ENTRY(tpc_io) rlinks;
+ TAILQ_ENTRY(tpc_io) links;
+};
+
+struct tpc_list {
+ uint8_t service_action;
+ int init_port;
+ uint16_t init_idx;
+ uint32_t list_id;
+ uint8_t flags;
+ uint8_t *params;
+ struct scsi_ec_cscd *cscd;
+ struct scsi_ec_segment *seg[TPC_MAX_SEGS];
+ uint8_t *inl;
+ int ncscd;
+ int nseg;
+ int leninl;
+ int curseg;
+ off_t curbytes;
+ int curops;
+ int stage;
+ uint8_t *buf;
+ int segbytes;
+ int tbdio;
+ int error;
+ int abort;
+ int completed;
+ TAILQ_HEAD(, tpc_io) allio;
+ struct scsi_sense_data sense_data;
+ uint8_t sense_len;
+ uint8_t scsi_status;
+ struct ctl_scsiio *ctsio;
+ struct ctl_lun *lun;
+ TAILQ_ENTRY(tpc_list) links;
+};
+
+void
+ctl_tpc_init(struct ctl_lun *lun)
+{
+
+ TAILQ_INIT(&lun->tpc_lists);
+}
+
+void
+ctl_tpc_shutdown(struct ctl_lun *lun)
+{
+ struct tpc_list *list;
+
+ while ((list = TAILQ_FIRST(&lun->tpc_lists)) != NULL) {
+ TAILQ_REMOVE(&lun->tpc_lists, list, links);
+ KASSERT(list->completed,
+ ("Not completed TPC (%p) on shutdown", list));
+ free(list, M_CTL);
+ }
+}
+
+int
+ctl_inquiry_evpd_tpc(struct ctl_scsiio *ctsio, int alloc_len)
+{
+ struct scsi_vpd_tpc *tpc_ptr;
+ struct scsi_vpd_tpc_descriptor *d_ptr;
+ struct scsi_vpd_tpc_descriptor_sc *sc_ptr;
+ struct scsi_vpd_tpc_descriptor_sc_descr *scd_ptr;
+ struct scsi_vpd_tpc_descriptor_pd *pd_ptr;
+ struct scsi_vpd_tpc_descriptor_sd *sd_ptr;
+ struct scsi_vpd_tpc_descriptor_sdid *sdid_ptr;
+ struct scsi_vpd_tpc_descriptor_gco *gco_ptr;
+ struct ctl_lun *lun;
+ int data_len;
+
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+ data_len = sizeof(struct scsi_vpd_tpc) +
+ roundup2(sizeof(struct scsi_vpd_tpc_descriptor_sc) +
+ 2 * sizeof(struct scsi_vpd_tpc_descriptor_sc_descr) + 7, 4) +
+ sizeof(struct scsi_vpd_tpc_descriptor_pd) +
+ roundup2(sizeof(struct scsi_vpd_tpc_descriptor_sd) + 4, 4) +
+ roundup2(sizeof(struct scsi_vpd_tpc_descriptor_sdid) + 2, 4) +
+ sizeof(struct scsi_vpd_tpc_descriptor_gco);
+
+ ctsio->kern_data_ptr = malloc(data_len, M_CTL, M_WAITOK | M_ZERO);
+ tpc_ptr = (struct scsi_vpd_tpc *)ctsio->kern_data_ptr;
+ ctsio->kern_sg_entries = 0;
+
+ if (data_len < alloc_len) {
+ ctsio->residual = alloc_len - data_len;
+ ctsio->kern_data_len = data_len;
+ ctsio->kern_total_len = data_len;
+ } else {
+ ctsio->residual = 0;
+ ctsio->kern_data_len = alloc_len;
+ ctsio->kern_total_len = alloc_len;
+ }
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+ ctsio->kern_sg_entries = 0;
+
+ /*
+ * The control device is always connected. The disk device, on the
+ * other hand, may not be online all the time.
+ */
+ if (lun != NULL)
+ tpc_ptr->device = (SID_QUAL_LU_CONNECTED << 5) |
+ lun->be_lun->lun_type;
+ else
+ tpc_ptr->device = (SID_QUAL_LU_OFFLINE << 5) | T_DIRECT;
+ tpc_ptr->page_code = SVPD_SCSI_TPC;
+ scsi_ulto2b(data_len - 4, tpc_ptr->page_length);
+
+ /* Supported commands */
+ d_ptr = (struct scsi_vpd_tpc_descriptor *)&tpc_ptr->descr[0];
+ sc_ptr = (struct scsi_vpd_tpc_descriptor_sc *)d_ptr;
+ scsi_ulto2b(SVPD_TPC_SC, sc_ptr->desc_type);
+ sc_ptr->list_length = 2 * sizeof(*scd_ptr) + 7;
+ scsi_ulto2b(roundup2(1 + sc_ptr->list_length, 4), sc_ptr->desc_length);
+ scd_ptr = &sc_ptr->descr[0];
+ scd_ptr->opcode = EXTENDED_COPY;
+ scd_ptr->sa_length = 3;
+ scd_ptr->supported_service_actions[0] = EC_EC_LID1;
+ scd_ptr->supported_service_actions[1] = EC_EC_LID4;
+ scd_ptr->supported_service_actions[2] = EC_COA;
+ scd_ptr = (struct scsi_vpd_tpc_descriptor_sc_descr *)
+ &scd_ptr->supported_service_actions[scd_ptr->sa_length];
+ scd_ptr->opcode = RECEIVE_COPY_STATUS;
+ scd_ptr->sa_length = 4;
+ scd_ptr->supported_service_actions[0] = RCS_RCS_LID1;
+ scd_ptr->supported_service_actions[1] = RCS_RCFD;
+ scd_ptr->supported_service_actions[2] = RCS_RCS_LID4;
+ scd_ptr->supported_service_actions[3] = RCS_RCOP;
+
+ /* Parameter data. */
+ d_ptr = (struct scsi_vpd_tpc_descriptor *)
+ (&d_ptr->parameters[0] + scsi_2btoul(d_ptr->desc_length));
+ pd_ptr = (struct scsi_vpd_tpc_descriptor_pd *)d_ptr;
+ scsi_ulto2b(SVPD_TPC_PD, pd_ptr->desc_type);
+ scsi_ulto2b(sizeof(*pd_ptr) - 4, pd_ptr->desc_length);
+ scsi_ulto2b(TPC_MAX_CSCDS, pd_ptr->maximum_cscd_descriptor_count);
+ scsi_ulto2b(TPC_MAX_SEGS, pd_ptr->maximum_segment_descriptor_count);
+ scsi_ulto4b(TPC_MAX_LIST, pd_ptr->maximum_descriptor_list_length);
+ scsi_ulto4b(TPC_MAX_INLINE, pd_ptr->maximum_inline_data_length);
+
+ /* Supported Descriptors */
+ d_ptr = (struct scsi_vpd_tpc_descriptor *)
+ (&d_ptr->parameters[0] + scsi_2btoul(d_ptr->desc_length));
+ sd_ptr = (struct scsi_vpd_tpc_descriptor_sd *)d_ptr;
+ scsi_ulto2b(SVPD_TPC_SD, sd_ptr->desc_type);
+ scsi_ulto2b(roundup2(sizeof(*sd_ptr) - 4 + 4, 4), sd_ptr->desc_length);
+ sd_ptr->list_length = 4;
+ sd_ptr->supported_descriptor_codes[0] = EC_SEG_B2B;
+ sd_ptr->supported_descriptor_codes[1] = EC_SEG_VERIFY;
+ sd_ptr->supported_descriptor_codes[2] = EC_SEG_REGISTER_KEY;
+ sd_ptr->supported_descriptor_codes[3] = EC_CSCD_ID;
+
+ /* Supported CSCD Descriptor IDs */
+ d_ptr = (struct scsi_vpd_tpc_descriptor *)
+ (&d_ptr->parameters[0] + scsi_2btoul(d_ptr->desc_length));
+ sdid_ptr = (struct scsi_vpd_tpc_descriptor_sdid *)d_ptr;
+ scsi_ulto2b(SVPD_TPC_SDID, sdid_ptr->desc_type);
+ scsi_ulto2b(roundup2(sizeof(*sdid_ptr) - 4 + 2, 4), sdid_ptr->desc_length);
+ scsi_ulto2b(2, sdid_ptr->list_length);
+ scsi_ulto2b(0xffff, &sdid_ptr->supported_descriptor_ids[0]);
+
+ /* General Copy Operations */
+ d_ptr = (struct scsi_vpd_tpc_descriptor *)
+ (&d_ptr->parameters[0] + scsi_2btoul(d_ptr->desc_length));
+ gco_ptr = (struct scsi_vpd_tpc_descriptor_gco *)d_ptr;
+ scsi_ulto2b(SVPD_TPC_GCO, gco_ptr->desc_type);
+ scsi_ulto2b(sizeof(*gco_ptr) - 4, gco_ptr->desc_length);
+ scsi_ulto4b(TPC_MAX_LISTS, gco_ptr->total_concurrent_copies);
+ scsi_ulto4b(TPC_MAX_LISTS, gco_ptr->maximum_identified_concurrent_copies);
+ scsi_ulto4b(TPC_MAX_SEG, gco_ptr->maximum_segment_length);
+ gco_ptr->data_segment_granularity = 0;
+ gco_ptr->inline_data_granularity = 0;
+
+ ctsio->scsi_status = SCSI_STATUS_OK;
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+ ctl_datamove((union ctl_io *)ctsio);
+
+ return (CTL_RETVAL_COMPLETE);
+}
+
+int
+ctl_receive_copy_operating_parameters(struct ctl_scsiio *ctsio)
+{
+ struct ctl_lun *lun;
+ struct scsi_receive_copy_operating_parameters *cdb;
+ struct scsi_receive_copy_operating_parameters_data *data;
+ int retval;
+ int alloc_len, total_len;
+
+ CTL_DEBUG_PRINT(("ctl_report_supported_tmf\n"));
+
+ cdb = (struct scsi_receive_copy_operating_parameters *)ctsio->cdb;
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+ retval = CTL_RETVAL_COMPLETE;
+
+ total_len = sizeof(*data) + 4;
+ alloc_len = scsi_4btoul(cdb->length);
+
+ ctsio->kern_data_ptr = malloc(total_len, M_CTL, M_WAITOK | M_ZERO);
+
+ ctsio->kern_sg_entries = 0;
+
+ if (total_len < alloc_len) {
+ ctsio->residual = alloc_len - total_len;
+ ctsio->kern_data_len = total_len;
+ ctsio->kern_total_len = total_len;
+ } else {
+ ctsio->residual = 0;
+ ctsio->kern_data_len = alloc_len;
+ ctsio->kern_total_len = alloc_len;
+ }
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+
+ data = (struct scsi_receive_copy_operating_parameters_data *)ctsio->kern_data_ptr;
+ scsi_ulto4b(sizeof(*data) - 4 + 4, data->length);
+ data->snlid = RCOP_SNLID;
+ scsi_ulto2b(TPC_MAX_CSCDS, data->maximum_cscd_descriptor_count);
+ scsi_ulto2b(TPC_MAX_SEGS, data->maximum_segment_descriptor_count);
+ scsi_ulto4b(TPC_MAX_LIST, data->maximum_descriptor_list_length);
+ scsi_ulto4b(TPC_MAX_SEG, data->maximum_segment_length);
+ scsi_ulto4b(TPC_MAX_INLINE, data->maximum_inline_data_length);
+ scsi_ulto4b(0, data->held_data_limit);
+ scsi_ulto4b(0, data->maximum_stream_device_transfer_size);
+ scsi_ulto2b(TPC_MAX_LISTS, data->total_concurrent_copies);
+ data->maximum_concurrent_copies = TPC_MAX_LISTS;
+ data->data_segment_granularity = 0;
+ data->inline_data_granularity = 0;
+ data->held_data_granularity = 0;
+ data->implemented_descriptor_list_length = 4;
+ data->list_of_implemented_descriptor_type_codes[0] = EC_SEG_B2B;
+ data->list_of_implemented_descriptor_type_codes[1] = EC_SEG_VERIFY;
+ data->list_of_implemented_descriptor_type_codes[2] = EC_SEG_REGISTER_KEY;
+ data->list_of_implemented_descriptor_type_codes[3] = EC_CSCD_ID;
+
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+
+ ctl_datamove((union ctl_io *)ctsio);
+ return (retval);
+}
+
+int
+ctl_receive_copy_status_lid1(struct ctl_scsiio *ctsio)
+{
+ struct ctl_lun *lun;
+ struct scsi_receive_copy_status_lid1 *cdb;
+ struct scsi_receive_copy_status_lid1_data *data;
+ struct tpc_list *list;
+ struct tpc_list list_copy;
+ int retval;
+ int alloc_len, total_len;
+ uint32_t list_id;
+
+ CTL_DEBUG_PRINT(("ctl_receive_copy_status_lid1\n"));
+
+ cdb = (struct scsi_receive_copy_status_lid1 *)ctsio->cdb;
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+ retval = CTL_RETVAL_COMPLETE;
+
+ list_id = cdb->list_identifier;
+ mtx_lock(&lun->lun_lock);
+ TAILQ_FOREACH(list, &lun->tpc_lists, links) {
+ if ((list->flags & EC_LIST_ID_USAGE_MASK) !=
+ EC_LIST_ID_USAGE_NONE && list->list_id == list_id)
+ break;
+ }
+ if (list == NULL) {
+ mtx_unlock(&lun->lun_lock);
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1,
+ /*command*/ 1, /*field*/ 2, /*bit_valid*/ 0,
+ /*bit*/ 0);
+ ctl_done((union ctl_io *)ctsio);
+ return (retval);
+ }
+ list_copy = *list;
+ if (list->completed) {
+ TAILQ_REMOVE(&lun->tpc_lists, list, links);
+ free(list, M_CTL);
+ }
+ mtx_unlock(&lun->lun_lock);
+
+ total_len = sizeof(*data);
+ alloc_len = scsi_4btoul(cdb->length);
+
+ ctsio->kern_data_ptr = malloc(total_len, M_CTL, M_WAITOK | M_ZERO);
+
+ ctsio->kern_sg_entries = 0;
+
+ if (total_len < alloc_len) {
+ ctsio->residual = alloc_len - total_len;
+ ctsio->kern_data_len = total_len;
+ ctsio->kern_total_len = total_len;
+ } else {
+ ctsio->residual = 0;
+ ctsio->kern_data_len = alloc_len;
+ ctsio->kern_total_len = alloc_len;
+ }
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+
+ data = (struct scsi_receive_copy_status_lid1_data *)ctsio->kern_data_ptr;
+ scsi_ulto4b(sizeof(*data) - 4, data->available_data);
+ if (list_copy.completed) {
+ if (list_copy.error || list_copy.abort)
+ data->copy_command_status = RCS_CCS_ERROR;
+ else
+ data->copy_command_status = RCS_CCS_COMPLETED;
+ } else
+ data->copy_command_status = RCS_CCS_INPROG;
+ scsi_ulto2b(list_copy.curseg, data->segments_processed);
+ if (list_copy.curbytes <= UINT32_MAX) {
+ data->transfer_count_units = RCS_TC_BYTES;
+ scsi_ulto4b(list_copy.curbytes, data->transfer_count);
+ } else {
+ data->transfer_count_units = RCS_TC_MBYTES;
+ scsi_ulto4b(list_copy.curbytes >> 20, data->transfer_count);
+ }
+
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+
+ ctl_datamove((union ctl_io *)ctsio);
+ return (retval);
+}
+
+int
+ctl_receive_copy_failure_details(struct ctl_scsiio *ctsio)
+{
+ struct ctl_lun *lun;
+ struct scsi_receive_copy_failure_details *cdb;
+ struct scsi_receive_copy_failure_details_data *data;
+ struct tpc_list *list;
+ struct tpc_list list_copy;
+ int retval;
+ int alloc_len, total_len;
+ uint32_t list_id;
+
+ CTL_DEBUG_PRINT(("ctl_receive_copy_failure_details\n"));
+
+ cdb = (struct scsi_receive_copy_failure_details *)ctsio->cdb;
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+ retval = CTL_RETVAL_COMPLETE;
+
+ list_id = cdb->list_identifier;
+ mtx_lock(&lun->lun_lock);
+ TAILQ_FOREACH(list, &lun->tpc_lists, links) {
+ if (list->completed && (list->flags & EC_LIST_ID_USAGE_MASK) !=
+ EC_LIST_ID_USAGE_NONE && list->list_id == list_id)
+ break;
+ }
+ if (list == NULL) {
+ mtx_unlock(&lun->lun_lock);
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1,
+ /*command*/ 1, /*field*/ 2, /*bit_valid*/ 0,
+ /*bit*/ 0);
+ ctl_done((union ctl_io *)ctsio);
+ return (retval);
+ }
+ list_copy = *list;
+ TAILQ_REMOVE(&lun->tpc_lists, list, links);
+ free(list, M_CTL);
+ mtx_unlock(&lun->lun_lock);
+
+ total_len = sizeof(*data) + list_copy.sense_len;
+ alloc_len = scsi_4btoul(cdb->length);
+
+ ctsio->kern_data_ptr = malloc(total_len, M_CTL, M_WAITOK | M_ZERO);
+
+ ctsio->kern_sg_entries = 0;
+
+ if (total_len < alloc_len) {
+ ctsio->residual = alloc_len - total_len;
+ ctsio->kern_data_len = total_len;
+ ctsio->kern_total_len = total_len;
+ } else {
+ ctsio->residual = 0;
+ ctsio->kern_data_len = alloc_len;
+ ctsio->kern_total_len = alloc_len;
+ }
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+
+ data = (struct scsi_receive_copy_failure_details_data *)ctsio->kern_data_ptr;
+ if (list_copy.completed && (list_copy.error || list_copy.abort)) {
+ scsi_ulto4b(sizeof(*data) - 4, data->available_data);
+ data->copy_command_status = RCS_CCS_ERROR;
+ } else
+ scsi_ulto4b(0, data->available_data);
+ scsi_ulto2b(list_copy.sense_len, data->sense_data_length);
+ memcpy(data->sense_data, &list_copy.sense_data, list_copy.sense_len);
+
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+
+ ctl_datamove((union ctl_io *)ctsio);
+ return (retval);
+}
+
+int
+ctl_receive_copy_status_lid4(struct ctl_scsiio *ctsio)
+{
+ struct ctl_lun *lun;
+ struct scsi_receive_copy_status_lid4 *cdb;
+ struct scsi_receive_copy_status_lid4_data *data;
+ struct tpc_list *list;
+ struct tpc_list list_copy;
+ int retval;
+ int alloc_len, total_len;
+ uint32_t list_id;
+
+ CTL_DEBUG_PRINT(("ctl_receive_copy_status_lid4\n"));
+
+ cdb = (struct scsi_receive_copy_status_lid4 *)ctsio->cdb;
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+ retval = CTL_RETVAL_COMPLETE;
+
+ list_id = scsi_4btoul(cdb->list_identifier);
+ mtx_lock(&lun->lun_lock);
+ TAILQ_FOREACH(list, &lun->tpc_lists, links) {
+ if ((list->flags & EC_LIST_ID_USAGE_MASK) !=
+ EC_LIST_ID_USAGE_NONE && list->list_id == list_id)
+ break;
+ }
+ if (list == NULL) {
+ mtx_unlock(&lun->lun_lock);
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1,
+ /*command*/ 1, /*field*/ 2, /*bit_valid*/ 0,
+ /*bit*/ 0);
+ ctl_done((union ctl_io *)ctsio);
+ return (retval);
+ }
+ list_copy = *list;
+ if (list->completed) {
+ TAILQ_REMOVE(&lun->tpc_lists, list, links);
+ free(list, M_CTL);
+ }
+ mtx_unlock(&lun->lun_lock);
+
+ total_len = sizeof(*data) + list_copy.sense_len;
+ alloc_len = scsi_4btoul(cdb->length);
+
+ ctsio->kern_data_ptr = malloc(total_len, M_CTL, M_WAITOK | M_ZERO);
+
+ ctsio->kern_sg_entries = 0;
+
+ if (total_len < alloc_len) {
+ ctsio->residual = alloc_len - total_len;
+ ctsio->kern_data_len = total_len;
+ ctsio->kern_total_len = total_len;
+ } else {
+ ctsio->residual = 0;
+ ctsio->kern_data_len = alloc_len;
+ ctsio->kern_total_len = alloc_len;
+ }
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+
+ data = (struct scsi_receive_copy_status_lid4_data *)ctsio->kern_data_ptr;
+ scsi_ulto4b(sizeof(*data) - 4, data->available_data);
+ data->response_to_service_action = list_copy.service_action;
+ if (list_copy.completed) {
+ if (list_copy.error)
+ data->copy_command_status = RCS_CCS_ERROR;
+ else if (list_copy.abort)
+ data->copy_command_status = RCS_CCS_ABORTED;
+ else
+ data->copy_command_status = RCS_CCS_COMPLETED;
+ } else
+ data->copy_command_status = RCS_CCS_INPROG_FG;
+ scsi_ulto2b(list_copy.curops, data->operation_counter);
+ scsi_ulto4b(UINT32_MAX, data->estimated_status_update_delay);
+ if (list_copy.curbytes <= UINT32_MAX) {
+ data->transfer_count_units = RCS_TC_BYTES;
+ scsi_ulto4b(list_copy.curbytes, data->transfer_count);
+ } else {
+ data->transfer_count_units = RCS_TC_MBYTES;
+ scsi_ulto4b(list_copy.curbytes >> 20, data->transfer_count);
+ }
+ scsi_ulto2b(list_copy.curseg, data->segments_processed);
+ data->sense_data_length = list_copy.sense_len;
+ memcpy(data->sense_data, &list_copy.sense_data, list_copy.sense_len);
+
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+
+ ctl_datamove((union ctl_io *)ctsio);
+ return (retval);
+}
+
+int
+ctl_copy_operation_abort(struct ctl_scsiio *ctsio)
+{
+ struct ctl_lun *lun;
+ struct scsi_copy_operation_abort *cdb;
+ struct tpc_list *list;
+ int retval;
+ uint32_t list_id;
+
+ CTL_DEBUG_PRINT(("ctl_copy_operation_abort\n"));
+
+ cdb = (struct scsi_copy_operation_abort *)ctsio->cdb;
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+ retval = CTL_RETVAL_COMPLETE;
+
+ list_id = scsi_4btoul(cdb->list_identifier);
+ mtx_lock(&lun->lun_lock);
+ TAILQ_FOREACH(list, &lun->tpc_lists, links) {
+ if ((list->flags & EC_LIST_ID_USAGE_MASK) !=
+ EC_LIST_ID_USAGE_NONE && list->list_id == list_id)
+ break;
+ }
+ if (list == NULL) {
+ mtx_unlock(&lun->lun_lock);
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1,
+ /*command*/ 1, /*field*/ 2, /*bit_valid*/ 0,
+ /*bit*/ 0);
+ ctl_done((union ctl_io *)ctsio);
+ return (retval);
+ }
+ list->abort = 1;
+ mtx_unlock(&lun->lun_lock);
+
+ ctl_set_success(ctsio);
+ ctl_done((union ctl_io *)ctsio);
+ return (retval);
+}
+
+static uint64_t
+tpc_resolve(struct tpc_list *list, uint16_t idx, uint32_t *ss)
+{
+
+ if (idx == 0xffff) {
+ if (ss && list->lun->be_lun)
+ *ss = list->lun->be_lun->blocksize;
+ return (list->lun->lun);
+ }
+ if (idx >= list->ncscd)
+ return (UINT64_MAX);
+ return (tpcl_resolve(list->init_port, &list->cscd[idx], ss));
+}
+
+static int
+tpc_process_b2b(struct tpc_list *list)
+{
+ struct scsi_ec_segment_b2b *seg;
+ struct scsi_ec_cscd_dtsp *sdstp, *ddstp;
+ struct tpc_io *tior, *tiow;
+ struct runl run, *prun;
+ uint64_t sl, dl;
+ off_t srclba, dstlba, numbytes, donebytes, roundbytes;
+ int numlba;
+ uint32_t srcblock, dstblock;
+
+ if (list->stage == 1) {
+complete:
+ while ((tior = TAILQ_FIRST(&list->allio)) != NULL) {
+ TAILQ_REMOVE(&list->allio, tior, links);
+ ctl_free_io(tior->io);
+ free(tior, M_CTL);
+ }
+ free(list->buf, M_CTL);
+ if (list->abort) {
+ ctl_set_task_aborted(list->ctsio);
+ return (CTL_RETVAL_ERROR);
+ } else if (list->error) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x0d, /*ascq*/ 0x01, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ } else {
+ list->curbytes += list->segbytes;
+ return (CTL_RETVAL_COMPLETE);
+ }
+ }
+
+ TAILQ_INIT(&list->allio);
+ seg = (struct scsi_ec_segment_b2b *)list->seg[list->curseg];
+ sl = tpc_resolve(list, scsi_2btoul(seg->src_cscd), &srcblock);
+ dl = tpc_resolve(list, scsi_2btoul(seg->dst_cscd), &dstblock);
+ if (sl >= CTL_MAX_LUNS || dl >= CTL_MAX_LUNS) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x08, /*ascq*/ 0x04, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ }
+ sdstp = &list->cscd[scsi_2btoul(seg->src_cscd)].dtsp;
+ if (scsi_3btoul(sdstp->block_length) != 0)
+ srcblock = scsi_3btoul(sdstp->block_length);
+ ddstp = &list->cscd[scsi_2btoul(seg->dst_cscd)].dtsp;
+ if (scsi_3btoul(ddstp->block_length) != 0)
+ dstblock = scsi_3btoul(ddstp->block_length);
+ numlba = scsi_2btoul(seg->number_of_blocks);
+ if (seg->flags & EC_SEG_DC)
+ numbytes = (off_t)numlba * dstblock;
+ else
+ numbytes = (off_t)numlba * srcblock;
+ srclba = scsi_8btou64(seg->src_lba);
+ dstlba = scsi_8btou64(seg->dst_lba);
+
+// printf("Copy %ju bytes from %ju @ %ju to %ju @ %ju\n",
+// (uintmax_t)numbytes, sl, scsi_8btou64(seg->src_lba),
+// dl, scsi_8btou64(seg->dst_lba));
+
+ if (numbytes == 0)
+ return (CTL_RETVAL_COMPLETE);
+
+ if (numbytes % srcblock != 0 || numbytes % dstblock != 0) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x26, /*ascq*/ 0x0A, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ }
+
+ list->buf = malloc(numbytes, M_CTL, M_WAITOK);
+ list->segbytes = numbytes;
+ donebytes = 0;
+ TAILQ_INIT(&run);
+ prun = &run;
+ list->tbdio = 1;
+ while (donebytes < numbytes) {
+ roundbytes = MIN(numbytes - donebytes, TPC_MAX_IO_SIZE);
+
+ tior = malloc(sizeof(*tior), M_CTL, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&tior->run);
+ tior->list = list;
+ TAILQ_INSERT_TAIL(&list->allio, tior, links);
+ tior->io = tpcl_alloc_io();
+ if (tior->io == NULL) {
+ list->error = 1;
+ goto complete;
+ }
+ ctl_scsi_read_write(tior->io,
+ /*data_ptr*/ &list->buf[donebytes],
+ /*data_len*/ roundbytes,
+ /*read_op*/ 1,
+ /*byte2*/ 0,
+ /*minimum_cdb_size*/ 0,
+ /*lba*/ srclba + donebytes / srcblock,
+ /*num_blocks*/ roundbytes / srcblock,
+ /*tag_type*/ CTL_TAG_SIMPLE,
+ /*control*/ 0);
+ tior->io->io_hdr.retries = 3;
+ tior->lun = sl;
+ tior->io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = tior;
+
+ tiow = malloc(sizeof(*tior), M_CTL, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&tiow->run);
+ tiow->list = list;
+ TAILQ_INSERT_TAIL(&list->allio, tiow, links);
+ tiow->io = tpcl_alloc_io();
+ if (tiow->io == NULL) {
+ list->error = 1;
+ goto complete;
+ }
+ ctl_scsi_read_write(tiow->io,
+ /*data_ptr*/ &list->buf[donebytes],
+ /*data_len*/ roundbytes,
+ /*read_op*/ 0,
+ /*byte2*/ 0,
+ /*minimum_cdb_size*/ 0,
+ /*lba*/ dstlba + donebytes / dstblock,
+ /*num_blocks*/ roundbytes / dstblock,
+ /*tag_type*/ CTL_TAG_SIMPLE,
+ /*control*/ 0);
+ tiow->io->io_hdr.retries = 3;
+ tiow->lun = dl;
+ tiow->io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = tior;
+
+ TAILQ_INSERT_TAIL(&tior->run, tiow, rlinks);
+ TAILQ_INSERT_TAIL(prun, tior, rlinks);
+ prun = &tior->run;
+ donebytes += roundbytes;
+ }
+
+ while ((tior = TAILQ_FIRST(&run)) != NULL) {
+ TAILQ_REMOVE(&run, tior, rlinks);
+ if (tpcl_queue(tior->io, tior->lun) != CTL_RETVAL_COMPLETE)
+ panic("tpcl_queue() error");
+ }
+
+ list->stage++;
+ return (CTL_RETVAL_QUEUED);
+}
+
+static int
+tpc_process_verify(struct tpc_list *list)
+{
+ struct scsi_ec_segment_verify *seg;
+ struct tpc_io *tio;
+ uint64_t sl;
+
+ if (list->stage == 1) {
+complete:
+ while ((tio = TAILQ_FIRST(&list->allio)) != NULL) {
+ TAILQ_REMOVE(&list->allio, tio, links);
+ ctl_free_io(tio->io);
+ free(tio, M_CTL);
+ }
+ if (list->abort) {
+ ctl_set_task_aborted(list->ctsio);
+ return (CTL_RETVAL_ERROR);
+ } else if (list->error) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x0d, /*ascq*/ 0x01, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ } else
+ return (CTL_RETVAL_COMPLETE);
+ }
+
+ TAILQ_INIT(&list->allio);
+ seg = (struct scsi_ec_segment_verify *)list->seg[list->curseg];
+ sl = tpc_resolve(list, scsi_2btoul(seg->src_cscd), NULL);
+ if (sl >= CTL_MAX_LUNS) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x08, /*ascq*/ 0x04, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ }
+
+// printf("Verify %ju\n", sl);
+
+ if ((seg->tur & 0x01) == 0)
+ return (CTL_RETVAL_COMPLETE);
+
+ list->tbdio = 1;
+ tio = malloc(sizeof(*tio), M_CTL, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&tio->run);
+ tio->list = list;
+ TAILQ_INSERT_TAIL(&list->allio, tio, links);
+ tio->io = tpcl_alloc_io();
+ if (tio->io == NULL) {
+ list->error = 1;
+ goto complete;
+ }
+ ctl_scsi_tur(tio->io, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0);
+ tio->io->io_hdr.retries = 3;
+ tio->lun = sl;
+ tio->io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = tio;
+ list->stage++;
+ if (tpcl_queue(tio->io, tio->lun) != CTL_RETVAL_COMPLETE)
+ panic("tpcl_queue() error");
+ return (CTL_RETVAL_QUEUED);
+}
+
+static int
+tpc_process_register_key(struct tpc_list *list)
+{
+ struct scsi_ec_segment_register_key *seg;
+ struct tpc_io *tio;
+ uint64_t dl;
+ int datalen;
+
+ if (list->stage == 1) {
+complete:
+ while ((tio = TAILQ_FIRST(&list->allio)) != NULL) {
+ TAILQ_REMOVE(&list->allio, tio, links);
+ ctl_free_io(tio->io);
+ free(tio, M_CTL);
+ }
+ free(list->buf, M_CTL);
+ if (list->abort) {
+ ctl_set_task_aborted(list->ctsio);
+ return (CTL_RETVAL_ERROR);
+ } else if (list->error) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x0d, /*ascq*/ 0x01, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ } else
+ return (CTL_RETVAL_COMPLETE);
+ }
+
+ TAILQ_INIT(&list->allio);
+ seg = (struct scsi_ec_segment_register_key *)list->seg[list->curseg];
+ dl = tpc_resolve(list, scsi_2btoul(seg->dst_cscd), NULL);
+ if (dl >= CTL_MAX_LUNS) {
+ ctl_set_sense(list->ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x08, /*ascq*/ 0x04, SSD_ELEM_NONE);
+ return (CTL_RETVAL_ERROR);
+ }
+
+// printf("Register Key %ju\n", dl);
+
+ list->tbdio = 1;
+ tio = malloc(sizeof(*tio), M_CTL, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&tio->run);
+ tio->list = list;
+ TAILQ_INSERT_TAIL(&list->allio, tio, links);
+ tio->io = tpcl_alloc_io();
+ if (tio->io == NULL) {
+ list->error = 1;
+ goto complete;
+ }
+ datalen = sizeof(struct scsi_per_res_out_parms);
+ list->buf = malloc(datalen, M_CTL, M_WAITOK);
+ ctl_scsi_persistent_res_out(tio->io,
+ list->buf, datalen, SPRO_REGISTER, -1,
+ scsi_8btou64(seg->res_key), scsi_8btou64(seg->sa_res_key),
+ /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0);
+ tio->io->io_hdr.retries = 3;
+ tio->lun = dl;
+ tio->io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = tio;
+ list->stage++;
+ if (tpcl_queue(tio->io, tio->lun) != CTL_RETVAL_COMPLETE)
+ panic("tpcl_queue() error");
+ return (CTL_RETVAL_QUEUED);
+}
+
+static void
+tpc_process(struct tpc_list *list)
+{
+ struct ctl_lun *lun = list->lun;
+ struct scsi_ec_segment *seg;
+ struct ctl_scsiio *ctsio = list->ctsio;
+ int retval = CTL_RETVAL_COMPLETE;
+
+//printf("ZZZ %d cscd, %d segs\n", list->ncscd, list->nseg);
+ while (list->curseg < list->nseg) {
+ seg = list->seg[list->curseg];
+ switch (seg->type_code) {
+ case EC_SEG_B2B:
+ retval = tpc_process_b2b(list);
+ break;
+ case EC_SEG_VERIFY:
+ retval = tpc_process_verify(list);
+ break;
+ case EC_SEG_REGISTER_KEY:
+ retval = tpc_process_register_key(list);
+ break;
+ default:
+ ctl_set_sense(ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_COPY_ABORTED,
+ /*asc*/ 0x26, /*ascq*/ 0x09, SSD_ELEM_NONE);
+ goto done;
+ }
+ if (retval == CTL_RETVAL_QUEUED)
+ return;
+ if (retval == CTL_RETVAL_ERROR) {
+ list->error = 1;
+ goto done;
+ }
+ list->curseg++;
+ list->stage = 0;
+ }
+
+ ctl_set_success(ctsio);
+
+done:
+//printf("ZZZ done\n");
+ mtx_lock(&lun->lun_lock);
+ if ((list->flags & EC_LIST_ID_USAGE_MASK) == EC_LIST_ID_USAGE_NONE) {
+ TAILQ_REMOVE(&lun->tpc_lists, list, links);
+ free(list, M_CTL);
+ } else {
+ list->completed = 1;
+ list->sense_data = ctsio->sense_data;
+ list->sense_len = ctsio->sense_len;
+ list->scsi_status = ctsio->scsi_status;
+ }
+ mtx_unlock(&lun->lun_lock);
+
+ ctl_done((union ctl_io *)ctsio);
+}
+
+/*
+ * For any sort of check condition, busy, etc., we just retry. We do not
+ * decrement the retry count for unit attention type errors. These are
+ * normal, and we want to save the retry count for "real" errors. Otherwise,
+ * we could end up with situations where a command will succeed in some
+ * situations and fail in others, depending on whether a unit attention is
+ * pending. Also, some of our error recovery actions, most notably the
+ * LUN reset action, will cause a unit attention.
+ *
+ * We can add more detail here later if necessary.
+ */
+static tpc_error_action
+tpc_checkcond_parse(union ctl_io *io)
+{
+ tpc_error_action error_action;
+ int error_code, sense_key, asc, ascq;
+
+ /*
+ * Default to retrying the command.
+ */
+ error_action = TPC_ERR_RETRY;
+
+ scsi_extract_sense_len(&io->scsiio.sense_data,
+ io->scsiio.sense_len,
+ &error_code,
+ &sense_key,
+ &asc,
+ &ascq,
+ /*show_errors*/ 1);
+
+ switch (error_code) {
+ case SSD_DEFERRED_ERROR:
+ case SSD_DESC_DEFERRED_ERROR:
+ error_action |= TPC_ERR_NO_DECREMENT;
+ break;
+ case SSD_CURRENT_ERROR:
+ case SSD_DESC_CURRENT_ERROR:
+ default:
+ switch (sense_key) {
+ case SSD_KEY_UNIT_ATTENTION:
+ error_action |= TPC_ERR_NO_DECREMENT;
+ break;
+ case SSD_KEY_HARDWARE_ERROR:
+ /*
+ * This is our generic "something bad happened"
+ * error code. It often isn't recoverable.
+ */
+ if ((asc == 0x44) && (ascq == 0x00))
+ error_action = TPC_ERR_FAIL;
+ break;
+ case SSD_KEY_NOT_READY:
+ /*
+ * If the LUN is powered down, there likely isn't
+ * much point in retrying right now.
+ */
+ if ((asc == 0x04) && (ascq == 0x02))
+ error_action = TPC_ERR_FAIL;
+ /*
+ * If the LUN is offline, there probably isn't much
+ * point in retrying, either.
+ */
+ if ((asc == 0x04) && (ascq == 0x03))
+ error_action = TPC_ERR_FAIL;
+ break;
+ }
+ }
+ return (error_action);
+}
+
+static tpc_error_action
+tpc_error_parse(union ctl_io *io)
+{
+ tpc_error_action error_action = TPC_ERR_RETRY;
+
+ switch (io->io_hdr.io_type) {
+ case CTL_IO_SCSI:
+ switch (io->io_hdr.status & CTL_STATUS_MASK) {
+ case CTL_SCSI_ERROR:
+ switch (io->scsiio.scsi_status) {
+ case SCSI_STATUS_CHECK_COND:
+ error_action = tpc_checkcond_parse(io);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case CTL_IO_TASK:
+ break;
+ default:
+ panic("%s: invalid ctl_io type %d\n", __func__,
+ io->io_hdr.io_type);
+ break;
+ }
+ return (error_action);
+}
+
+void
+tpc_done(union ctl_io *io)
+{
+ struct tpc_io *tio, *tior;
+
+ /*
+ * Very minimal retry logic. We basically retry if we got an error
+ * back, and the retry count is greater than 0. If we ever want
+ * more sophisticated initiator type behavior, the CAM error
+ * recovery code in ../common might be helpful.
+ */
+// if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
+// ctl_io_error_print(io, NULL);
+ tio = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
+ if (((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
+ && (io->io_hdr.retries > 0)) {
+ ctl_io_status old_status;
+ tpc_error_action error_action;
+
+ error_action = tpc_error_parse(io);
+ switch (error_action & TPC_ERR_MASK) {
+ case TPC_ERR_FAIL:
+ break;
+ case TPC_ERR_RETRY:
+ default:
+ if ((error_action & TPC_ERR_NO_DECREMENT) == 0)
+ io->io_hdr.retries--;
+ old_status = io->io_hdr.status;
+ io->io_hdr.status = CTL_STATUS_NONE;
+ io->io_hdr.flags &= ~CTL_FLAG_ABORT;
+ io->io_hdr.flags &= ~CTL_FLAG_SENT_2OTHER_SC;
+ if (tpcl_queue(io, tio->lun) != CTL_RETVAL_COMPLETE) {
+ printf("%s: error returned from ctl_queue()!\n",
+ __func__);
+ io->io_hdr.status = old_status;
+ } else
+ return;
+ }
+ }
+
+ if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
+ tio->list->error = 1;
+ else
+ atomic_add_int(&tio->list->curops, 1);
+ if (!tio->list->error && !tio->list->abort) {
+ while ((tior = TAILQ_FIRST(&tio->run)) != NULL) {
+ TAILQ_REMOVE(&tio->run, tior, rlinks);
+ atomic_add_int(&tio->list->tbdio, 1);
+ if (tpcl_queue(tior->io, tior->lun) != CTL_RETVAL_COMPLETE)
+ panic("tpcl_queue() error");
+ }
+ }
+ if (atomic_fetchadd_int(&tio->list->tbdio, -1) == 1)
+ tpc_process(tio->list);
+}
+
+int
+ctl_extended_copy_lid1(struct ctl_scsiio *ctsio)
+{
+ struct scsi_extended_copy *cdb;
+ struct scsi_extended_copy_lid1_data *data;
+ struct ctl_lun *lun;
+ struct tpc_list *list, *tlist;
+ uint8_t *ptr;
+ char *value;
+ int len, off, lencscd, lenseg, leninl, nseg;
+
+ CTL_DEBUG_PRINT(("ctl_extended_copy_lid1\n"));
+
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+ cdb = (struct scsi_extended_copy *)ctsio->cdb;
+ len = scsi_4btoul(cdb->length);
+
+ if (len < sizeof(struct scsi_extended_copy_lid1_data) ||
+ len > sizeof(struct scsi_extended_copy_lid1_data) +
+ TPC_MAX_LIST + TPC_MAX_INLINE) {
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1, /*command*/ 1,
+ /*field*/ 9, /*bit_valid*/ 0, /*bit*/ 0);
+ goto done;
+ }
+
+ /*
+ * If we've got a kernel request that hasn't been malloced yet,
+ * malloc it and tell the caller the data buffer is here.
+ */
+ if ((ctsio->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0) {
+ ctsio->kern_data_ptr = malloc(len, M_CTL, M_WAITOK);
+ ctsio->kern_data_len = len;
+ ctsio->kern_total_len = len;
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+ ctsio->kern_sg_entries = 0;
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+ ctl_datamove((union ctl_io *)ctsio);
+
+ return (CTL_RETVAL_COMPLETE);
+ }
+
+ data = (struct scsi_extended_copy_lid1_data *)ctsio->kern_data_ptr;
+ lencscd = scsi_2btoul(data->cscd_list_length);
+ lenseg = scsi_4btoul(data->segment_list_length);
+ leninl = scsi_4btoul(data->inline_data_length);
+ if (len < sizeof(struct scsi_extended_copy_lid1_data) +
+ lencscd + lenseg + leninl ||
+ leninl > TPC_MAX_INLINE) {
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1, /*command*/ 0,
+ /*field*/ 2, /*bit_valid*/ 0, /*bit*/ 0);
+ goto done;
+ }
+ if (lencscd > TPC_MAX_CSCDS * sizeof(struct scsi_ec_cscd)) {
+ ctl_set_sense(ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+ /*asc*/ 0x26, /*ascq*/ 0x06, SSD_ELEM_NONE);
+ goto done;
+ }
+ if (lencscd + lenseg > TPC_MAX_LIST) {
+ ctl_set_param_len_error(ctsio);
+ goto done;
+ }
+
+ list = malloc(sizeof(struct tpc_list), M_CTL, M_WAITOK | M_ZERO);
+ list->service_action = cdb->service_action;
+ value = ctl_get_opt(&lun->be_lun->options, "insecure_tpc");
+ if (value != NULL && strcmp(value, "on") == 0)
+ list->init_port = -1;
+ else
+ list->init_port = ctsio->io_hdr.nexus.targ_port;
+ list->init_idx = ctl_get_resindex(&ctsio->io_hdr.nexus);
+ list->list_id = data->list_identifier;
+ list->flags = data->flags;
+ list->params = ctsio->kern_data_ptr;
+ list->cscd = (struct scsi_ec_cscd *)&data->data[0];
+ ptr = &data->data[lencscd];
+ for (nseg = 0, off = 0; off < lenseg; nseg++) {
+ if (nseg >= TPC_MAX_SEGS) {
+ free(list, M_CTL);
+ ctl_set_sense(ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+ /*asc*/ 0x26, /*ascq*/ 0x08, SSD_ELEM_NONE);
+ goto done;
+ }
+ list->seg[nseg] = (struct scsi_ec_segment *)(ptr + off);
+ off += sizeof(struct scsi_ec_segment) +
+ scsi_2btoul(list->seg[nseg]->descr_length);
+ }
+ list->inl = &data->data[lencscd + lenseg];
+ list->ncscd = lencscd / sizeof(struct scsi_ec_cscd);
+ list->nseg = nseg;
+ list->leninl = leninl;
+ list->ctsio = ctsio;
+ list->lun = lun;
+ mtx_lock(&lun->lun_lock);
+ if ((list->flags & EC_LIST_ID_USAGE_MASK) != EC_LIST_ID_USAGE_NONE) {
+ TAILQ_FOREACH(tlist, &lun->tpc_lists, links) {
+ if ((tlist->flags & EC_LIST_ID_USAGE_MASK) !=
+ EC_LIST_ID_USAGE_NONE &&
+ tlist->list_id == list->list_id)
+ break;
+ }
+ if (tlist != NULL && !tlist->completed) {
+ mtx_unlock(&lun->lun_lock);
+ free(list, M_CTL);
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1,
+ /*command*/ 0, /*field*/ 0, /*bit_valid*/ 0,
+ /*bit*/ 0);
+ goto done;
+ }
+ if (tlist != NULL) {
+ TAILQ_REMOVE(&lun->tpc_lists, tlist, links);
+ free(tlist, M_CTL);
+ }
+ }
+ TAILQ_INSERT_TAIL(&lun->tpc_lists, list, links);
+ mtx_unlock(&lun->lun_lock);
+
+ tpc_process(list);
+ return (CTL_RETVAL_COMPLETE);
+
+done:
+ ctl_done((union ctl_io *)ctsio);
+ return (CTL_RETVAL_COMPLETE);
+}
+
+int
+ctl_extended_copy_lid4(struct ctl_scsiio *ctsio)
+{
+ struct scsi_extended_copy *cdb;
+ struct scsi_extended_copy_lid4_data *data;
+ struct ctl_lun *lun;
+ struct tpc_list *list, *tlist;
+ uint8_t *ptr;
+ char *value;
+ int len, off, lencscd, lenseg, leninl, nseg;
+
+ CTL_DEBUG_PRINT(("ctl_extended_copy_lid4\n"));
+
+ lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+ cdb = (struct scsi_extended_copy *)ctsio->cdb;
+ len = scsi_4btoul(cdb->length);
+
+ if (len < sizeof(struct scsi_extended_copy_lid4_data) ||
+ len > sizeof(struct scsi_extended_copy_lid4_data) +
+ TPC_MAX_LIST + TPC_MAX_INLINE) {
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1, /*command*/ 1,
+ /*field*/ 9, /*bit_valid*/ 0, /*bit*/ 0);
+ goto done;
+ }
+
+ /*
+ * If we've got a kernel request that hasn't been malloced yet,
+ * malloc it and tell the caller the data buffer is here.
+ */
+ if ((ctsio->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0) {
+ ctsio->kern_data_ptr = malloc(len, M_CTL, M_WAITOK);
+ ctsio->kern_data_len = len;
+ ctsio->kern_total_len = len;
+ ctsio->kern_data_resid = 0;
+ ctsio->kern_rel_offset = 0;
+ ctsio->kern_sg_entries = 0;
+ ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
+ ctsio->be_move_done = ctl_config_move_done;
+ ctl_datamove((union ctl_io *)ctsio);
+
+ return (CTL_RETVAL_COMPLETE);
+ }
+
+ data = (struct scsi_extended_copy_lid4_data *)ctsio->kern_data_ptr;
+ lencscd = scsi_2btoul(data->cscd_list_length);
+ lenseg = scsi_2btoul(data->segment_list_length);
+ leninl = scsi_2btoul(data->inline_data_length);
+ if (len < sizeof(struct scsi_extended_copy_lid4_data) +
+ lencscd + lenseg + leninl ||
+ leninl > TPC_MAX_INLINE) {
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1, /*command*/ 0,
+ /*field*/ 2, /*bit_valid*/ 0, /*bit*/ 0);
+ goto done;
+ }
+ if (lencscd > TPC_MAX_CSCDS * sizeof(struct scsi_ec_cscd)) {
+ ctl_set_sense(ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+ /*asc*/ 0x26, /*ascq*/ 0x06, SSD_ELEM_NONE);
+ goto done;
+ }
+ if (lencscd + lenseg > TPC_MAX_LIST) {
+ ctl_set_param_len_error(ctsio);
+ goto done;
+ }
+
+ list = malloc(sizeof(struct tpc_list), M_CTL, M_WAITOK | M_ZERO);
+ list->service_action = cdb->service_action;
+ value = ctl_get_opt(&lun->be_lun->options, "insecure_tpc");
+ if (value != NULL && strcmp(value, "on") == 0)
+ list->init_port = -1;
+ else
+ list->init_port = ctsio->io_hdr.nexus.targ_port;
+ list->init_idx = ctl_get_resindex(&ctsio->io_hdr.nexus);
+ list->list_id = scsi_4btoul(data->list_identifier);
+ list->flags = data->flags;
+ list->params = ctsio->kern_data_ptr;
+ list->cscd = (struct scsi_ec_cscd *)&data->data[0];
+ ptr = &data->data[lencscd];
+ for (nseg = 0, off = 0; off < lenseg; nseg++) {
+ if (nseg >= TPC_MAX_SEGS) {
+ free(list, M_CTL);
+ ctl_set_sense(ctsio, /*current_error*/ 1,
+ /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+ /*asc*/ 0x26, /*ascq*/ 0x08, SSD_ELEM_NONE);
+ goto done;
+ }
+ list->seg[nseg] = (struct scsi_ec_segment *)(ptr + off);
+ off += sizeof(struct scsi_ec_segment) +
+ scsi_2btoul(list->seg[nseg]->descr_length);
+ }
+ list->inl = &data->data[lencscd + lenseg];
+ list->ncscd = lencscd / sizeof(struct scsi_ec_cscd);
+ list->nseg = nseg;
+ list->leninl = leninl;
+ list->ctsio = ctsio;
+ list->lun = lun;
+ mtx_lock(&lun->lun_lock);
+ if ((list->flags & EC_LIST_ID_USAGE_MASK) != EC_LIST_ID_USAGE_NONE) {
+ TAILQ_FOREACH(tlist, &lun->tpc_lists, links) {
+ if ((tlist->flags & EC_LIST_ID_USAGE_MASK) !=
+ EC_LIST_ID_USAGE_NONE &&
+ tlist->list_id == list->list_id)
+ break;
+ }
+ if (tlist != NULL && !tlist->completed) {
+ mtx_unlock(&lun->lun_lock);
+ free(list, M_CTL);
+ ctl_set_invalid_field(ctsio, /*sks_valid*/ 1,
+ /*command*/ 0, /*field*/ 0, /*bit_valid*/ 0,
+ /*bit*/ 0);
+ goto done;
+ }
+ if (tlist != NULL) {
+ TAILQ_REMOVE(&lun->tpc_lists, tlist, links);
+ free(tlist, M_CTL);
+ }
+ }
+ TAILQ_INSERT_TAIL(&lun->tpc_lists, list, links);
+ mtx_unlock(&lun->lun_lock);
+
+ tpc_process(list);
+ return (CTL_RETVAL_COMPLETE);
+
+done:
+ ctl_done((union ctl_io *)ctsio);
+ return (CTL_RETVAL_COMPLETE);
+}
+
diff --git a/sys/cam/ctl/ctl_tpc.h b/sys/cam/ctl/ctl_tpc.h
new file mode 100644
index 0000000..ecbaec1
--- /dev/null
+++ b/sys/cam/ctl/ctl_tpc.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CTL_TPC_H
+#define _CTL_TPC_H 1
+
+void tpc_done(union ctl_io *io);
+
+uint64_t tpcl_resolve(int init_port, struct scsi_ec_cscd *cscd, uint32_t *ss);
+union ctl_io * tpcl_alloc_io(void);
+int tpcl_queue(union ctl_io *io, uint64_t lun);
+
+#endif /* _CTL_TPC_H */
diff --git a/sys/cam/ctl/ctl_tpc_local.c b/sys/cam/ctl/ctl_tpc_local.c
new file mode 100644
index 0000000..8fb7978
--- /dev/null
+++ b/sys/cam/ctl/ctl_tpc_local.c
@@ -0,0 +1,387 @@
+/*-
+ * Copyright (c) 2014 Alexander Motin <mav@FreeBSD.org>
+ * Copyright (c) 2004, 2005 Silicon Graphics International Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <cam/cam.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+#include <cam/ctl/ctl_io.h>
+#include <cam/ctl/ctl.h>
+#include <cam/ctl/ctl_frontend.h>
+#include <cam/ctl/ctl_frontend_internal.h>
+#include <cam/ctl/ctl_util.h>
+#include <cam/ctl/ctl_backend.h>
+#include <cam/ctl/ctl_ioctl.h>
+#include <cam/ctl/ctl_ha.h>
+#include <cam/ctl/ctl_private.h>
+#include <cam/ctl/ctl_debug.h>
+#include <cam/ctl/ctl_scsi_all.h>
+#include <cam/ctl/ctl_tpc.h>
+#include <cam/ctl/ctl_error.h>
+
+struct tpcl_softc {
+ struct ctl_port port;
+ int cur_tag_num;
+};
+
+extern struct ctl_softc *control_softc;
+static struct tpcl_softc tpcl_softc;
+
+static int tpcl_init(void);
+static void tpcl_shutdown(void);
+static void tpcl_online(void *arg);
+static void tpcl_offline(void *arg);
+static int tpcl_lun_enable(void *arg, struct ctl_id target_id, int lun_id);
+static int tpcl_lun_disable(void *arg, struct ctl_id target_id, int lun_id);
+static void tpcl_datamove(union ctl_io *io);
+static void tpcl_done(union ctl_io *io);
+
+
+static struct ctl_frontend tpcl_frontend =
+{
+ .name = "tpc",
+ .init = tpcl_init,
+ .shutdown = tpcl_shutdown,
+};
+CTL_FRONTEND_DECLARE(ctltpc, tpcl_frontend);
+
+static int
+tpcl_init(void)
+{
+ struct ctl_softc *softc = control_softc;
+ struct tpcl_softc *tsoftc = &tpcl_softc;
+ struct ctl_port *port;
+ struct scsi_transportid_spi *tid;
+ int len;
+
+ memset(tsoftc, 0, sizeof(*tsoftc));
+
+ port = &tsoftc->port;
+ port->frontend = &tpcl_frontend;
+ port->port_type = CTL_PORT_INTERNAL;
+ port->num_requested_ctl_io = 100;
+ port->port_name = "tpc";
+ port->port_online = tpcl_online;
+ port->port_offline = tpcl_offline;
+ port->onoff_arg = tsoftc;
+ port->lun_enable = tpcl_lun_enable;
+ port->lun_disable = tpcl_lun_disable;
+ port->targ_lun_arg = tsoftc;
+ port->fe_datamove = tpcl_datamove;
+ port->fe_done = tpcl_done;
+ port->max_targets = 1;
+ port->max_target_id = 0;
+ port->max_initiators = 1;
+
+ if (ctl_port_register(port, (softc->flags & CTL_FLAG_MASTER_SHELF)) != 0)
+ {
+ printf("%s: tpc frontend registration failed\n", __func__);
+ return (0);
+ }
+
+ len = sizeof(struct scsi_transportid_spi);
+ port->init_devid = malloc(sizeof(struct ctl_devid) + len,
+ M_CTL, M_WAITOK | M_ZERO);
+ port->init_devid->len = len;
+ tid = (struct scsi_transportid_spi *)port->init_devid->data;
+ tid->format_protocol = SCSI_TRN_SPI_FORMAT_DEFAULT | SCSI_PROTO_SPI;
+ scsi_ulto2b(0, tid->scsi_addr);
+ scsi_ulto2b(port->targ_port, tid->rel_trgt_port_id);
+
+ ctl_port_online(port);
+ return (0);
+}
+
+void
+tpcl_shutdown(void)
+{
+ struct tpcl_softc *tsoftc = &tpcl_softc;
+ struct ctl_port *port;
+
+ port = &tsoftc->port;
+ ctl_port_offline(port);
+ if (ctl_port_deregister(&tsoftc->port) != 0)
+ printf("%s: ctl_frontend_deregister() failed\n", __func__);
+}
+
+static void
+tpcl_online(void *arg)
+{
+}
+
+static void
+tpcl_offline(void *arg)
+{
+}
+
+static int
+tpcl_lun_enable(void *arg, struct ctl_id target_id, int lun_id)
+{
+
+ return (0);
+}
+
+static int
+tpcl_lun_disable(void *arg, struct ctl_id target_id, int lun_id)
+{
+
+ return (0);
+}
+
+static void
+tpcl_datamove(union ctl_io *io)
+{
+ struct ctl_sg_entry *ext_sglist, *kern_sglist;
+ struct ctl_sg_entry ext_entry, kern_entry;
+ int ext_sg_entries, kern_sg_entries;
+ int ext_sg_start, ext_offset;
+ int len_to_copy, len_copied;
+ int kern_watermark, ext_watermark;
+ struct ctl_scsiio *ctsio;
+ int i, j;
+
+ ext_sg_start = 0;
+ ext_offset = 0;
+ ext_sglist = NULL;
+
+ CTL_DEBUG_PRINT(("%s\n", __func__));
+
+ ctsio = &io->scsiio;
+
+ /*
+ * If this is the case, we're probably doing a BBR read and don't
+ * actually need to transfer the data. This will effectively
+ * bit-bucket the data.
+ */
+ if (ctsio->ext_data_ptr == NULL)
+ goto bailout;
+
+ /*
+ * To simplify things here, if we have a single buffer, stick it in
+ * a S/G entry and just make it a single entry S/G list.
+ */
+ if (ctsio->io_hdr.flags & CTL_FLAG_EDPTR_SGLIST) {
+ int len_seen;
+
+ ext_sglist = (struct ctl_sg_entry *)ctsio->ext_data_ptr;
+ ext_sg_entries = ctsio->ext_sg_entries;
+ ext_sg_start = 0;
+ ext_offset = 0;
+ len_seen = 0;
+ for (i = 0; i < ext_sg_entries; i++) {
+ if ((len_seen + ext_sglist[i].len) >=
+ ctsio->ext_data_filled) {
+ ext_sg_start = i;
+ ext_offset = ctsio->ext_data_filled - len_seen;
+ break;
+ }
+ len_seen += ext_sglist[i].len;
+ }
+ } else {
+ ext_sglist = &ext_entry;
+ ext_sglist->addr = ctsio->ext_data_ptr;
+ ext_sglist->len = ctsio->ext_data_len;
+ ext_sg_entries = 1;
+ ext_sg_start = 0;
+ ext_offset = ctsio->ext_data_filled;
+ }
+
+ if (ctsio->kern_sg_entries > 0) {
+ kern_sglist = (struct ctl_sg_entry *)ctsio->kern_data_ptr;
+ kern_sg_entries = ctsio->kern_sg_entries;
+ } else {
+ kern_sglist = &kern_entry;
+ kern_sglist->addr = ctsio->kern_data_ptr;
+ kern_sglist->len = ctsio->kern_data_len;
+ kern_sg_entries = 1;
+ }
+
+ kern_watermark = 0;
+ ext_watermark = ext_offset;
+ len_copied = 0;
+ for (i = ext_sg_start, j = 0;
+ i < ext_sg_entries && j < kern_sg_entries;) {
+ uint8_t *ext_ptr, *kern_ptr;
+
+ len_to_copy = min(ext_sglist[i].len - ext_watermark,
+ kern_sglist[j].len - kern_watermark);
+
+ ext_ptr = (uint8_t *)ext_sglist[i].addr;
+ ext_ptr = ext_ptr + ext_watermark;
+ if (io->io_hdr.flags & CTL_FLAG_BUS_ADDR) {
+ /*
+ * XXX KDM fix this!
+ */
+ panic("need to implement bus address support");
+#if 0
+ kern_ptr = bus_to_virt(kern_sglist[j].addr);
+#endif
+ } else
+ kern_ptr = (uint8_t *)kern_sglist[j].addr;
+ kern_ptr = kern_ptr + kern_watermark;
+
+ kern_watermark += len_to_copy;
+ ext_watermark += len_to_copy;
+
+ if ((ctsio->io_hdr.flags & CTL_FLAG_DATA_MASK) ==
+ CTL_FLAG_DATA_IN) {
+ CTL_DEBUG_PRINT(("%s: copying %d bytes to user\n",
+ __func__, len_to_copy));
+ CTL_DEBUG_PRINT(("%s: from %p to %p\n", __func__,
+ kern_ptr, ext_ptr));
+ memcpy(ext_ptr, kern_ptr, len_to_copy);
+ } else {
+ CTL_DEBUG_PRINT(("%s: copying %d bytes from user\n",
+ __func__, len_to_copy));
+ CTL_DEBUG_PRINT(("%s: from %p to %p\n", __func__,
+ ext_ptr, kern_ptr));
+ memcpy(kern_ptr, ext_ptr, len_to_copy);
+ }
+
+ len_copied += len_to_copy;
+
+ if (ext_sglist[i].len == ext_watermark) {
+ i++;
+ ext_watermark = 0;
+ }
+
+ if (kern_sglist[j].len == kern_watermark) {
+ j++;
+ kern_watermark = 0;
+ }
+ }
+
+ ctsio->ext_data_filled += len_copied;
+
+ CTL_DEBUG_PRINT(("%s: ext_sg_entries: %d, kern_sg_entries: %d\n",
+ __func__, ext_sg_entries, kern_sg_entries));
+ CTL_DEBUG_PRINT(("%s: ext_data_len = %d, kern_data_len = %d\n",
+ __func__, ctsio->ext_data_len, ctsio->kern_data_len));
+
+ /* XXX KDM set residual?? */
+bailout:
+ io->scsiio.be_move_done(io);
+}
+
+static void
+tpcl_done(union ctl_io *io)
+{
+
+ tpc_done(io);
+}
+
+uint64_t
+tpcl_resolve(int init_port, struct scsi_ec_cscd *cscd, uint32_t *ss)
+{
+ struct ctl_softc *softc = control_softc;
+ struct scsi_ec_cscd_id *cscdid;
+ struct ctl_port *port;
+ struct ctl_lun *lun;
+ uint64_t lunid = UINT64_MAX, l;
+ int i;
+
+ if (cscd->type_code != EC_CSCD_ID)
+ return (lunid);
+
+ cscdid = (struct scsi_ec_cscd_id *)cscd;
+ mtx_lock(&softc->ctl_lock);
+ if (init_port >= 0) {
+ port = softc->ctl_ports[ctl_port_idx(init_port)];
+ if (port == NULL || port->lun_map == NULL)
+ init_port = -1;
+ }
+ if (init_port < 0) {
+ STAILQ_FOREACH(lun, &softc->lun_list, links) {
+ if (lun->lun_devid == NULL)
+ continue;
+ if (scsi_devid_match(lun->lun_devid->data,
+ lun->lun_devid->len, &cscdid->codeset,
+ cscdid->length + 4) == 0) {
+ lunid = lun->lun;
+ if (ss && lun->be_lun)
+ *ss = lun->be_lun->blocksize;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < CTL_MAX_LUNS; i++) {
+ l = port->lun_map(port->targ_lun_arg, i);
+ if (l >= CTL_MAX_LUNS)
+ continue;
+ lun = softc->ctl_luns[l];
+ if (lun == NULL || lun->lun_devid == NULL)
+ continue;
+ if (scsi_devid_match(lun->lun_devid->data,
+ lun->lun_devid->len, &cscdid->codeset,
+ cscdid->length + 4) == 0) {
+ lunid = lun->lun;
+ if (ss && lun->be_lun)
+ *ss = lun->be_lun->blocksize;
+ break;
+ }
+ }
+ }
+ mtx_unlock(&softc->ctl_lock);
+ return (lunid);
+};
+
+union ctl_io *
+tpcl_alloc_io(void)
+{
+ struct tpcl_softc *tsoftc = &tpcl_softc;
+
+ return (ctl_alloc_io(tsoftc->port.ctl_pool_ref));
+};
+
+int
+tpcl_queue(union ctl_io *io, uint64_t lun)
+{
+ struct tpcl_softc *tsoftc = &tpcl_softc;
+
+ io->io_hdr.nexus.initid.id = 0;
+ io->io_hdr.nexus.targ_port = tsoftc->port.targ_port;
+ io->io_hdr.nexus.targ_target.id = 0;
+ io->io_hdr.nexus.targ_lun = lun;
+ io->scsiio.tag_num = atomic_fetchadd_int(&tsoftc->cur_tag_num, 1);
+ io->scsiio.ext_data_filled = 0;
+ return (ctl_queue(io));
+}
diff --git a/sys/cam/ctl/scsi_ctl.c b/sys/cam/ctl/scsi_ctl.c
index cd3524a..3529683 100644
--- a/sys/cam/ctl/scsi_ctl.c
+++ b/sys/cam/ctl/scsi_ctl.c
@@ -804,8 +804,7 @@ ctlfestart(struct cam_periph *periph, union ccb *start_ccb)
if (io == NULL) {
scsi_status = SCSI_STATUS_BUSY;
csio->sense_len = 0;
- } else if ((io->io_hdr.status & CTL_STATUS_MASK) ==
- CTL_CMD_ABORTED &&
+ } else if ((io->io_hdr.flags & CTL_FLAG_ABORT) &&
(io->io_hdr.flags & CTL_FLAG_ABORT_STATUS) == 0) {
io->io_hdr.flags &= ~CTL_FLAG_STATUS_QUEUED;
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index 1c7aaee..1e0cc97 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -1266,6 +1266,273 @@ struct scsi_report_timestamp_data
uint8_t reserve2[2];
};
+struct scsi_receive_copy_status_lid1
+{
+ uint8_t opcode;
+ uint8_t service_action;
+#define RCS_RCS_LID1 0x00
+ uint8_t list_identifier;
+ uint8_t reserved[7];
+ uint8_t length[4];
+ uint8_t reserved1;
+ uint8_t control;
+};
+
+struct scsi_receive_copy_status_lid1_data
+{
+ uint8_t available_data[4];
+ uint8_t copy_command_status;
+#define RCS_CCS_INPROG 0x00
+#define RCS_CCS_COMPLETED 0x01
+#define RCS_CCS_ERROR 0x02
+ uint8_t segments_processed[2];
+ uint8_t transfer_count_units;
+#define RCS_TC_BYTES 0x00
+#define RCS_TC_KBYTES 0x01
+#define RCS_TC_MBYTES 0x02
+#define RCS_TC_GBYTES 0x03
+#define RCS_TC_TBYTES 0x04
+#define RCS_TC_PBYTES 0x05
+#define RCS_TC_EBYTES 0x06
+#define RCS_TC_LBAS 0xf1
+ uint8_t transfer_count[4];
+};
+
+struct scsi_receive_copy_failure_details
+{
+ uint8_t opcode;
+ uint8_t service_action;
+#define RCS_RCFD 0x04
+ uint8_t list_identifier;
+ uint8_t reserved[7];
+ uint8_t length[4];
+ uint8_t reserved1;
+ uint8_t control;
+};
+
+struct scsi_receive_copy_failure_details_data
+{
+ uint8_t available_data[4];
+ uint8_t reserved[52];
+ uint8_t copy_command_status;
+ uint8_t reserved2;
+ uint8_t sense_data_length[2];
+ uint8_t sense_data[];
+};
+
+struct scsi_receive_copy_status_lid4
+{
+ uint8_t opcode;
+ uint8_t service_action;
+#define RCS_RCS_LID4 0x05
+ uint8_t list_identifier[4];
+ uint8_t reserved[4];
+ uint8_t length[4];
+ uint8_t reserved1;
+ uint8_t control;
+};
+
+struct scsi_receive_copy_status_lid4_data
+{
+ uint8_t available_data[4];
+ uint8_t response_to_service_action;
+ uint8_t copy_command_status;
+#define RCS_CCS_COMPLETED_PROD 0x03
+#define RCS_CCS_COMPLETED_RESID 0x04
+#define RCS_CCS_INPROG_FGBG 0x10
+#define RCS_CCS_INPROG_FG 0x11
+#define RCS_CCS_INPROG_BG 0x12
+#define RCS_CCS_ABORTED 0x60
+ uint8_t operation_counter[2];
+ uint8_t estimated_status_update_delay[4];
+ uint8_t extended_copy_completion_status;
+ uint8_t length_of_the_sense_data_field;
+ uint8_t sense_data_length;
+ uint8_t transfer_count_units;
+ uint8_t transfer_count[8];
+ uint8_t segments_processed[2];
+ uint8_t reserved[2];
+ uint8_t sense_data[];
+};
+
+struct scsi_receive_copy_operating_parameters
+{
+ uint8_t opcode;
+ uint8_t service_action;
+#define RCS_RCOP 0x03
+ uint8_t reserved[8];
+ uint8_t length[4];
+ uint8_t reserved1;
+ uint8_t control;
+};
+
+struct scsi_receive_copy_operating_parameters_data
+{
+ uint8_t length[4];
+ uint8_t snlid;
+#define RCOP_SNLID 0x01
+ uint8_t reserved[3];
+ uint8_t maximum_cscd_descriptor_count[2];
+ uint8_t maximum_segment_descriptor_count[2];
+ uint8_t maximum_descriptor_list_length[4];
+ uint8_t maximum_segment_length[4];
+ uint8_t maximum_inline_data_length[4];
+ uint8_t held_data_limit[4];
+ uint8_t maximum_stream_device_transfer_size[4];
+ uint8_t reserved2[2];
+ uint8_t total_concurrent_copies[2];
+ uint8_t maximum_concurrent_copies;
+ uint8_t data_segment_granularity;
+ uint8_t inline_data_granularity;
+ uint8_t held_data_granularity;
+ uint8_t reserved3[3];
+ uint8_t implemented_descriptor_list_length;
+ uint8_t list_of_implemented_descriptor_type_codes[0];
+};
+
+struct scsi_extended_copy
+{
+ uint8_t opcode;
+ uint8_t service_action;
+#define EC_EC_LID1 0x00
+#define EC_EC_LID4 0x01
+ uint8_t reserved[8];
+ uint8_t length[4];
+ uint8_t reserved1;
+ uint8_t control;
+};
+
+struct scsi_ec_cscd_dtsp
+{
+ uint8_t flags;
+#define EC_CSCD_FIXED 0x01
+#define EC_CSCD_PAD 0x04
+ uint8_t block_length[3];
+};
+
+struct scsi_ec_cscd
+{
+ uint8_t type_code;
+#define EC_CSCD_EXT 0xff
+ uint8_t luidt_pdt;
+#define EC_LUIDT_MASK 0xc0
+#define EC_LUIDT_LUN 0x00
+#define EC_LUIDT_PROXY_TOKEN 0x40
+ uint8_t relative_initiator_port[2];
+ uint8_t cscd_params[24];
+ struct scsi_ec_cscd_dtsp dtsp;
+};
+
+struct scsi_ec_cscd_id
+{
+ uint8_t type_code;
+#define EC_CSCD_ID 0xe4
+ uint8_t luidt_pdt;
+ uint8_t relative_initiator_port[2];
+ uint8_t codeset;
+ uint8_t id_type;
+ uint8_t reserved;
+ uint8_t length;
+ uint8_t designator[20];
+ struct scsi_ec_cscd_dtsp dtsp;
+};
+
+struct scsi_ec_segment
+{
+ uint8_t type_code;
+ uint8_t flags;
+#define EC_SEG_DC 0x02
+#define EC_SEG_CAT 0x01
+ uint8_t descr_length[2];
+ uint8_t params[];
+};
+
+struct scsi_ec_segment_b2b
+{
+ uint8_t type_code;
+#define EC_SEG_B2B 0x02
+ uint8_t flags;
+ uint8_t descr_length[2];
+ uint8_t src_cscd[2];
+ uint8_t dst_cscd[2];
+ uint8_t reserved[2];
+ uint8_t number_of_blocks[2];
+ uint8_t src_lba[8];
+ uint8_t dst_lba[8];
+};
+
+struct scsi_ec_segment_verify
+{
+ uint8_t type_code;
+#define EC_SEG_VERIFY 0x07
+ uint8_t reserved;
+ uint8_t descr_length[2];
+ uint8_t src_cscd[2];
+ uint8_t reserved2[2];
+ uint8_t tur;
+ uint8_t reserved3[3];
+};
+
+struct scsi_ec_segment_register_key
+{
+ uint8_t type_code;
+#define EC_SEG_REGISTER_KEY 0x14
+ uint8_t reserved;
+ uint8_t descr_length[2];
+ uint8_t reserved2[2];
+ uint8_t dst_cscd[2];
+ uint8_t res_key[8];
+ uint8_t sa_res_key[8];
+ uint8_t reserved3[4];
+};
+
+struct scsi_extended_copy_lid1_data
+{
+ uint8_t list_identifier;
+ uint8_t flags;
+#define EC_PRIORITY 0x07
+#define EC_LIST_ID_USAGE_MASK 0x18
+#define EC_LIST_ID_USAGE_FULL 0x08
+#define EC_LIST_ID_USAGE_NOHOLD 0x10
+#define EC_LIST_ID_USAGE_NONE 0x18
+#define EC_STR 0x20
+ uint8_t cscd_list_length[2];
+ uint8_t reserved[4];
+ uint8_t segment_list_length[4];
+ uint8_t inline_data_length[4];
+ uint8_t data[];
+};
+
+struct scsi_extended_copy_lid4_data
+{
+ uint8_t list_format;
+#define EC_LIST_FORMAT 0x01
+ uint8_t flags;
+ uint8_t header_cscd_list_length[2];
+ uint8_t reserved[11];
+ uint8_t flags2;
+#define EC_IMMED 0x01
+#define EC_G_SENSE 0x02
+ uint8_t header_cscd_type_code;
+ uint8_t reserved2[3];
+ uint8_t list_identifier[4];
+ uint8_t reserved3[18];
+ uint8_t cscd_list_length[2];
+ uint8_t segment_list_length[2];
+ uint8_t inline_data_length[2];
+ uint8_t data[];
+};
+
+struct scsi_copy_operation_abort
+{
+ uint8_t opcode;
+ uint8_t service_action;
+#define EC_COA 0x1c
+ uint8_t list_identifier[4];
+ uint8_t reserved[9];
+ uint8_t control;
+};
+
struct ata_pass_16 {
u_int8_t opcode;
u_int8_t protocol;
@@ -1337,6 +1604,8 @@ struct ata_pass_16 {
#define MODE_SENSE_10 0x5A
#define PERSISTENT_RES_IN 0x5E
#define PERSISTENT_RES_OUT 0x5F
+#define EXTENDED_COPY 0x83
+#define RECEIVE_COPY_STATUS 0x84
#define ATA_PASS_16 0x85
#define READ_16 0x88
#define COMPARE_AND_WRITE 0x89
@@ -1450,7 +1719,9 @@ struct scsi_inquiry_data
#define SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
u_int8_t dev_qual2;
#define SID_QUAL2 0x7F
-#define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+#define SID_LU_CONG 0x40
+#define SID_RMB 0x80
+#define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & SID_RMB) != 0)
u_int8_t version;
#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
#define SCSI_REV_0 0
@@ -1773,6 +2044,83 @@ struct scsi_vpd_scsi_ports
*/
#define SVPD_ATA_INFORMATION 0x89
+
+struct scsi_vpd_tpc_descriptor
+{
+ uint8_t desc_type[2];
+ uint8_t desc_length[2];
+ uint8_t parameters[];
+};
+
+struct scsi_vpd_tpc_descriptor_sc_descr
+{
+ uint8_t opcode;
+ uint8_t sa_length;
+ uint8_t supported_service_actions[0];
+};
+
+struct scsi_vpd_tpc_descriptor_sc
+{
+ uint8_t desc_type[2];
+#define SVPD_TPC_SC 0x0001
+ uint8_t desc_length[2];
+ uint8_t list_length;
+ struct scsi_vpd_tpc_descriptor_sc_descr descr[];
+};
+
+struct scsi_vpd_tpc_descriptor_pd
+{
+ uint8_t desc_type[2];
+#define SVPD_TPC_PD 0x0004
+ uint8_t desc_length[2];
+ uint8_t reserved[4];
+ uint8_t maximum_cscd_descriptor_count[2];
+ uint8_t maximum_segment_descriptor_count[2];
+ uint8_t maximum_descriptor_list_length[4];
+ uint8_t maximum_inline_data_length[4];
+ uint8_t reserved2[12];
+};
+
+struct scsi_vpd_tpc_descriptor_sd
+{
+ uint8_t desc_type[2];
+#define SVPD_TPC_SD 0x0008
+ uint8_t desc_length[2];
+ uint8_t list_length;
+ uint8_t supported_descriptor_codes[];
+};
+
+struct scsi_vpd_tpc_descriptor_sdid
+{
+ uint8_t desc_type[2];
+#define SVPD_TPC_SDID 0x000C
+ uint8_t desc_length[2];
+ uint8_t list_length[2];
+ uint8_t supported_descriptor_ids[];
+};
+
+struct scsi_vpd_tpc_descriptor_gco
+{
+ uint8_t desc_type[2];
+#define SVPD_TPC_GCO 0x8001
+ uint8_t desc_length[2];
+ uint8_t total_concurrent_copies[4];
+ uint8_t maximum_identified_concurrent_copies[4];
+ uint8_t maximum_segment_length[4];
+ uint8_t data_segment_granularity;
+ uint8_t inline_data_granularity;
+ uint8_t reserved[18];
+};
+
+struct scsi_vpd_tpc
+{
+ uint8_t device;
+ uint8_t page_code;
+#define SVPD_SCSI_TPC 0x8F
+ uint8_t page_length[2];
+ struct scsi_vpd_tpc_descriptor descr[];
+};
+
/*
* Block Device Characteristics VPD Page based on
* T10/1799-D Revision 31
diff --git a/sys/conf/files b/sys/conf/files
index f73dbac..61a7c80 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -127,6 +127,8 @@ cam/ctl/ctl_frontend_cam_sim.c optional ctl
cam/ctl/ctl_frontend_internal.c optional ctl
cam/ctl/ctl_frontend_iscsi.c optional ctl
cam/ctl/ctl_scsi_all.c optional ctl
+cam/ctl/ctl_tpc.c optional ctl
+cam/ctl/ctl_tpc_local.c optional ctl
cam/ctl/ctl_error.c optional ctl
cam/ctl/ctl_util.c optional ctl
cam/ctl/scsi_ctl.c optional ctl
diff --git a/sys/modules/ctl/Makefile b/sys/modules/ctl/Makefile
index 45f88c3..0ba5836 100644
--- a/sys/modules/ctl/Makefile
+++ b/sys/modules/ctl/Makefile
@@ -14,6 +14,8 @@ SRCS+= ctl_frontend_cam_sim.c
SRCS+= ctl_frontend_internal.c
SRCS+= ctl_frontend_iscsi.c
SRCS+= ctl_scsi_all.c
+SRCS+= ctl_tpc.c
+SRCS+= ctl_tpc_local.c
SRCS+= ctl_error.c
SRCS+= ctl_util.c
SRCS+= scsi_ctl.c
diff --git a/usr.sbin/ctladm/ctladm.8 b/usr.sbin/ctladm/ctladm.8
index fc050eb..ae1b20c 100644
--- a/usr.sbin/ctladm/ctladm.8
+++ b/usr.sbin/ctladm/ctladm.8
@@ -951,6 +951,14 @@ Specifies LUN SCSI name string.
Specifies LUN EUI-64 identifier.
.It Va naa
Specifies LUN NAA identifier.
+Either EUI or NAA identifier should be set to UNIQUE value to allow
+EXTENDED COPY command access the LUN.
+Non-unique LUN identifiers may lead to data corruption.
+.It Va insecure_tpc
+Setting to "on" allows EXTENDED COPY command sent to this LUN access
+other LUNs on this host, not accessible otherwise.
+This allows to offload copying between different iSCSI targets residing
+on the same host in trusted environments.
.It Va unmap
Set to "on", enables UNMAP support for the LUN.
.El
OpenPOWER on IntegriCloud