diff options
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/iscsi/iscsi_target_nego.c | 12 | ||||
-rw-r--r-- | drivers/target/target_core_configfs.c | 49 | ||||
-rw-r--r-- | drivers/target/target_core_device.c | 1 | ||||
-rw-r--r-- | drivers/target/target_core_fabric_configfs.c | 3 | ||||
-rw-r--r-- | drivers/target/target_core_file.c | 1 | ||||
-rw-r--r-- | drivers/target/target_core_iblock.c | 4 | ||||
-rw-r--r-- | drivers/target/target_core_sbc.c | 30 | ||||
-rw-r--r-- | drivers/target/target_core_spc.c | 934 | ||||
-rw-r--r-- | drivers/target/target_core_xcopy.c | 103 | ||||
-rw-r--r-- | drivers/target/target_core_xcopy.h | 2 |
10 files changed, 1053 insertions, 86 deletions
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index f2919319ad38..ff49c8f3fe24 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -1018,6 +1018,13 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo return 0; } +/* + * RETURN VALUE: + * + * 1 = Login successful + * -1 = Login failed + * 0 = More PDU exchanges required + */ static int iscsi_target_do_login(struct iscsit_conn *conn, struct iscsi_login *login) { int pdu_count = 0; @@ -1363,12 +1370,13 @@ int iscsi_target_start_negotiation( ret = -1; if (ret < 0) { - cancel_delayed_work_sync(&conn->login_work); iscsi_target_restore_sock_callbacks(conn); iscsi_remove_failed_auth_entry(conn); } - if (ret != 0) + if (ret != 0) { + cancel_delayed_work_sync(&conn->login_work); iscsi_target_nego_release(conn); + } return ret; } diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 416514c5c7ac..611b0424e305 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -12,6 +12,7 @@ * ****************************************************************************/ +#include <linux/kstrtox.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <generated/utsrelease.h> @@ -547,6 +548,7 @@ DEF_CONFIGFS_ATTRIB_SHOW(unmap_granularity); DEF_CONFIGFS_ATTRIB_SHOW(unmap_granularity_alignment); DEF_CONFIGFS_ATTRIB_SHOW(unmap_zeroes_data); DEF_CONFIGFS_ATTRIB_SHOW(max_write_same_len); +DEF_CONFIGFS_ATTRIB_SHOW(emulate_rsoc); #define DEF_CONFIGFS_ATTRIB_STORE_U32(_name) \ static ssize_t _name##_store(struct config_item *item, const char *page,\ @@ -577,7 +579,7 @@ static ssize_t _name##_store(struct config_item *item, const char *page, \ bool flag; \ int ret; \ \ - ret = strtobool(page, &flag); \ + ret = kstrtobool(page, &flag); \ if (ret < 0) \ return ret; \ da->_name = flag; \ @@ -637,7 +639,7 @@ static ssize_t emulate_model_alias_store(struct config_item *item, return -EINVAL; } - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -659,7 +661,7 @@ static ssize_t emulate_write_cache_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -711,7 +713,7 @@ static ssize_t emulate_tas_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -736,7 +738,7 @@ static ssize_t emulate_tpu_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -766,7 +768,7 @@ static ssize_t emulate_tpws_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -865,7 +867,7 @@ static ssize_t pi_prot_format_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -902,7 +904,7 @@ static ssize_t pi_prot_verify_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -931,7 +933,7 @@ static ssize_t force_pr_aptpl_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; if (da->da_dev->export_count) { @@ -953,7 +955,7 @@ static ssize_t emulate_rest_reord_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -976,7 +978,7 @@ static ssize_t unmap_zeroes_data_store(struct config_item *item, bool flag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -1100,8 +1102,6 @@ static ssize_t block_size_store(struct config_item *item, } da->block_size = val; - if (da->max_bytes_per_io) - da->hw_max_sectors = da->max_bytes_per_io / val; pr_debug("dev[%p]: SE Device block_size changed to %u\n", da->da_dev, val); @@ -1125,7 +1125,7 @@ static ssize_t alua_support_store(struct config_item *item, bool flag, oldflag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -1164,7 +1164,7 @@ static ssize_t pgr_support_store(struct config_item *item, bool flag, oldflag; int ret; - ret = strtobool(page, &flag); + ret = kstrtobool(page, &flag); if (ret < 0) return ret; @@ -1186,6 +1186,23 @@ static ssize_t pgr_support_store(struct config_item *item, return count; } +static ssize_t emulate_rsoc_store(struct config_item *item, + const char *page, size_t count) +{ + struct se_dev_attrib *da = to_attrib(item); + bool flag; + int ret; + + ret = kstrtobool(page, &flag); + if (ret < 0) + return ret; + + da->emulate_rsoc = flag; + pr_debug("dev[%p]: SE Device REPORT_SUPPORTED_OPERATION_CODES_EMULATION flag: %d\n", + da->da_dev, flag); + return count; +} + CONFIGFS_ATTR(, emulate_model_alias); CONFIGFS_ATTR(, emulate_dpo); CONFIGFS_ATTR(, emulate_fua_write); @@ -1198,6 +1215,7 @@ CONFIGFS_ATTR(, emulate_tpws); CONFIGFS_ATTR(, emulate_caw); CONFIGFS_ATTR(, emulate_3pc); CONFIGFS_ATTR(, emulate_pr); +CONFIGFS_ATTR(, emulate_rsoc); CONFIGFS_ATTR(, pi_prot_type); CONFIGFS_ATTR_RO(, hw_pi_prot_type); CONFIGFS_ATTR(, pi_prot_format); @@ -1261,6 +1279,7 @@ struct configfs_attribute *sbc_attrib_attrs[] = { &attr_max_write_same_len, &attr_alua_support, &attr_pgr_support, + &attr_emulate_rsoc, NULL, }; EXPORT_SYMBOL(sbc_attrib_attrs); diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index cb4f7cc02f8f..f6e58410ec3f 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -804,6 +804,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->dev_attrib.emulate_caw = DA_EMULATE_CAW; dev->dev_attrib.emulate_3pc = DA_EMULATE_3PC; dev->dev_attrib.emulate_pr = DA_EMULATE_PR; + dev->dev_attrib.emulate_rsoc = DA_EMULATE_RSOC; dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE0_PROT; dev->dev_attrib.enforce_pr_isids = DA_ENFORCE_PR_ISIDS; dev->dev_attrib.force_pr_aptpl = DA_FORCE_PR_APTPL; diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 95a88f6224cd..67b18a67317a 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -11,6 +11,7 @@ * ****************************************************************************/ +#include <linux/kstrtox.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/utsname.h> @@ -829,7 +830,7 @@ static ssize_t target_fabric_tpg_base_enable_store(struct config_item *item, int ret; bool op; - ret = strtobool(page, &op); + ret = kstrtobool(page, &op); if (ret) return ret; diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 7e81a53dbf3c..fd584111da45 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -193,7 +193,6 @@ static int fd_configure_device(struct se_device *dev) } dev->dev_attrib.hw_block_size = fd_dev->fd_block_size; - dev->dev_attrib.max_bytes_per_io = FD_MAX_BYTES; dev->dev_attrib.hw_max_sectors = FD_MAX_BYTES / fd_dev->fd_block_size; dev->dev_attrib.hw_queue_depth = FD_MAX_DEVICE_QUEUE_DEPTH; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index d9266cf558dc..cc838ffd1294 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -124,7 +124,9 @@ static int iblock_configure_device(struct se_device *dev) q = bdev_get_queue(bd); dev->dev_attrib.hw_block_size = bdev_logical_block_size(bd); - dev->dev_attrib.hw_max_sectors = queue_max_hw_sectors(q); + dev->dev_attrib.hw_max_sectors = mult_frac(queue_max_hw_sectors(q), + SECTOR_SIZE, + dev->dev_attrib.hw_block_size); dev->dev_attrib.hw_queue_depth = q->nr_requests; /* diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 1e3216de1e04..7536ca797606 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -270,14 +270,6 @@ static inline unsigned long long transport_lba_64(unsigned char *cdb) return get_unaligned_be64(&cdb[2]); } -/* - * For VARIABLE_LENGTH_CDB w/ 32 byte extended CDBs - */ -static inline unsigned long long transport_lba_64_ext(unsigned char *cdb) -{ - return get_unaligned_be64(&cdb[12]); -} - static sense_reason_t sbc_setup_write_same(struct se_cmd *cmd, unsigned char flags, struct sbc_ops *ops) { @@ -454,12 +446,22 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes sense_reason_t ret = TCM_NO_SENSE; int i; - /* - * Handle early failure in transport_generic_request_failure(), - * which will not have taken ->caw_sem yet.. - */ - if (!success && (!cmd->t_data_sg || !cmd->t_bidi_data_sg)) - return TCM_NO_SENSE; + if (!success) { + /* + * Handle early failure in transport_generic_request_failure(), + * which will not have taken ->caw_sem yet.. + */ + if (!cmd->t_data_sg || !cmd->t_bidi_data_sg) + return TCM_NO_SENSE; + + /* + * The command has been stopped or aborted so + * we don't have to perform the write operation. + */ + WARN_ON(!(cmd->transport_state & + (CMD_T_ABORTED | CMD_T_STOP))); + goto out; + } /* * Handle special case for zero-length COMPARE_AND_WRITE */ diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 7cca3b15472b..fcc7b10a7ae3 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -227,7 +227,7 @@ spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf) struct t10_alua_tg_pt_gp *tg_pt_gp; unsigned char *prod = &dev->t10_wwn.model[0]; u32 prod_len; - u32 unit_serial_len, off = 0; + u32 off = 0; u16 len = 0, id_len; off = 4; @@ -272,13 +272,9 @@ check_t10_vend_desc: prod_len += strlen(prod); prod_len++; /* For : */ - if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) { - unit_serial_len = strlen(&dev->t10_wwn.unit_serial[0]); - unit_serial_len++; /* For NULL Terminator */ - + if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) id_len += sprintf(&buf[off+12], "%s:%s", prod, &dev->t10_wwn.unit_serial[0]); - } buf[off] = 0x2; /* ASCII */ buf[off+1] = 0x1; /* T10 Vendor ID */ buf[off+2] = 0x0; @@ -519,6 +515,7 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) struct se_device *dev = cmd->se_dev; u32 mtl = 0; int have_tp = 0, opt, min; + u32 io_max_blocks; /* * Following spc3r22 section 6.5.3 Block Limits VPD page, when @@ -557,7 +554,10 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE) / dev->dev_attrib.block_size; } - put_unaligned_be32(min_not_zero(mtl, dev->dev_attrib.hw_max_sectors), &buf[8]); + io_max_blocks = mult_frac(dev->dev_attrib.hw_max_sectors, + dev->dev_attrib.hw_block_size, + dev->dev_attrib.block_size); + put_unaligned_be32(min_not_zero(mtl, io_max_blocks), &buf[8]); /* * Set OPTIMAL TRANSFER LENGTH @@ -1314,6 +1314,922 @@ spc_emulate_testunitready(struct se_cmd *cmd) return 0; } +static void set_dpofua_usage_bits(u8 *usage_bits, struct se_device *dev) +{ + if (!target_check_fua(dev)) + usage_bits[1] &= ~0x18; + else + usage_bits[1] |= 0x18; +} + +static void set_dpofua_usage_bits32(u8 *usage_bits, struct se_device *dev) +{ + if (!target_check_fua(dev)) + usage_bits[10] &= ~0x18; + else + usage_bits[10] |= 0x18; +} + +static struct target_opcode_descriptor tcm_opcode_read6 = { + .support = SCSI_SUPPORT_FULL, + .opcode = READ_6, + .cdb_size = 6, + .usage_bits = {READ_6, 0x1f, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_read10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = READ_10, + .cdb_size = 10, + .usage_bits = {READ_10, 0xf8, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_read12 = { + .support = SCSI_SUPPORT_FULL, + .opcode = READ_12, + .cdb_size = 12, + .usage_bits = {READ_12, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_read16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = READ_16, + .cdb_size = 16, + .usage_bits = {READ_16, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_write6 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_6, + .cdb_size = 6, + .usage_bits = {WRITE_6, 0x1f, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_write10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_10, + .cdb_size = 10, + .usage_bits = {WRITE_10, 0xf8, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_write_verify10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_VERIFY, + .cdb_size = 10, + .usage_bits = {WRITE_VERIFY, 0xf0, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_write12 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_12, + .cdb_size = 12, + .usage_bits = {WRITE_12, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_write16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_16, + .cdb_size = 16, + .usage_bits = {WRITE_16, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_write_verify16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_VERIFY_16, + .cdb_size = 16, + .usage_bits = {WRITE_VERIFY_16, 0xf0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static bool tcm_is_ws_enabled(struct se_cmd *cmd) +{ + struct sbc_ops *ops = cmd->protocol_data; + struct se_device *dev = cmd->se_dev; + + return (dev->dev_attrib.emulate_tpws && !!ops->execute_unmap) || + !!ops->execute_write_same; +} + +static struct target_opcode_descriptor tcm_opcode_write_same32 = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = VARIABLE_LENGTH_CMD, + .service_action = WRITE_SAME_32, + .cdb_size = 32, + .usage_bits = {VARIABLE_LENGTH_CMD, SCSI_CONTROL_MASK, 0x00, 0x00, + 0x00, 0x00, SCSI_GROUP_NUMBER_MASK, 0x18, + 0x00, WRITE_SAME_32, 0xe8, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff}, + .enabled = tcm_is_ws_enabled, + .update_usage_bits = set_dpofua_usage_bits32, +}; + +static bool tcm_is_caw_enabled(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + return dev->dev_attrib.emulate_caw; +} + +static struct target_opcode_descriptor tcm_opcode_compare_write = { + .support = SCSI_SUPPORT_FULL, + .opcode = COMPARE_AND_WRITE, + .cdb_size = 16, + .usage_bits = {COMPARE_AND_WRITE, 0x18, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .enabled = tcm_is_caw_enabled, + .update_usage_bits = set_dpofua_usage_bits, +}; + +static struct target_opcode_descriptor tcm_opcode_read_capacity = { + .support = SCSI_SUPPORT_FULL, + .opcode = READ_CAPACITY, + .cdb_size = 10, + .usage_bits = {READ_CAPACITY, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, + 0x01, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_read_capacity16 = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = SERVICE_ACTION_IN_16, + .service_action = SAI_READ_CAPACITY_16, + .cdb_size = 16, + .usage_bits = {SERVICE_ACTION_IN_16, SAI_READ_CAPACITY_16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, +}; + +static bool tcm_is_rep_ref_enabled(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + spin_lock(&dev->t10_alua.lba_map_lock); + if (list_empty(&dev->t10_alua.lba_map_list)) { + spin_unlock(&dev->t10_alua.lba_map_lock); + return false; + } + spin_unlock(&dev->t10_alua.lba_map_lock); + return true; + +} + +static struct target_opcode_descriptor tcm_opcode_read_report_refferals = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = SERVICE_ACTION_IN_16, + .service_action = SAI_REPORT_REFERRALS, + .cdb_size = 16, + .usage_bits = {SERVICE_ACTION_IN_16, SAI_REPORT_REFERRALS, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, + .enabled = tcm_is_rep_ref_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_sync_cache = { + .support = SCSI_SUPPORT_FULL, + .opcode = SYNCHRONIZE_CACHE, + .cdb_size = 10, + .usage_bits = {SYNCHRONIZE_CACHE, 0x02, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_sync_cache16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = SYNCHRONIZE_CACHE_16, + .cdb_size = 16, + .usage_bits = {SYNCHRONIZE_CACHE_16, 0x02, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, +}; + +static bool tcm_is_unmap_enabled(struct se_cmd *cmd) +{ + struct sbc_ops *ops = cmd->protocol_data; + struct se_device *dev = cmd->se_dev; + + return ops->execute_unmap && dev->dev_attrib.emulate_tpu; +} + +static struct target_opcode_descriptor tcm_opcode_unmap = { + .support = SCSI_SUPPORT_FULL, + .opcode = UNMAP, + .cdb_size = 10, + .usage_bits = {UNMAP, 0x00, 0x00, 0x00, + 0x00, 0x00, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_unmap_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_write_same = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_SAME, + .cdb_size = 10, + .usage_bits = {WRITE_SAME, 0xe8, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_ws_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_write_same16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_SAME_16, + .cdb_size = 16, + .usage_bits = {WRITE_SAME_16, 0xe8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .enabled = tcm_is_ws_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_verify = { + .support = SCSI_SUPPORT_FULL, + .opcode = VERIFY, + .cdb_size = 10, + .usage_bits = {VERIFY, 0x00, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_verify16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = VERIFY_16, + .cdb_size = 16, + .usage_bits = {VERIFY_16, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_start_stop = { + .support = SCSI_SUPPORT_FULL, + .opcode = START_STOP, + .cdb_size = 6, + .usage_bits = {START_STOP, 0x01, 0x00, 0x00, + 0x01, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_mode_select = { + .support = SCSI_SUPPORT_FULL, + .opcode = MODE_SELECT, + .cdb_size = 6, + .usage_bits = {MODE_SELECT, 0x10, 0x00, 0x00, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_mode_select10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = MODE_SELECT_10, + .cdb_size = 10, + .usage_bits = {MODE_SELECT_10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_mode_sense = { + .support = SCSI_SUPPORT_FULL, + .opcode = MODE_SENSE, + .cdb_size = 6, + .usage_bits = {MODE_SENSE, 0x08, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_mode_sense10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = MODE_SENSE_10, + .cdb_size = 10, + .usage_bits = {MODE_SENSE_10, 0x18, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_pri_read_keys = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_IN, + .service_action = PRI_READ_KEYS, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_IN, PRI_READ_KEYS, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_pri_read_resrv = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_IN, + .service_action = PRI_READ_RESERVATION, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_IN, PRI_READ_RESERVATION, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static bool tcm_is_pr_enabled(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + return dev->dev_attrib.emulate_pr; +} + +static struct target_opcode_descriptor tcm_opcode_pri_read_caps = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_IN, + .service_action = PRI_REPORT_CAPABILITIES, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_IN, PRI_REPORT_CAPABILITIES, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pri_read_full_status = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_IN, + .service_action = PRI_READ_FULL_STATUS, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_IN, PRI_READ_FULL_STATUS, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_register = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_REGISTER, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_REGISTER, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_reserve = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_RESERVE, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_RESERVE, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_release = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_RELEASE, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_RELEASE, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_clear = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_CLEAR, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_CLEAR, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_preempt = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_PREEMPT, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_PREEMPT, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_preempt_abort = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_PREEMPT_AND_ABORT, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_PREEMPT_AND_ABORT, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_reg_ign_exist = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_REGISTER_AND_IGNORE_EXISTING_KEY, + .cdb_size = 10, + .usage_bits = { + PERSISTENT_RESERVE_OUT, PRO_REGISTER_AND_IGNORE_EXISTING_KEY, + 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_pro_register_move = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = PERSISTENT_RESERVE_OUT, + .service_action = PRO_REGISTER_AND_MOVE, + .cdb_size = 10, + .usage_bits = {PERSISTENT_RESERVE_OUT, PRO_REGISTER_AND_MOVE, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_pr_enabled, +}; + +static bool tcm_is_scsi2_reservations_enabled(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + return dev->dev_attrib.emulate_pr; +} + +static struct target_opcode_descriptor tcm_opcode_release = { + .support = SCSI_SUPPORT_FULL, + .opcode = RELEASE, + .cdb_size = 6, + .usage_bits = {RELEASE, 0x00, 0x00, 0x00, + 0x00, SCSI_CONTROL_MASK}, + .enabled = tcm_is_scsi2_reservations_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_release10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = RELEASE_10, + .cdb_size = 10, + .usage_bits = {RELEASE_10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_scsi2_reservations_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_reserve = { + .support = SCSI_SUPPORT_FULL, + .opcode = RESERVE, + .cdb_size = 6, + .usage_bits = {RESERVE, 0x00, 0x00, 0x00, + 0x00, SCSI_CONTROL_MASK}, + .enabled = tcm_is_scsi2_reservations_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_reserve10 = { + .support = SCSI_SUPPORT_FULL, + .opcode = RESERVE_10, + .cdb_size = 10, + .usage_bits = {RESERVE_10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, SCSI_CONTROL_MASK}, + .enabled = tcm_is_scsi2_reservations_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_request_sense = { + .support = SCSI_SUPPORT_FULL, + .opcode = REQUEST_SENSE, + .cdb_size = 6, + .usage_bits = {REQUEST_SENSE, 0x00, 0x00, 0x00, + 0xff, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_inquiry = { + .support = SCSI_SUPPORT_FULL, + .opcode = INQUIRY, + .cdb_size = 6, + .usage_bits = {INQUIRY, 0x01, 0xff, 0xff, + 0xff, SCSI_CONTROL_MASK}, +}; + +static bool tcm_is_3pc_enabled(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + return dev->dev_attrib.emulate_3pc; +} + +static struct target_opcode_descriptor tcm_opcode_extended_copy_lid1 = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = EXTENDED_COPY, + .cdb_size = 16, + .usage_bits = {EXTENDED_COPY, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, + .enabled = tcm_is_3pc_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_rcv_copy_res_op_params = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = RECEIVE_COPY_RESULTS, + .service_action = RCR_SA_OPERATING_PARAMETERS, + .cdb_size = 16, + .usage_bits = {RECEIVE_COPY_RESULTS, RCR_SA_OPERATING_PARAMETERS, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, + .enabled = tcm_is_3pc_enabled, +}; + +static struct target_opcode_descriptor tcm_opcode_report_luns = { + .support = SCSI_SUPPORT_FULL, + .opcode = REPORT_LUNS, + .cdb_size = 12, + .usage_bits = {REPORT_LUNS, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_test_unit_ready = { + .support = SCSI_SUPPORT_FULL, + .opcode = TEST_UNIT_READY, + .cdb_size = 6, + .usage_bits = {TEST_UNIT_READY, 0x00, 0x00, 0x00, + 0x00, SCSI_CONTROL_MASK}, +}; + +static struct target_opcode_descriptor tcm_opcode_report_target_pgs = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = MAINTENANCE_IN, + .service_action = MI_REPORT_TARGET_PGS, + .cdb_size = 12, + .usage_bits = {MAINTENANCE_IN, 0xE0 | MI_REPORT_TARGET_PGS, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, +}; + + +static bool spc_rsoc_enabled(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + + return dev->dev_attrib.emulate_rsoc; +} + +static struct target_opcode_descriptor tcm_opcode_report_supp_opcodes = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = MAINTENANCE_IN, + .service_action = MI_REPORT_SUPPORTED_OPERATION_CODES, + .cdb_size = 12, + .usage_bits = {MAINTENANCE_IN, MI_REPORT_SUPPORTED_OPERATION_CODES, + 0x87, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, + .enabled = spc_rsoc_enabled, +}; + +static bool tcm_is_set_tpg_enabled(struct se_cmd *cmd) +{ + struct t10_alua_tg_pt_gp *l_tg_pt_gp; + struct se_lun *l_lun = cmd->se_lun; + + rcu_read_lock(); + l_tg_pt_gp = rcu_dereference(l_lun->lun_tg_pt_gp); + if (!l_tg_pt_gp) { + rcu_read_unlock(); + return false; + } + if (!(l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA)) { + rcu_read_unlock(); + return false; + } + rcu_read_unlock(); + + return true; +} + +static struct target_opcode_descriptor tcm_opcode_set_tpg = { + .support = SCSI_SUPPORT_FULL, + .serv_action_valid = 1, + .opcode = MAINTENANCE_OUT, + .service_action = MO_SET_TARGET_PGS, + .cdb_size = 12, + .usage_bits = {MAINTENANCE_OUT, MO_SET_TARGET_PGS, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, SCSI_CONTROL_MASK}, + .enabled = tcm_is_set_tpg_enabled, +}; + +static struct target_opcode_descriptor *tcm_supported_opcodes[] = { + &tcm_opcode_read6, + &tcm_opcode_read10, + &tcm_opcode_read12, + &tcm_opcode_read16, + &tcm_opcode_write6, + &tcm_opcode_write10, + &tcm_opcode_write_verify10, + &tcm_opcode_write12, + &tcm_opcode_write16, + &tcm_opcode_write_verify16, + &tcm_opcode_write_same32, + &tcm_opcode_compare_write, + &tcm_opcode_read_capacity, + &tcm_opcode_read_capacity16, + &tcm_opcode_read_report_refferals, + &tcm_opcode_sync_cache, + &tcm_opcode_sync_cache16, + &tcm_opcode_unmap, + &tcm_opcode_write_same, + &tcm_opcode_write_same16, + &tcm_opcode_verify, + &tcm_opcode_verify16, + &tcm_opcode_start_stop, + &tcm_opcode_mode_select, + &tcm_opcode_mode_select10, + &tcm_opcode_mode_sense, + &tcm_opcode_mode_sense10, + &tcm_opcode_pri_read_keys, + &tcm_opcode_pri_read_resrv, + &tcm_opcode_pri_read_caps, + &tcm_opcode_pri_read_full_status, + &tcm_opcode_pro_register, + &tcm_opcode_pro_reserve, + &tcm_opcode_pro_release, + &tcm_opcode_pro_clear, + &tcm_opcode_pro_preempt, + &tcm_opcode_pro_preempt_abort, + &tcm_opcode_pro_reg_ign_exist, + &tcm_opcode_pro_register_move, + &tcm_opcode_release, + &tcm_opcode_release10, + &tcm_opcode_reserve, + &tcm_opcode_reserve10, + &tcm_opcode_request_sense, + &tcm_opcode_inquiry, + &tcm_opcode_extended_copy_lid1, + &tcm_opcode_rcv_copy_res_op_params, + &tcm_opcode_report_luns, + &tcm_opcode_test_unit_ready, + &tcm_opcode_report_target_pgs, + &tcm_opcode_report_supp_opcodes, + &tcm_opcode_set_tpg, +}; + +static int +spc_rsoc_encode_command_timeouts_descriptor(unsigned char *buf, u8 ctdp, + struct target_opcode_descriptor *descr) +{ + if (!ctdp) + return 0; + + put_unaligned_be16(0xa, buf); + buf[3] = descr->specific_timeout; + put_unaligned_be32(descr->nominal_timeout, &buf[4]); + put_unaligned_be32(descr->recommended_timeout, &buf[8]); + + return 12; +} + +static int +spc_rsoc_encode_command_descriptor(unsigned char *buf, u8 ctdp, + struct target_opcode_descriptor *descr) +{ + int td_size = 0; + + buf[0] = descr->opcode; + + put_unaligned_be16(descr->service_action, &buf[2]); + + buf[5] = (ctdp << 1) | descr->serv_action_valid; + put_unaligned_be16(descr->cdb_size, &buf[6]); + + td_size = spc_rsoc_encode_command_timeouts_descriptor(&buf[8], ctdp, + descr); + + return 8 + td_size; +} + +static int +spc_rsoc_encode_one_command_descriptor(unsigned char *buf, u8 ctdp, + struct target_opcode_descriptor *descr, + struct se_device *dev) +{ + int td_size = 0; + + if (!descr) { + buf[1] = (ctdp << 7) | SCSI_SUPPORT_NOT_SUPPORTED; + return 2; + } + + buf[1] = (ctdp << 7) | SCSI_SUPPORT_FULL; + put_unaligned_be16(descr->cdb_size, &buf[2]); + memcpy(&buf[4], descr->usage_bits, descr->cdb_size); + if (descr->update_usage_bits) + descr->update_usage_bits(&buf[4], dev); + + td_size = spc_rsoc_encode_command_timeouts_descriptor( + &buf[4 + descr->cdb_size], ctdp, descr); + + return 4 + descr->cdb_size + td_size; +} + +static sense_reason_t +spc_rsoc_get_descr(struct se_cmd *cmd, struct target_opcode_descriptor **opcode) +{ + struct target_opcode_descriptor *descr; + struct se_session *sess = cmd->se_sess; + unsigned char *cdb = cmd->t_task_cdb; + u8 opts = cdb[2] & 0x3; + u8 requested_opcode; + u16 requested_sa; + int i; + + requested_opcode = cdb[3]; + requested_sa = ((u16)cdb[4]) << 8 | cdb[5]; + *opcode = NULL; + + if (opts > 3) { + pr_debug("TARGET_CORE[%s]: Invalid REPORT SUPPORTED OPERATION CODES" + " with unsupported REPORTING OPTIONS %#x for 0x%08llx from %s\n", + cmd->se_tfo->fabric_name, opts, + cmd->se_lun->unpacked_lun, + sess->se_node_acl->initiatorname); + return TCM_INVALID_CDB_FIELD; + } + + for (i = 0; i < ARRAY_SIZE(tcm_supported_opcodes); i++) { + descr = tcm_supported_opcodes[i]; + if (descr->opcode != requested_opcode) + continue; + + switch (opts) { + case 0x1: + /* + * If the REQUESTED OPERATION CODE field specifies an + * operation code for which the device server implements + * service actions, then the device server shall + * terminate the command with CHECK CONDITION status, + * with the sense key set to ILLEGAL REQUEST, and the + * additional sense code set to INVALID FIELD IN CDB + */ + if (descr->serv_action_valid) + return TCM_INVALID_CDB_FIELD; + + if (!descr->enabled || descr->enabled(cmd)) + *opcode = descr; + break; + case 0x2: + /* + * If the REQUESTED OPERATION CODE field specifies an + * operation code for which the device server does not + * implement service actions, then the device server + * shall terminate the command with CHECK CONDITION + * status, with the sense key set to ILLEGAL REQUEST, + * and the additional sense code set to INVALID FIELD IN CDB. + */ + if (descr->serv_action_valid && + descr->service_action == requested_sa) { + if (!descr->enabled || descr->enabled(cmd)) + *opcode = descr; + } else if (!descr->serv_action_valid) + return TCM_INVALID_CDB_FIELD; + break; + case 0x3: + /* + * The command support data for the operation code and + * service action a specified in the REQUESTED OPERATION + * CODE field and REQUESTED SERVICE ACTION field shall + * be returned in the one_command parameter data format. + */ + if (descr->service_action == requested_sa) + if (!descr->enabled || descr->enabled(cmd)) + *opcode = descr; + break; + } + } + + return 0; +} + +static sense_reason_t +spc_emulate_report_supp_op_codes(struct se_cmd *cmd) +{ + int descr_num = ARRAY_SIZE(tcm_supported_opcodes); + struct target_opcode_descriptor *descr = NULL; + unsigned char *cdb = cmd->t_task_cdb; + u8 rctd = (cdb[2] >> 7) & 0x1; + unsigned char *buf = NULL; + int response_length = 0; + u8 opts = cdb[2] & 0x3; + unsigned char *rbuf; + sense_reason_t ret = 0; + int i; + + if (!cmd->se_dev->dev_attrib.emulate_rsoc) + return TCM_UNSUPPORTED_SCSI_OPCODE; + + rbuf = transport_kmap_data_sg(cmd); + if (cmd->data_length && !rbuf) { + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + goto out; + } + + if (opts == 0) + response_length = 4 + (8 + rctd * 12) * descr_num; + else { + ret = spc_rsoc_get_descr(cmd, &descr); + if (ret) + goto out; + + if (descr) + response_length = 4 + descr->cdb_size + rctd * 12; + else + response_length = 2; + } + + buf = kzalloc(response_length, GFP_KERNEL); + if (!buf) { + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + goto out; + } + response_length = 0; + + if (opts == 0) { + response_length += 4; + + for (i = 0; i < ARRAY_SIZE(tcm_supported_opcodes); i++) { + descr = tcm_supported_opcodes[i]; + if (descr->enabled && !descr->enabled(cmd)) + continue; + + response_length += spc_rsoc_encode_command_descriptor( + &buf[response_length], rctd, descr); + } + put_unaligned_be32(response_length - 3, buf); + } else { + response_length = spc_rsoc_encode_one_command_descriptor( + &buf[response_length], rctd, descr, + cmd->se_dev); + } + + memcpy(rbuf, buf, min_t(u32, response_length, cmd->data_length)); +out: + kfree(buf); + transport_kunmap_data_sg(cmd); + + if (!ret) + target_complete_cmd_with_length(cmd, SAM_STAT_GOOD, response_length); + return ret; +} + sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) { @@ -1439,6 +2355,10 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) cmd->execute_cmd = target_emulate_report_target_port_groups; } + if ((cdb[1] & 0x1f) == + MI_REPORT_SUPPORTED_OPERATION_CODES) + cmd->execute_cmd = + spc_emulate_report_supp_op_codes; *size = get_unaligned_be32(&cdb[6]); } else { /* diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 8713cda0c2fb..49eaee022ef1 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -582,11 +582,11 @@ static int target_xcopy_read_source( struct xcopy_op *xop, struct se_device *src_dev, sector_t src_lba, - u32 src_sectors) + u32 src_bytes) { struct xcopy_pt_cmd xpt_cmd; struct se_cmd *se_cmd = &xpt_cmd.se_cmd; - u32 length = (src_sectors * src_dev->dev_attrib.block_size); + u32 transfer_length_block = src_bytes / src_dev->dev_attrib.block_size; int rc; unsigned char cdb[16]; bool remote_port = (xop->op_origin == XCOL_DEST_RECV_OP); @@ -597,11 +597,11 @@ static int target_xcopy_read_source( memset(&cdb[0], 0, 16); cdb[0] = READ_16; put_unaligned_be64(src_lba, &cdb[2]); - put_unaligned_be32(src_sectors, &cdb[10]); - pr_debug("XCOPY: Built READ_16: LBA: %llu Sectors: %u Length: %u\n", - (unsigned long long)src_lba, src_sectors, length); + put_unaligned_be32(transfer_length_block, &cdb[10]); + pr_debug("XCOPY: Built READ_16: LBA: %llu Blocks: %u Length: %u\n", + (unsigned long long)src_lba, transfer_length_block, src_bytes); - __target_init_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, + __target_init_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, src_bytes, DMA_FROM_DEVICE, 0, &xpt_cmd.sense_buffer[0], 0); rc = target_xcopy_setup_pt_cmd(&xpt_cmd, xop, src_dev, &cdb[0], @@ -627,11 +627,11 @@ static int target_xcopy_write_destination( struct xcopy_op *xop, struct se_device *dst_dev, sector_t dst_lba, - u32 dst_sectors) + u32 dst_bytes) { struct xcopy_pt_cmd xpt_cmd; struct se_cmd *se_cmd = &xpt_cmd.se_cmd; - u32 length = (dst_sectors * dst_dev->dev_attrib.block_size); + u32 transfer_length_block = dst_bytes / dst_dev->dev_attrib.block_size; int rc; unsigned char cdb[16]; bool remote_port = (xop->op_origin == XCOL_SOURCE_RECV_OP); @@ -642,11 +642,11 @@ static int target_xcopy_write_destination( memset(&cdb[0], 0, 16); cdb[0] = WRITE_16; put_unaligned_be64(dst_lba, &cdb[2]); - put_unaligned_be32(dst_sectors, &cdb[10]); - pr_debug("XCOPY: Built WRITE_16: LBA: %llu Sectors: %u Length: %u\n", - (unsigned long long)dst_lba, dst_sectors, length); + put_unaligned_be32(transfer_length_block, &cdb[10]); + pr_debug("XCOPY: Built WRITE_16: LBA: %llu Blocks: %u Length: %u\n", + (unsigned long long)dst_lba, transfer_length_block, dst_bytes); - __target_init_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, + __target_init_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, dst_bytes, DMA_TO_DEVICE, 0, &xpt_cmd.sense_buffer[0], 0); rc = target_xcopy_setup_pt_cmd(&xpt_cmd, xop, dst_dev, &cdb[0], @@ -670,9 +670,10 @@ static void target_xcopy_do_work(struct work_struct *work) struct se_cmd *ec_cmd = xop->xop_se_cmd; struct se_device *src_dev, *dst_dev; sector_t src_lba, dst_lba, end_lba; - unsigned int max_sectors; + unsigned long long max_bytes, max_bytes_src, max_bytes_dst, max_blocks; int rc = 0; - unsigned short nolb, max_nolb, copied_nolb = 0; + unsigned short nolb; + unsigned int copied_bytes = 0; sense_reason_t sense_rc; sense_rc = target_parse_xcopy_cmd(xop); @@ -691,23 +692,31 @@ static void target_xcopy_do_work(struct work_struct *work) nolb = xop->nolb; end_lba = src_lba + nolb; /* - * Break up XCOPY I/O into hw_max_sectors sized I/O based on the - * smallest max_sectors between src_dev + dev_dev, or + * Break up XCOPY I/O into hw_max_sectors * hw_block_size sized + * I/O based on the smallest max_bytes between src_dev + dst_dev */ - max_sectors = min(src_dev->dev_attrib.hw_max_sectors, - dst_dev->dev_attrib.hw_max_sectors); - max_sectors = min_t(u32, max_sectors, XCOPY_MAX_SECTORS); + max_bytes_src = (unsigned long long) src_dev->dev_attrib.hw_max_sectors * + src_dev->dev_attrib.hw_block_size; + max_bytes_dst = (unsigned long long) dst_dev->dev_attrib.hw_max_sectors * + dst_dev->dev_attrib.hw_block_size; - max_nolb = min_t(u16, max_sectors, ((u16)(~0U))); + max_bytes = min_t(u64, max_bytes_src, max_bytes_dst); + max_bytes = min_t(u64, max_bytes, XCOPY_MAX_BYTES); - pr_debug("target_xcopy_do_work: nolb: %hu, max_nolb: %hu end_lba: %llu\n", - nolb, max_nolb, (unsigned long long)end_lba); - pr_debug("target_xcopy_do_work: Starting src_lba: %llu, dst_lba: %llu\n", + /* + * Using shift instead of the division because otherwise GCC + * generates __udivdi3 that is missing on i386 + */ + max_blocks = max_bytes >> ilog2(src_dev->dev_attrib.block_size); + + pr_debug("%s: nolb: %u, max_blocks: %llu end_lba: %llu\n", __func__, + nolb, max_blocks, (unsigned long long)end_lba); + pr_debug("%s: Starting src_lba: %llu, dst_lba: %llu\n", __func__, (unsigned long long)src_lba, (unsigned long long)dst_lba); - while (src_lba < end_lba) { - unsigned short cur_nolb = min(nolb, max_nolb); - u32 cur_bytes = cur_nolb * src_dev->dev_attrib.block_size; + while (nolb) { + u32 cur_bytes = min_t(u64, max_bytes, nolb * src_dev->dev_attrib.block_size); + unsigned short cur_nolb = cur_bytes / src_dev->dev_attrib.block_size; if (cur_bytes != xop->xop_data_bytes) { /* @@ -724,43 +733,43 @@ static void target_xcopy_do_work(struct work_struct *work) xop->xop_data_bytes = cur_bytes; } - pr_debug("target_xcopy_do_work: Calling read src_dev: %p src_lba: %llu," - " cur_nolb: %hu\n", src_dev, (unsigned long long)src_lba, cur_nolb); + pr_debug("%s: Calling read src_dev: %p src_lba: %llu, cur_nolb: %hu\n", + __func__, src_dev, (unsigned long long)src_lba, cur_nolb); - rc = target_xcopy_read_source(ec_cmd, xop, src_dev, src_lba, cur_nolb); + rc = target_xcopy_read_source(ec_cmd, xop, src_dev, src_lba, cur_bytes); if (rc < 0) goto out; - src_lba += cur_nolb; - pr_debug("target_xcopy_do_work: Incremented READ src_lba to %llu\n", + src_lba += cur_bytes / src_dev->dev_attrib.block_size; + pr_debug("%s: Incremented READ src_lba to %llu\n", __func__, (unsigned long long)src_lba); - pr_debug("target_xcopy_do_work: Calling write dst_dev: %p dst_lba: %llu," - " cur_nolb: %hu\n", dst_dev, (unsigned long long)dst_lba, cur_nolb); + pr_debug("%s: Calling write dst_dev: %p dst_lba: %llu, cur_nolb: %u\n", + __func__, dst_dev, (unsigned long long)dst_lba, cur_nolb); rc = target_xcopy_write_destination(ec_cmd, xop, dst_dev, - dst_lba, cur_nolb); + dst_lba, cur_bytes); if (rc < 0) goto out; - dst_lba += cur_nolb; - pr_debug("target_xcopy_do_work: Incremented WRITE dst_lba to %llu\n", + dst_lba += cur_bytes / dst_dev->dev_attrib.block_size; + pr_debug("%s: Incremented WRITE dst_lba to %llu\n", __func__, (unsigned long long)dst_lba); - copied_nolb += cur_nolb; - nolb -= cur_nolb; + copied_bytes += cur_bytes; + nolb -= cur_bytes / src_dev->dev_attrib.block_size; } xcopy_pt_undepend_remotedev(xop); target_free_sgl(xop->xop_data_sg, xop->xop_data_nents); kfree(xop); - pr_debug("target_xcopy_do_work: Final src_lba: %llu, dst_lba: %llu\n", + pr_debug("%s: Final src_lba: %llu, dst_lba: %llu\n", __func__, (unsigned long long)src_lba, (unsigned long long)dst_lba); - pr_debug("target_xcopy_do_work: Blocks copied: %hu, Bytes Copied: %u\n", - copied_nolb, copied_nolb * dst_dev->dev_attrib.block_size); + pr_debug("%s: Blocks copied: %u, Bytes Copied: %u\n", __func__, + copied_bytes / dst_dev->dev_attrib.block_size, copied_bytes); - pr_debug("target_xcopy_do_work: Setting X-COPY GOOD status -> sending response\n"); + pr_debug("%s: Setting X-COPY GOOD status -> sending response\n", __func__); target_complete_cmd(ec_cmd, SAM_STAT_GOOD); return; @@ -776,8 +785,8 @@ out: err_free: kfree(xop); - pr_warn_ratelimited("target_xcopy_do_work: rc: %d, sense: %u, XCOPY operation failed\n", - rc, sense_rc); + pr_warn_ratelimited("%s: rc: %d, sense: %u, XCOPY operation failed\n", + __func__, rc, sense_rc); target_complete_cmd_with_sense(ec_cmd, SAM_STAT_CHECK_CONDITION, sense_rc); } @@ -1009,8 +1018,14 @@ sense_reason_t target_do_receive_copy_results(struct se_cmd *se_cmd) { unsigned char *cdb = &se_cmd->t_task_cdb[0]; int sa = (cdb[1] & 0x1f), list_id = cdb[2]; + struct se_device *dev = se_cmd->se_dev; sense_reason_t rc = TCM_NO_SENSE; + if (!dev->dev_attrib.emulate_3pc) { + pr_debug("Third-party copy operations explicitly disabled\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } + pr_debug("Entering target_do_receive_copy_results: SA: 0x%02x, List ID:" " 0x%02x, AL: %u\n", sa, list_id, se_cmd->data_length); diff --git a/drivers/target/target_core_xcopy.h b/drivers/target/target_core_xcopy.h index e5f20005179a..0aad7dc65895 100644 --- a/drivers/target/target_core_xcopy.h +++ b/drivers/target/target_core_xcopy.h @@ -5,7 +5,7 @@ #define XCOPY_TARGET_DESC_LEN 32 #define XCOPY_SEGMENT_DESC_LEN 28 #define XCOPY_NAA_IEEE_REGEX_LEN 16 -#define XCOPY_MAX_SECTORS 4096 +#define XCOPY_MAX_BYTES 16777216 /* 16 MB */ /* * SPC4r37 6.4.6.1 |