Skip to content

Commit acb7e88

Browse files
nj-shettykawasaki
authored andcommitted
nvme: Add copy offloading support
Add support for the NVMe Copy command. This command supports a single destination range and up to 256 source ranges. Add trace event support for nvme_copy_cmd. Signed-off-by: Kanchan Joshi <[email protected]> Signed-off-by: Nitesh Shetty <[email protected]> Signed-off-by: Javier González <[email protected]> Signed-off-by: Anuj Gupta <[email protected]> [ bvanassche: generalized Copy support from one to 256 source ranges; fixed an endianness issue in nvme_config_copy(); renamed rsvd91 into rsvd81 and verified the offset with pahole ] Signed-off-by: Bart Van Assche <[email protected]>
1 parent 02ec1e7 commit acb7e88

4 files changed

Lines changed: 169 additions & 3 deletions

File tree

drivers/nvme/host/constants.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ static const char * const nvme_ops[] = {
1919
[nvme_cmd_resv_report] = "Reservation Report",
2020
[nvme_cmd_resv_acquire] = "Reservation Acquire",
2121
[nvme_cmd_resv_release] = "Reservation Release",
22+
[nvme_cmd_copy] = "Copy Offload",
2223
[nvme_cmd_zone_mgmt_send] = "Zone Management Send",
2324
[nvme_cmd_zone_mgmt_recv] = "Zone Management Receive",
2425
[nvme_cmd_zone_append] = "Zone Append",

drivers/nvme/host/core.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <linux/async.h>
88
#include <linux/blkdev.h>
9+
#include <linux/blk-copy.h>
910
#include <linux/blk-mq.h>
1011
#include <linux/blk-integrity.h>
1112
#include <linux/compat.h>
@@ -821,6 +822,87 @@ static inline void nvme_setup_flush(struct nvme_ns *ns,
821822
cmnd->common.nsid = cpu_to_le32(ns->head->ns_id);
822823
}
823824

825+
/*
826+
* Translate REQ_OP_COPY_SRC and REQ_OP_COPY_DST bios into an NVMe Copy command.
827+
* The NVMe copy command supports multiple source LBA ranges, a single
828+
* destination LBA range, and also supports copying across NVMe namespaces. This
829+
* implementation supports all these features except copying across NVMe
830+
* namespaces.
831+
*/
832+
static inline blk_status_t nvme_setup_copy_offload(struct nvme_ns *ns,
833+
struct request *req,
834+
struct nvme_command *cmnd)
835+
{
836+
const u32 nr_range = blk_copy_bio_count(req, REQ_OP_COPY_SRC);
837+
struct nvme_ns *src_ns, *dst_ns;
838+
struct bio *src_bio = NULL, *dst_bio;
839+
struct nvme_copy_range *range;
840+
u16 control = 0;
841+
u64 dlba;
842+
843+
dst_bio = blk_first_copy_bio(req, REQ_OP_COPY_DST);
844+
845+
if (WARN_ON_ONCE(!dst_bio))
846+
return BLK_STS_IOERR;
847+
848+
/* TO DO: derive dst_ns from dst_bio. */
849+
dst_ns = ns;
850+
dlba = nvme_sect_to_lba(dst_ns->head, dst_bio->bi_iter.bi_sector);
851+
852+
if (req->cmd_flags & REQ_FUA)
853+
control |= NVME_RW_FUA;
854+
855+
if (req->cmd_flags & REQ_FAILFAST_DEV)
856+
control |= NVME_RW_LR;
857+
858+
*cmnd = (typeof(*cmnd)){
859+
.copy = {
860+
.opcode = nvme_cmd_copy,
861+
.nsid = cpu_to_le32(dst_ns->head->ns_id),
862+
.control = cpu_to_le16(control),
863+
.sdlba = cpu_to_le64(dlba),
864+
.desfmt_prinfor = 2, /* DESFMT=2 */
865+
.nr_range = nr_range - 1, /* 0's based */
866+
}
867+
};
868+
869+
range = kmalloc_array(nr_range, sizeof(*range),
870+
GFP_ATOMIC | __GFP_ZERO | __GFP_NOWARN);
871+
if (!range)
872+
return BLK_STS_RESOURCE;
873+
874+
for (unsigned int i = 0; i < nr_range; i++) {
875+
u64 slba;
876+
u32 nslb;
877+
878+
if (!src_bio)
879+
src_bio = blk_first_copy_bio(req, REQ_OP_COPY_SRC);
880+
else
881+
src_bio = blk_next_copy_bio(src_bio);
882+
if (WARN_ON_ONCE(!src_bio))
883+
goto free_range;
884+
/* TO DO: derive src_ns from src_bio. */
885+
src_ns = ns;
886+
slba = nvme_sect_to_lba(src_ns->head,
887+
src_bio->bi_iter.bi_sector);
888+
nslb = src_bio->bi_iter.bi_size >> src_ns->head->lba_shift;
889+
range[i].nsid = cpu_to_le32(src_ns->head->ns_id); /* requires DESFMT=2 */
890+
range[i].slba = cpu_to_le64(slba);
891+
range[i].nlb = cpu_to_le16(nslb - 1);
892+
}
893+
894+
req->special_vec.bv_page = virt_to_page(range);
895+
req->special_vec.bv_offset = offset_in_page(range);
896+
req->special_vec.bv_len = sizeof(*range) * nr_range;
897+
req->rq_flags |= RQF_SPECIAL_PAYLOAD;
898+
899+
return BLK_STS_OK;
900+
901+
free_range:
902+
kfree(range);
903+
return BLK_STS_IOERR;
904+
}
905+
824906
static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
825907
struct nvme_command *cmnd)
826908
{
@@ -1122,6 +1204,10 @@ blk_status_t nvme_setup_cmd(struct nvme_ns *ns, struct request *req)
11221204
case REQ_OP_ZONE_APPEND:
11231205
ret = nvme_setup_rw(ns, req, cmd, nvme_cmd_zone_append);
11241206
break;
1207+
case REQ_OP_COPY_DST:
1208+
case REQ_OP_COPY_SRC:
1209+
ret = nvme_setup_copy_offload(ns, req, cmd);
1210+
break;
11251211
default:
11261212
WARN_ON_ONCE(1);
11271213
return BLK_STS_IOERR;
@@ -1884,6 +1970,21 @@ static bool nvme_init_integrity(struct nvme_ns_head *head,
18841970
return true;
18851971
}
18861972

1973+
static void nvme_config_copy(struct nvme_ns *ns, struct nvme_id_ns *id,
1974+
struct queue_limits *lim)
1975+
{
1976+
struct nvme_ctrl *ctrl = ns->ctrl;
1977+
1978+
if (!(ctrl->oncs & NVME_CTRL_ONCS_COPY)) {
1979+
lim->max_copy_hw_sectors = 0;
1980+
return;
1981+
}
1982+
lim->max_copy_hw_sectors = nvme_lba_to_sect(ns->head,
1983+
le16_to_cpu(id->mssrl));
1984+
lim->max_copy_src_segments = 256;
1985+
lim->max_copy_dst_segments = 1;
1986+
}
1987+
18871988
static bool nvme_ns_ids_equal(struct nvme_ns_ids *a, struct nvme_ns_ids *b)
18881989
{
18891990
return uuid_equal(&a->uuid, &b->uuid) &&
@@ -2416,6 +2517,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
24162517
if (!nvme_update_disk_info(ns, id, nvm, &lim))
24172518
capacity = 0;
24182519

2520+
nvme_config_copy(ns, id, &lim);
24192521
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
24202522
ns->head->ids.csi == NVME_CSI_ZNS)
24212523
nvme_update_zone_info(ns, &lim, &zi);
@@ -2542,6 +2644,9 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
25422644
lim.physical_block_size = ns_lim->physical_block_size;
25432645
lim.io_min = ns_lim->io_min;
25442646
lim.io_opt = ns_lim->io_opt;
2647+
lim.max_copy_hw_sectors = UINT_MAX;
2648+
lim.max_copy_src_segments = U16_MAX;
2649+
lim.max_copy_dst_segments = U16_MAX;
25452650
queue_limits_stack_bdev(&lim, ns->disk->part0, 0,
25462651
ns->head->disk->disk_name);
25472652
if (unsupported)
@@ -5368,6 +5473,7 @@ static inline void _nvme_check_size(void)
53685473
BUILD_BUG_ON(sizeof(struct nvme_download_firmware) != 64);
53695474
BUILD_BUG_ON(sizeof(struct nvme_format_cmd) != 64);
53705475
BUILD_BUG_ON(sizeof(struct nvme_dsm_cmd) != 64);
5476+
BUILD_BUG_ON(sizeof(struct nvme_copy_command) != 64);
53715477
BUILD_BUG_ON(sizeof(struct nvme_write_zeroes_cmd) != 64);
53725478
BUILD_BUG_ON(sizeof(struct nvme_abort_cmd) != 64);
53735479
BUILD_BUG_ON(sizeof(struct nvme_get_log_page_command) != 64);

drivers/nvme/host/trace.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,23 @@ static const char *nvme_trace_read_write(struct trace_seq *p, u8 *cdw10)
153153
return ret;
154154
}
155155

