Skip to content

Commit cdc949b

Browse files
nj-shettykawasaki
authored andcommitted
nvmet: Support the Copy command
Support the Copy command for namespaces backed by a block device or by a file. For namespaces backed by a block device, we call blkdev_copy_offload() and fall back to blkdev_copy_onload() if necessary. For namespaces backed by a file we call vfs_copy_file_range(). nvmet always reports that the Copy command is supported. Tracing support is added for the Copy command. Signed-off-by: Nitesh Shetty <[email protected]> Signed-off-by: Anuj Gupta <[email protected]> [ bvanassche: Increased namespace limits. ] Signed-off-by: Bart Van Assche <[email protected]>
1 parent acb7e88 commit cdc949b

6 files changed

Lines changed: 179 additions & 8 deletions

File tree

drivers/nvme/host/trace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static const char *nvme_trace_read_write(struct trace_seq *p, u8 *cdw10)
143143
u16 length = get_unaligned_le16(cdw10 + 8);
144144
u16 control = get_unaligned_le16(cdw10 + 10);
145145
u32 dsmgmt = get_unaligned_le32(cdw10 + 12);
146-
u32 reftag = get_unaligned_le32(cdw10 + 16);
146+
u32 reftag = get_unaligned_le32(cdw10 + 16);
147147

148148
trace_seq_printf(p,
149149
"slba=%llu, len=%u, ctrl=0x%x, dsmgmt=%u, reftag=%u",

drivers/nvme/target/admin-cmd.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -733,8 +733,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
733733
id->mnan = cpu_to_le32(NVMET_MAX_NAMESPACES);
734734
id->oncs = cpu_to_le16(NVME_CTRL_ONCS_DSM |
735735
NVME_CTRL_ONCS_WRITE_ZEROES |
736-
NVME_CTRL_ONCS_RESERVATIONS);
737-
736+
NVME_CTRL_ONCS_RESERVATIONS | NVME_CTRL_ONCS_COPY);
738737
/* XXX: don't report vwc if the underlying device is write through */
739738
id->vwc = NVME_CTRL_VWC_PRESENT;
740739

@@ -797,6 +796,27 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
797796
nvmet_req_complete(req, status);
798797
}
799798

