in target_core_xcopy.c [667:782]
static void target_xcopy_do_work(struct work_struct *work)
{
struct xcopy_op *xop = container_of(work, struct xcopy_op, xop_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;
int rc = 0;
unsigned short nolb, max_nolb, copied_nolb = 0;
sense_reason_t sense_rc;
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)) {
sense_rc = TCM_INVALID_PARAMETER_LIST;
goto err_free;
}
src_dev = xop->src_dev;
dst_dev = xop->dst_dev;
src_lba = xop->src_lba;
dst_lba = xop->dst_lba;
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
*/
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_nolb = min_t(u16, max_sectors, ((u16)(~0U)));
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",
(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;
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);
rc = target_xcopy_read_source(ec_cmd, xop, src_dev, src_lba, cur_nolb);
if (rc < 0)
goto out;
src_lba += cur_nolb;
pr_debug("target_xcopy_do_work: Incremented READ src_lba to %llu\n",
(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);
rc = target_xcopy_write_destination(ec_cmd, xop, dst_dev,
dst_lba, cur_nolb);
if (rc < 0)
goto out;
dst_lba += cur_nolb;
pr_debug("target_xcopy_do_work: Incremented WRITE dst_lba to %llu\n",
(unsigned long long)dst_lba);
copied_nolb += cur_nolb;
nolb -= cur_nolb;
}
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",
(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("target_xcopy_do_work: Setting X-COPY GOOD status -> sending response\n");
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);
pr_warn_ratelimited("target_xcopy_do_work: rc: %d, sense: %u, XCOPY operation failed\n",
rc, sense_rc);
target_complete_cmd_with_sense(ec_cmd, SAM_STAT_CHECK_CONDITION, sense_rc);
}