156+
static const char *nvme_trace_copy(struct trace_seq *p, u8 *cdw10)
157+
{
158+
const char *ret = trace_seq_buffer_ptr(p);
159+
u64 sdlba = get_unaligned_le64(cdw10);
160+
u8 nr_range = get_unaligned_le16(cdw10 + 8);
161+
u16 control = get_unaligned_le16(cdw10 + 10);
162+
u32 dsmgmt = get_unaligned_le32(cdw10 + 12);
163+
u32 reftag = get_unaligned_le32(cdw10 + 16);
164+
165+
trace_seq_printf(p,
166+
"sdlba=%llu, nr_range=%u, ctrl=0x%x, dsmgmt=%u, reftag=%u",
167+
sdlba, nr_range, control, dsmgmt, reftag);
168+
trace_seq_putc(p, 0);
169+
170+
return ret;
171+
}
172+
156173
static const char *nvme_trace_dsm(struct trace_seq *p, u8 *cdw10)
157174
{
158175
const char *ret = trace_seq_buffer_ptr(p);
@@ -386,6 +403,8 @@ const char *nvme_trace_parse_nvm_cmd(struct trace_seq *p,
386403
return nvme_trace_resv_rel(p, cdw10);
387404
case nvme_cmd_resv_report:
388405
return nvme_trace_resv_report(p, cdw10);
406+
case nvme_cmd_copy:
407+
return nvme_trace_copy(p, cdw10);
389408
default:
390409
return nvme_trace_common(p, cdw10);
391410
}

include/linux/nvme.h

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ struct nvme_id_ctrl {
376376
__u8 nvscc;
377377
__u8 nwpc;
378378
__le16 acwu;
379-
__u8 rsvd534[2];
379+
__le16 ocfs;
380380
__le32 sgls;
381381
__le32 mnan;
382382
__u8 rsvd544[224];
@@ -404,6 +404,7 @@ enum {
404404
NVME_CTRL_ONCS_WRITE_ZEROES = 1 << 3,
405405
NVME_CTRL_ONCS_RESERVATIONS = 1 << 5,
406406
NVME_CTRL_ONCS_TIMESTAMP = 1 << 6,
407+
NVME_CTRL_ONCS_COPY = 1 << 8,
407408
NVME_CTRL_VWC_PRESENT = 1 << 0,
408409
NVME_CTRL_OACS_SEC_SUPP = 1 << 0,
409410
NVME_CTRL_OACS_NS_MNGT_SUPP = 1 << 3,
@@ -458,7 +459,10 @@ struct nvme_id_ns {
458459
__le16 npdg;
459460
__le16 npda;
460461
__le16 nows;
461-
__u8 rsvd74[18];
462+
__le16 mssrl;
463+
__le32 mcl;
464+
__u8 msrc;
465+
__u8 rsvd81[11];
462466
__le32 anagrpid;
463467
__u8 rsvd96[3];
464468
__u8 nsattr;
@@ -967,6 +971,7 @@ enum nvme_opcode {
967971
nvme_cmd_resv_acquire = 0x11,
968972
nvme_cmd_io_mgmt_recv = 0x12,
969973
nvme_cmd_resv_release = 0x15,
974+
nvme_cmd_copy = 0x19,
970975
nvme_cmd_zone_mgmt_send = 0x79,
971976
nvme_cmd_zone_mgmt_recv = 0x7a,
972977
nvme_cmd_zone_append = 0x7d,
@@ -991,7 +996,8 @@ enum nvme_opcode {
991996
nvme_opcode_name(nvme_cmd_resv_release), \
992997
nvme_opcode_name(nvme_cmd_zone_mgmt_send), \
993998
nvme_opcode_name(nvme_cmd_zone_mgmt_recv), \
994-
nvme_opcode_name(nvme_cmd_zone_append))
999+
nvme_opcode_name(nvme_cmd_zone_append), \
1000+
nvme_opcode_name(nvme_cmd_copy))
9951001

9961002

9971003

@@ -1169,6 +1175,39 @@ struct nvme_dsm_range {
11691175
__le64 slba;
11701176
};
11711177

1178+
struct nvme_copy_command {
1179+
__u8 opcode;
1180+
__u8 flags;
1181+
__u16 command_id;
1182+
__le32 nsid;
1183+
__u64 rsvd2;
1184+
__le64 metadata;
1185+
union nvme_data_ptr dptr;
1186+
__le64 sdlba;
1187+
__u8 nr_range;
1188+
__u8 desfmt_prinfor;
1189+
__le16 control;
1190+
__le16 rsvd13;
1191+
__le16 dspec;
1192+
__le32 ilbrt;
1193+
__le16 lbat;
1194+
__le16 lbatm;
1195+
};
1196+
1197+
struct nvme_copy_range {
1198+
__le32 nsid; /* DESFMT=2 only */
1199+
__le32 rsvd1;
1200+
__le64 slba;
1201+
__le16 nlb;
1202+
__le16 rsvd18;
1203+
__le32 rsvd20;
1204+
__le32 eilbrt;
1205+
__le16 elbat;
1206+
__le16 elbatm;
1207+
};
1208+
1209+
static_assert(sizeof(struct nvme_copy_range) == 32);
1210+
11721211
struct nvme_write_zeroes_cmd {
11731212
__u8 opcode;
11741213
__u8 flags;
@@ -2001,6 +2040,7 @@ struct nvme_command {
20012040
struct nvme_download_firmware dlfw;
20022041
struct nvme_format_cmd format;
20032042
struct nvme_dsm_cmd dsm;
2043+
struct nvme_copy_command copy;
20042044
struct nvme_write_zeroes_cmd write_zeroes;
20052045
struct nvme_zone_mgmt_send_cmd zms;
20062046
struct nvme_zone_mgmt_recv_cmd zmr;

0 commit comments

Comments
 (0)