799+
static void nvmet_set_copy_limits(struct nvme_id_ns *id)
800+
{
801+
/*
802+
* MSRC = Maximum Source Range Count - the maximum number of
803+
* source ranges that may be used to specify source data in a
804+
* Copy command. 0's based.
805+
*/
806+
id->msrc = 256 - 1;
807+
/*
808+
* MSSRL = Maximum Single Source Range Length - the maximum number
809+
* of logical blocks that may be specified in the Number of Logical
810+
* Blocks field in each valid Source Range Entries Descriptor.
811+
*/
812+
id->mssrl = cpu_to_le16(U16_MAX);
813+
/*
814+
* MCL = Maximum Copy Length - the maximum number of logical
815+
* blocks that may be specified in a Copy command.
816+
*/
817+
id->mcl = cpu_to_le32(U32_MAX);
818+
}
819+
800820
static void nvmet_execute_identify_ns(struct nvmet_req *req)
801821
{
802822
struct nvme_id_ns *id;
@@ -845,6 +865,8 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
845865
if (req->ns->bdev)
846866
nvmet_bdev_set_limits(req->ns->bdev, id);
847867

868+
nvmet_set_copy_limits(id);
869+
848870
/*
849871
* We just provide a single LBA format that matches what the
850872
* underlying device reports.

drivers/nvme/target/io-cmd-bdev.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,83 @@ static void nvmet_bdev_execute_write_zeroes(struct nvmet_req *req)
451451
}
452452
}
453453

454+
static void nvmet_bdev_copy_endio(const struct blk_copy_params *params)
455+
{
456+
struct nvmet_req *rq = params->private;
457+
blk_status_t status = params->status;
458+
459+
/*
460+
* From the NVM Command Set Specification section about the Copy
461+
* Command: "If the command completes with failure (i.e., completes with
462+
* a status code other than Successful Completion), then: [ ... ] Dword
463+
* 0 of the completion queue entry contains the number of the lowest
464+
* numbered Source Range entry that was not successfully copied". Since
465+
* that information is not available, clear Dword 0.
466+
*/
467+
rq->cqe->result.u32 = cpu_to_le32(0);
468+
469+
nvmet_req_complete(rq, blk_to_nvme_status(rq, status));
470+
}
471+
472+
static void nvmet_bdev_execute_copy(struct nvmet_req *rq)
473+
{
474+
u32 i, nr_range = (u32)rq->cmd->copy.nr_range + 1;
475+
struct blk_copy_seg *in_segs __free(kfree) = NULL;
476+
struct nvme_command *cmd = rq->cmd;
477+
struct nvme_copy_range range;
478+
u64 src_len, copy_len = 0;
479+
loff_t dst_pos, src_pos;
480+
u16 status;
481+
int ret;
482+
483+
status = NVME_SC_INTERNAL;
484+
in_segs = kmalloc_array(nr_range, sizeof(*in_segs), GFP_KERNEL);
485+
if (!in_segs)
486+
goto err_rq_complete;
487+
488+
for (i = 0; i < nr_range; i++) {
489+
status = nvmet_copy_from_sgl(rq, i * sizeof(range), &range,
490+
sizeof(range));
491+
if (WARN_ON_ONCE(status))
492+
goto err_rq_complete;
493+
/*
494+
* TO DO: implement support for different source and destination namespace
495+
* IDs.
496+
*/
497+
status = errno_to_nvme_status(rq, -EIO);
498+
if (le32_to_cpu(range.nsid) != rq->ns->nsid)
499+
goto err_rq_complete;
500+
src_pos = le64_to_cpu(range.slba) << rq->ns->blksize_shift;
501+
src_len = (le16_to_cpu(range.nlb) + 1) << rq->ns->blksize_shift;
502+
in_segs[i] =
503+
(struct blk_copy_seg){ .pos = src_pos, .len = src_len };
504+
copy_len += src_len;
505+
}
506+
507+
dst_pos = le64_to_cpu(cmd->copy.sdlba) << rq->ns->blksize_shift;
508+
struct blk_copy_seg out_seg = { .pos = dst_pos, .len = copy_len };
509+
struct blk_copy_params params = {
510+
.in_bdev = rq->ns->bdev,
511+
.in_segs = in_segs,
512+
.in_nseg = nr_range,
513+
.out_bdev = rq->ns->bdev,
514+
.out_segs = &out_seg,
515+
.out_nseg = 1,
516+
.end_io = nvmet_bdev_copy_endio,
517+
.private = rq,
518+
};
519+
ret = blkdev_copy_offload(&params);
520+
if (ret == -EIOCBQUEUED)
521+
return;
522+
if (ret)
523+
ret = blkdev_copy_onload(&params);
524+
525+
rq->cqe->result.u32 = cpu_to_le32(ret == 0);
526+
status = errno_to_nvme_status(rq, ret);
527+
err_rq_complete:
528+
nvmet_req_complete(rq, status);
529+
}
530+
454531
u16 nvmet_bdev_parse_io_cmd(struct nvmet_req *req)
455532
{
456533
switch (req->cmd->common.opcode) {
@@ -469,6 +546,9 @@ u16 nvmet_bdev_parse_io_cmd(struct nvmet_req *req)
469546
case nvme_cmd_write_zeroes:
470547
req->execute = nvmet_bdev_execute_write_zeroes;
471548
return 0;
549+
case nvme_cmd_copy:
550+
req->execute = nvmet_bdev_execute_copy;
551+
return 0;
472552
default:
473553
return nvmet_report_invalid_opcode(req);
474554
}

drivers/nvme/target/io-cmd-file.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,7 @@ static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
131131
if (req->f.mpool_alloc && nr_bvec > NVMET_MAX_MPOOL_BVEC)
132132
is_sync = true;
133133

134-
pos = le64_to_cpu(req->cmd->rw.slba) << req->ns->blksize_shift;
135-
if (unlikely(pos + req->transfer_len > req->ns->size)) {
136-
nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
137-
return true;
138-
}
134+
pos = le64_to_cpu(req->cmd->copy.sdlba) << req->ns->blksize_shift;
139135

140136
memset(&req->f.iocb, 0, sizeof(struct kiocb));
141137
for_each_sg(req->sg, sg, req->sg_cnt, i) {
@@ -321,6 +317,50 @@ static void nvmet_file_dsm_work(struct work_struct *w)
321317
}
322318
}
323319

