diff options
Diffstat (limited to 'drivers/target/target_core_xcopy.c')
| -rw-r--r-- | drivers/target/target_core_xcopy.c | 588 |
1 files changed, 244 insertions, 344 deletions
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index c2e1fc927fdf..93534a6e14b7 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * Filename: target_core_xcopy.c * @@ -9,25 +10,16 @@ * Author: * Nicholas A. Bellinger <nab@daterainc.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * ******************************************************************************/ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/list.h> +#include <linux/rculist.h> #include <linux/configfs.h> #include <linux/ratelimit.h> #include <scsi/scsi_proto.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <target/target_core_base.h> #include <target/target_core_backend.h> @@ -42,73 +34,83 @@ static struct workqueue_struct *xcopy_wq = NULL; static sense_reason_t target_parse_xcopy_cmd(struct xcopy_op *xop); -static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf) -{ - int off = 0; - - buf[off++] = (0x6 << 4); - buf[off++] = 0x01; - buf[off++] = 0x40; - buf[off] = (0x5 << 4); - - spc_parse_naa_6h_vendor_specific(dev, &buf[off]); - return 0; -} - -struct xcopy_dev_search_info { - const unsigned char *dev_wwn; - struct se_device *found_dev; -}; - +/** + * target_xcopy_locate_se_dev_e4_iter - compare XCOPY NAA device identifiers + * + * @se_dev: device being considered for match + * @dev_wwn: XCOPY requested NAA dev_wwn + * @return: 1 on match, 0 on no-match + */ static int target_xcopy_locate_se_dev_e4_iter(struct se_device *se_dev, - void *data) + const unsigned char *dev_wwn) { - struct xcopy_dev_search_info *info = data; unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN]; int rc; - if (!se_dev->dev_attrib.emulate_3pc) + if (!se_dev->dev_attrib.emulate_3pc) { + pr_debug("XCOPY: emulate_3pc disabled on se_dev %p\n", se_dev); return 0; + } memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN); - target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]); + spc_gen_naa_6h_vendor_specific(se_dev, &tmp_dev_wwn[0]); - rc = memcmp(&tmp_dev_wwn[0], info->dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN); - if (rc != 0) - return 0; - - info->found_dev = se_dev; - pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev); - - rc = target_depend_item(&se_dev->dev_group.cg_item); + rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN); if (rc != 0) { - pr_err("configfs_depend_item attempt failed: %d for se_dev: %p\n", - rc, se_dev); - return rc; + pr_debug("XCOPY: skip non-matching: %*ph\n", + XCOPY_NAA_IEEE_REGEX_LEN, tmp_dev_wwn); + return 0; } + pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev); - pr_debug("Called configfs_depend_item for se_dev: %p se_dev->se_dev_group: %p\n", - se_dev, &se_dev->dev_group); return 1; } -static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn, - struct se_device **found_dev) +static int target_xcopy_locate_se_dev_e4(struct se_session *sess, + const unsigned char *dev_wwn, + struct se_device **_found_dev, + struct percpu_ref **_found_lun_ref) { - struct xcopy_dev_search_info info; - int ret; - - memset(&info, 0, sizeof(info)); - info.dev_wwn = dev_wwn; - - ret = target_for_each_device(target_xcopy_locate_se_dev_e4_iter, &info); - if (ret == 1) { - *found_dev = info.found_dev; - return 0; - } else { - pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n"); - return -EINVAL; + struct se_dev_entry *deve; + struct se_node_acl *nacl; + struct se_lun *this_lun = NULL; + struct se_device *found_dev = NULL; + + /* cmd with NULL sess indicates no associated $FABRIC_MOD */ + if (!sess) + goto err_out; + + pr_debug("XCOPY 0xe4: searching for: %*ph\n", + XCOPY_NAA_IEEE_REGEX_LEN, dev_wwn); + + nacl = sess->se_node_acl; + rcu_read_lock(); + hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { + struct se_device *this_dev; + int rc; + + this_lun = deve->se_lun; + this_dev = rcu_dereference_raw(this_lun->lun_se_dev); + + rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn); + if (rc) { + if (percpu_ref_tryget_live(&this_lun->lun_ref)) + found_dev = this_dev; + break; + } } + rcu_read_unlock(); + if (found_dev == NULL) + goto err_out; + + pr_debug("lun_ref held for se_dev: %p se_dev->se_dev_group: %p\n", + found_dev, &found_dev->dev_group); + *_found_dev = found_dev; + *_found_lun_ref = &this_lun->lun_ref; + return 0; +err_out: + pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n"); + return -EINVAL; } static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop, @@ -143,7 +145,7 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op * Assigned designator */ desig_len = desc[7]; - if (desig_len != 16) { + if (desig_len != XCOPY_NAA_IEEE_REGEX_LEN) { pr_err("XCOPY 0xe4: invalid desig_len: %d\n", (int)desig_len); return -EINVAL; } @@ -227,7 +229,7 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd, * se_device the XCOPY was received upon.. */ memset(&xop->local_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN); - target_xcopy_gen_naa_ieee(local_dev, &xop->local_dev_wwn[0]); + spc_gen_naa_6h_vendor_specific(local_dev, &xop->local_dev_wwn[0]); while (start < tdll) { /* @@ -255,12 +257,16 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd, switch (xop->op_origin) { case XCOL_SOURCE_RECV_OP: - rc = target_xcopy_locate_se_dev_e4(xop->dst_tid_wwn, - &xop->dst_dev); + rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess, + xop->dst_tid_wwn, + &xop->dst_dev, + &xop->remote_lun_ref); break; case XCOL_DEST_RECV_OP: - rc = target_xcopy_locate_se_dev_e4(xop->src_tid_wwn, - &xop->src_dev); + rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess, + xop->src_tid_wwn, + &xop->src_dev, + &xop->remote_lun_ref); break; default: pr_err("XCOPY CSCD descriptor IDs not found in CSCD list - " @@ -290,8 +296,7 @@ out: return -EINVAL; } -static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op *xop, - unsigned char *p) +static int target_xcopy_parse_segdesc_02(struct xcopy_op *xop, unsigned char *p) { unsigned char *desc = p; int dc = (desc[1] & 0x02); @@ -324,17 +329,12 @@ static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op xop->nolb, (unsigned long long)xop->src_lba, (unsigned long long)xop->dst_lba); - if (dc != 0) { - xop->dbl = get_unaligned_be24(&desc[29]); - - pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl); - } return 0; } -static int target_xcopy_parse_segment_descriptors(struct se_cmd *se_cmd, - struct xcopy_op *xop, unsigned char *p, - unsigned int sdll, sense_reason_t *sense_ret) +static int target_xcopy_parse_segment_descriptors(struct xcopy_op *xop, + unsigned char *p, unsigned int sdll, + sense_reason_t *sense_ret) { unsigned char *desc = p; unsigned int start = 0; @@ -362,7 +362,7 @@ static int target_xcopy_parse_segment_descriptors(struct se_cmd *se_cmd, */ switch (desc[0]) { case 0x02: - rc = target_xcopy_parse_segdesc_02(se_cmd, xop, desc); + rc = target_xcopy_parse_segdesc_02(xop, desc); if (rc < 0) goto out; @@ -389,7 +389,6 @@ out: */ struct xcopy_pt_cmd { - bool remote_port; struct se_cmd se_cmd; struct completion xpt_passthrough_sem; unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER]; @@ -406,18 +405,12 @@ static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd) static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop) { - struct se_device *remote_dev; - if (xop->op_origin == XCOL_SOURCE_RECV_OP) - remote_dev = xop->dst_dev; + pr_debug("putting dst lun_ref for %p\n", xop->dst_dev); else - remote_dev = xop->src_dev; - - pr_debug("Calling configfs_undepend_item for" - " remote_dev: %p remote_dev->dev_group: %p\n", - remote_dev, &remote_dev->dev_group.cg_item); + pr_debug("putting src lun_ref for %p\n", xop->src_dev); - target_undepend_item(&remote_dev->dev_group.cg_item); + percpu_ref_put(xop->remote_lun_ref); } static void xcopy_pt_release_cmd(struct se_cmd *se_cmd) @@ -425,7 +418,8 @@ static void xcopy_pt_release_cmd(struct se_cmd *se_cmd) struct xcopy_pt_cmd *xpt_cmd = container_of(se_cmd, struct xcopy_pt_cmd, se_cmd); - kfree(xpt_cmd); + /* xpt_cmd is on the stack, nothing to free here */ + pr_debug("xpt_cmd done: %p\n", xpt_cmd); } static int xcopy_pt_check_stop_free(struct se_cmd *se_cmd) @@ -442,11 +436,6 @@ static int xcopy_pt_write_pending(struct se_cmd *se_cmd) return 0; } -static int xcopy_pt_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static int xcopy_pt_queue_data_in(struct se_cmd *se_cmd) { return 0; @@ -463,7 +452,6 @@ static const struct target_core_fabric_ops xcopy_pt_tfo = { .release_cmd = xcopy_pt_release_cmd, .check_stop_free = xcopy_pt_check_stop_free, .write_pending = xcopy_pt_write_pending, - .write_pending_status = xcopy_pt_write_pending_status, .queue_data_in = xcopy_pt_queue_data_in, .queue_status = xcopy_pt_queue_status, }; @@ -474,16 +462,13 @@ static const struct target_core_fabric_ops xcopy_pt_tfo = { int target_xcopy_setup_pt(void) { - int ret; - - xcopy_wq = alloc_workqueue("xcopy_wq", WQ_MEM_RECLAIM, 0); + xcopy_wq = alloc_workqueue("xcopy_wq", WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!xcopy_wq) { pr_err("Unable to allocate xcopy_wq\n"); return -ENOMEM; } memset(&xcopy_pt_tpg, 0, sizeof(struct se_portal_group)); - INIT_LIST_HEAD(&xcopy_pt_tpg.se_tpg_node); INIT_LIST_HEAD(&xcopy_pt_tpg.acl_node_list); INIT_LIST_HEAD(&xcopy_pt_tpg.tpg_sess_list); @@ -493,9 +478,7 @@ int target_xcopy_setup_pt(void) INIT_LIST_HEAD(&xcopy_pt_nacl.acl_list); INIT_LIST_HEAD(&xcopy_pt_nacl.acl_sess_list); memset(&xcopy_pt_sess, 0, sizeof(struct se_session)); - ret = transport_init_session(&xcopy_pt_sess); - if (ret < 0) - return ret; + transport_init_session(&xcopy_pt_sess); xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg; xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess; @@ -512,131 +495,56 @@ void target_xcopy_release_pt(void) destroy_workqueue(xcopy_wq); } -static void target_xcopy_setup_pt_port( - struct xcopy_pt_cmd *xpt_cmd, - struct xcopy_op *xop, - bool remote_port) -{ - struct se_cmd *ec_cmd = xop->xop_se_cmd; - struct se_cmd *pt_cmd = &xpt_cmd->se_cmd; - - if (xop->op_origin == XCOL_SOURCE_RECV_OP) { - /* - * Honor destination port reservations for X-COPY PUSH emulation - * when CDB is received on local source port, and READs blocks to - * WRITE on remote destination port. - */ - if (remote_port) { - xpt_cmd->remote_port = remote_port; - } else { - pt_cmd->se_lun = ec_cmd->se_lun; - pt_cmd->se_dev = ec_cmd->se_dev; - - pr_debug("Honoring local SRC port from ec_cmd->se_dev:" - " %p\n", pt_cmd->se_dev); - pt_cmd->se_lun = ec_cmd->se_lun; - pr_debug("Honoring local SRC port from ec_cmd->se_lun: %p\n", - pt_cmd->se_lun); - } - } else { - /* - * Honor source port reservation for X-COPY PULL emulation - * when CDB is received on local desintation port, and READs - * blocks from the remote source port to WRITE on local - * destination port. - */ - if (remote_port) { - xpt_cmd->remote_port = remote_port; - } else { - pt_cmd->se_lun = ec_cmd->se_lun; - pt_cmd->se_dev = ec_cmd->se_dev; - - pr_debug("Honoring local DST port from ec_cmd->se_dev:" - " %p\n", pt_cmd->se_dev); - pt_cmd->se_lun = ec_cmd->se_lun; - pr_debug("Honoring local DST port from ec_cmd->se_lun: %p\n", - pt_cmd->se_lun); - } - } -} - -static void target_xcopy_init_pt_lun(struct se_device *se_dev, - struct se_cmd *pt_cmd, bool remote_port) -{ - /* - * Don't allocate + init an pt_cmd->se_lun if honoring local port for - * reservations. The pt_cmd->se_lun pointer will be setup from within - * target_xcopy_setup_pt_port() - */ - if (remote_port) { - pr_debug("Setup emulated se_dev: %p from se_dev\n", - pt_cmd->se_dev); - pt_cmd->se_lun = &se_dev->xcopy_lun; - pt_cmd->se_dev = se_dev; - } - - pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; -} - +/* + * target_xcopy_setup_pt_cmd - set up a pass-through command + * @xpt_cmd: Data structure to initialize. + * @xop: Describes the XCOPY operation received from an initiator. + * @se_dev: Backend device to associate with @xpt_cmd if + * @remote_port == true. + * @cdb: SCSI CDB to be copied into @xpt_cmd. + * @remote_port: If false, use the LUN through which the XCOPY command has + * been received. If true, use @se_dev->xcopy_lun. + * + * Set up a SCSI command (READ or WRITE) that will be used to execute an + * XCOPY command. + */ static int target_xcopy_setup_pt_cmd( struct xcopy_pt_cmd *xpt_cmd, struct xcopy_op *xop, struct se_device *se_dev, unsigned char *cdb, - bool remote_port, - bool alloc_mem) + bool remote_port) { struct se_cmd *cmd = &xpt_cmd->se_cmd; - sense_reason_t sense_rc; - int ret = 0, rc; + /* * Setup LUN+port to honor reservations based upon xop->op_origin for * X-COPY PUSH or X-COPY PULL based upon where the CDB was received. */ - target_xcopy_init_pt_lun(se_dev, cmd, remote_port); - target_xcopy_setup_pt_port(xpt_cmd, xop, remote_port); + if (remote_port) { + cmd->se_lun = &se_dev->xcopy_lun; + cmd->se_dev = se_dev; + } else { + cmd->se_lun = xop->xop_se_cmd->se_lun; + cmd->se_dev = xop->xop_se_cmd->se_dev; + } + cmd->se_cmd_flags |= SCF_SE_LUN_CMD; + + if (target_cmd_init_cdb(cmd, cdb, GFP_KERNEL)) + return -EINVAL; cmd->tag = 0; - sense_rc = target_setup_cmd_from_cdb(cmd, cdb); - if (sense_rc) { - ret = -EINVAL; - goto out; - } + if (target_cmd_parse_cdb(cmd)) + return -EINVAL; - if (alloc_mem) { - rc = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents, - cmd->data_length, false, false); - if (rc < 0) { - ret = rc; - goto out; - } - /* - * Set this bit so that transport_free_pages() allows the - * caller to release SGLs + physical memory allocated by - * transport_generic_get_mem().. - */ - cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC; - } else { - /* - * Here the previously allocated SGLs for the internal READ - * are mapped zero-copy to the internal WRITE. - */ - sense_rc = transport_generic_map_mem_to_cmd(cmd, - xop->xop_data_sg, xop->xop_data_nents, - NULL, 0); - if (sense_rc) { - ret = -EINVAL; - goto out; - } + if (transport_generic_map_mem_to_cmd(cmd, xop->xop_data_sg, + xop->xop_data_nents, NULL, 0)) + return -EINVAL; - pr_debug("Setup PASSTHROUGH_NOALLOC t_data_sg: %p t_data_nents:" - " %u\n", cmd->t_data_sg, cmd->t_data_nents); - } + pr_debug("Setup PASSTHROUGH_NOALLOC t_data_sg: %p t_data_nents:" + " %u\n", cmd->t_data_sg, cmd->t_data_nents); return 0; - -out: - return ret; } static int target_xcopy_issue_pt_cmd(struct xcopy_pt_cmd *xpt_cmd) @@ -664,61 +572,44 @@ 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; - u32 length = (src_sectors * src_dev->dev_attrib.block_size); + struct xcopy_pt_cmd xpt_cmd; + struct se_cmd *se_cmd = &xpt_cmd.se_cmd; + 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); - xpt_cmd = kzalloc(sizeof(struct xcopy_pt_cmd), GFP_KERNEL); - if (!xpt_cmd) { - pr_err("Unable to allocate xcopy_pt_cmd\n"); - return -ENOMEM; - } - init_completion(&xpt_cmd->xpt_passthrough_sem); - se_cmd = &xpt_cmd->se_cmd; + memset(&xpt_cmd, 0, sizeof(xpt_cmd)); + init_completion(&xpt_cmd.xpt_passthrough_sem); 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); - - transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, - DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]); - xop->src_pt_cmd = xpt_cmd; - - rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0], - remote_port, true); + 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, src_bytes, + DMA_FROM_DEVICE, 0, &xpt_cmd.sense_buffer[0], 0, + NULL); + rc = target_xcopy_setup_pt_cmd(&xpt_cmd, xop, src_dev, &cdb[0], + remote_port); if (rc < 0) { - ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; - transport_generic_free_cmd(se_cmd, 0); - return rc; + ec_cmd->scsi_status = se_cmd->scsi_status; + goto out; } - xop->xop_data_sg = se_cmd->t_data_sg; - xop->xop_data_nents = se_cmd->t_data_nents; pr_debug("XCOPY-READ: Saved xop->xop_data_sg: %p, num: %u for READ" " memory\n", xop->xop_data_sg, xop->xop_data_nents); - rc = target_xcopy_issue_pt_cmd(xpt_cmd); - if (rc < 0) { - ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; - transport_generic_free_cmd(se_cmd, 0); - return rc; - } - /* - * Clear off the allocated t_data_sg, that has been saved for - * zero-copy WRITE submission reuse in struct xcopy_op.. - */ - se_cmd->t_data_sg = NULL; - se_cmd->t_data_nents = 0; - - return 0; + rc = target_xcopy_issue_pt_cmd(&xpt_cmd); + if (rc < 0) + ec_cmd->scsi_status = se_cmd->scsi_status; +out: + transport_generic_free_cmd(se_cmd, 0); + return rc; } static int target_xcopy_write_destination( @@ -726,61 +617,41 @@ 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; - u32 length = (dst_sectors * dst_dev->dev_attrib.block_size); + struct xcopy_pt_cmd xpt_cmd; + struct se_cmd *se_cmd = &xpt_cmd.se_cmd; + 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); - xpt_cmd = kzalloc(sizeof(struct xcopy_pt_cmd), GFP_KERNEL); - if (!xpt_cmd) { - pr_err("Unable to allocate xcopy_pt_cmd\n"); - return -ENOMEM; - } - init_completion(&xpt_cmd->xpt_passthrough_sem); - se_cmd = &xpt_cmd->se_cmd; + memset(&xpt_cmd, 0, sizeof(xpt_cmd)); + init_completion(&xpt_cmd.xpt_passthrough_sem); 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); - - transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, - DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]); - xop->dst_pt_cmd = xpt_cmd; - - rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, dst_dev, &cdb[0], - remote_port, false); + 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, dst_bytes, + DMA_TO_DEVICE, 0, &xpt_cmd.sense_buffer[0], 0, + NULL); + rc = target_xcopy_setup_pt_cmd(&xpt_cmd, xop, dst_dev, &cdb[0], + remote_port); if (rc < 0) { - struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd; - ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; - /* - * If the failure happened before the t_mem_list hand-off in - * target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that - * core releases this memory on error during X-COPY WRITE I/O. - */ - src_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC; - src_cmd->t_data_sg = xop->xop_data_sg; - src_cmd->t_data_nents = xop->xop_data_nents; - - transport_generic_free_cmd(se_cmd, 0); - return rc; - } - - rc = target_xcopy_issue_pt_cmd(xpt_cmd); - if (rc < 0) { - ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; - se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC; - transport_generic_free_cmd(se_cmd, 0); - return rc; + ec_cmd->scsi_status = se_cmd->scsi_status; + goto out; } - return 0; + rc = target_xcopy_issue_pt_cmd(&xpt_cmd); + if (rc < 0) + ec_cmd->scsi_status = se_cmd->scsi_status; +out: + transport_generic_free_cmd(se_cmd, 0); + return rc; } static void target_xcopy_do_work(struct work_struct *work) @@ -789,15 +660,20 @@ 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, cur_nolb, max_nolb, copied_nolb = 0; + unsigned short nolb; + unsigned int copied_bytes = 0; + sense_reason_t sense_rc; - if (target_parse_xcopy_cmd(xop) != TCM_NO_SENSE) + sense_rc = target_parse_xcopy_cmd(xop); + if (sense_rc != TCM_NO_SENSE) goto err_free; - if (WARN_ON_ONCE(!xop->src_dev) || WARN_ON_ONCE(!xop->dst_dev)) + if (WARN_ON_ONCE(!xop->src_dev) || WARN_ON_ONCE(!xop->dst_dev)) { + sense_rc = TCM_INVALID_PARAMETER_LIST; goto err_free; + } src_dev = xop->src_dev; dst_dev = xop->dst_dev; @@ -806,83 +682,102 @@ 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_bytes = min_t(u64, max_bytes_src, max_bytes_dst); + max_bytes = min_t(u64, max_bytes, XCOPY_MAX_BYTES); - max_nolb = min_t(u16, max_sectors, ((u16)(~0U))); + /* + * 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("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", + 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) { - cur_nolb = min(nolb, max_nolb); + 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) { + /* + * (Re)allocate a buffer large enough to hold the XCOPY + * I/O size, which can be reused each read / write loop. + */ + target_free_sgl(xop->xop_data_sg, xop->xop_data_nents); + rc = target_alloc_sgl(&xop->xop_data_sg, + &xop->xop_data_nents, + cur_bytes, + false, false); + if (rc < 0) + goto out; + 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); - if (rc < 0) { - transport_generic_free_cmd(&xop->src_pt_cmd->se_cmd, 0); + 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; - - transport_generic_free_cmd(&xop->src_pt_cmd->se_cmd, 0); - xop->dst_pt_cmd->se_cmd.se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC; - - transport_generic_free_cmd(&xop->dst_pt_cmd->se_cmd, 0); + 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; out: + /* + * The XCOPY command was aborted after some data was transferred. + * Terminate command with CHECK CONDITION status, with the sense key + * set to COPY ABORTED. + */ + sense_rc = TCM_COPY_TARGET_DEVICE_NOT_REACHABLE; xcopy_pt_undepend_remotedev(xop); + target_free_sgl(xop->xop_data_sg, xop->xop_data_nents); err_free: kfree(xop); - /* - * Don't override an error scsi status if it has already been set - */ - if (ec_cmd->scsi_status == SAM_STAT_GOOD) { - pr_warn_ratelimited("target_xcopy_do_work: rc: %d, Setting X-COPY" - " CHECK_CONDITION -> sending response\n", rc); - ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; - } - target_complete_cmd(ec_cmd, ec_cmd->scsi_status); + 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); } /* @@ -943,8 +838,7 @@ static sense_reason_t target_parse_xcopy_cmd(struct xcopy_op *xop) */ seg_desc = &p[16] + tdll; - rc = target_xcopy_parse_segment_descriptors(se_cmd, xop, seg_desc, - sdll, &ret); + rc = target_xcopy_parse_segment_descriptors(xop, seg_desc, sdll, &ret); if (rc <= 0) goto out; @@ -1105,7 +999,7 @@ static sense_reason_t target_rcr_operating_parameters(struct se_cmd *se_cmd) put_unaligned_be32(42, &p[0]); transport_kunmap_data_sg(se_cmd); - target_complete_cmd(se_cmd, GOOD); + target_complete_cmd(se_cmd, SAM_STAT_GOOD); return TCM_NO_SENSE; } @@ -1114,8 +1008,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); |