320+
static void nvmet_file_copy_work(struct work_struct *w)
321+
{
322+
struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
323+
u32 id, nr_range = req->cmd->copy.nr_range + 1;
324+
loff_t dst_pos;
325+
ssize_t ret;
326+
u16 status;
327+
328+
status = errno_to_nvme_status(req, -ENOSPC);
329+
dst_pos = le64_to_cpu(req->cmd->copy.sdlba) << req->ns->blksize_shift;
330+
331+
for (id = 0; id < nr_range; id++) {
332+
struct nvme_copy_range range;
333+
loff_t src_pos, src_len;
334+
335+
status = nvmet_copy_from_sgl(req, id * sizeof(range), &range,
336+
sizeof(range));
337+
if (status)
338+
goto out;
339+
/*
340+
* TO DO: implement support for different source and destination namespace
341+
* IDs.
342+
*/
343+
status = errno_to_nvme_status(req, -EIO);
344+
if (le32_to_cpu(range.nsid) != req->ns->nsid)
345+
goto out;
346+
src_pos = le64_to_cpu(range.slba) << (req->ns->blksize_shift);
347+
src_len = (le16_to_cpu(range.nlb) + 1) << req->ns->blksize_shift;
348+
ret = vfs_copy_file_range(req->ns->file, src_pos, req->ns->file,
349+
dst_pos, src_len, COPY_FILE_SPLICE);
350+
if (ret != src_len) {
351+
req->cqe->result.u32 = cpu_to_le32(id);
352+
status = errno_to_nvme_status(req, ret < 0 ? ret : -EIO);
353+
goto out;
354+
}
355+
dst_pos += ret;
356+
}
357+
358+
status = 0;
359+
360+
out:
361+
nvmet_req_complete(req, status);
362+
}
363+
324364
static void nvmet_file_execute_dsm(struct nvmet_req *req)
325365
{
326366
if (!nvmet_check_data_len_lte(req, nvmet_dsm_len(req)))
@@ -329,6 +369,12 @@ static void nvmet_file_execute_dsm(struct nvmet_req *req)
329369
queue_work(nvmet_wq, &req->f.work);
330370
}
331371

372+
static void nvmet_file_execute_copy(struct nvmet_req *req)
373+
{
374+
INIT_WORK(&req->f.work, nvmet_file_copy_work);
375+
queue_work(nvmet_wq, &req->f.work);
376+
}
377+
332378
static void nvmet_file_write_zeroes_work(struct work_struct *w)
333379
{
334380
struct nvmet_req *req = container_of(w, struct nvmet_req, f.work);
@@ -375,6 +421,9 @@ u16 nvmet_file_parse_io_cmd(struct nvmet_req *req)
375421
case nvme_cmd_write_zeroes:
376422
req->execute = nvmet_file_execute_write_zeroes;
377423
return 0;
424+
case nvme_cmd_copy:
425+
req->execute = nvmet_file_execute_copy;
426+
return 0;
378427
default:
379428
return nvmet_report_invalid_opcode(req);
380429
}

drivers/nvme/target/trace.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ static const char *nvmet_trace_dsm(struct trace_seq *p, u8 *cdw10)
9292
return ret;
9393
}
9494

95+
static const char *nvmet_trace_copy(struct trace_seq *p, u8 *cdw10)
96+
{
97+
const char *ret = trace_seq_buffer_ptr(p);
98+
u64 sdlba = get_unaligned_le64(cdw10);
99+
u8 nr_range = get_unaligned_le16(cdw10 + 8);
100+
u16 control = get_unaligned_le16(cdw10 + 10);
101+
u32 dsmgmt = get_unaligned_le32(cdw10 + 12);
102+
u32 reftag = get_unaligned_le32(cdw10 + 16);
103+
104+
trace_seq_printf(p,
105+
"sdlba=%llu, nr_range=%u, ctrl=1x%x, dsmgmt=%u, reftag=%u",
106+
sdlba, nr_range, control, dsmgmt, reftag);
107+
trace_seq_putc(p, 0);
108+
109+
return ret;
110+
}
111+
95112
static const char *nvmet_trace_common(struct trace_seq *p, u8 *cdw10)
96113
{
97114
const char *ret = trace_seq_buffer_ptr(p);
@@ -303,6 +320,8 @@ const char *nvmet_trace_parse_nvm_cmd(struct trace_seq *p,
303320
return nvmet_trace_resv_rel(p, cdw10);
304321
case nvme_cmd_resv_report:
305322
return nvmet_trace_resv_report(p, cdw10);
323+
case nvme_cmd_copy:
324+
return nvmet_trace_copy(p, cdw10);
306325
default:
307326
return nvmet_trace_common(p, cdw10);
308327
}

include/linux/nvme.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,7 @@ enum {
22202220
NVME_SC_PMR_SAN_PROHIBITED = 0x123,
22212221
NVME_SC_ANA_GROUP_ID_INVALID = 0x124,
22222222
NVME_SC_ANA_ATTACH_FAILED = 0x125,
2223+
NVME_SC_COMMAND_SIZE_LIMIT_EXC = 0x183,
22232224

22242225
/*
22252226
* I/O Command Set Specific - NVM commands:

0 commit comments

Comments
 (0)