From: SelvaKumar S <hidden> Date: 2021-08-17 11:28:44
This started out as an attempt to support NVMe Simple Copy Command (SCC),
and evolved during the RFC review process.
The patchset, at this point, contains -
1. SCC support in NVMe driver
2. Block-layer infra for copy-offload operation
3. ioctl interface to user-space
4. copy-emulation infra in the block-layer
5. copy-offload plumbing to dm-kcopyd (thus creating couple of in-kernel
users such as dm-clone)
The SCC specification, i.e. TP4065a can be found in following link
https://nvmexpress.org/wp-content/uploads/NVM-Express-1.4-Ratified-TPs.zip
Simple copy is a copy offloading feature and can be used to copy multiple
contiguous ranges (source_ranges) of LBA's to a single destination LBA
within the device, reducing traffic between host and device.
We define a block ioctl for copy and copy payload format similar to
discard. For device supporting native simple copy, we attach the control
information as payload to the bio and submit to the device. Copy emulation
is implemented incase underlaying device does not support copy offload or
based on sysfs choice. Copy emulation is done by reading each source range
into buffer and writing it to the destination.
At present this implementation does not support copy offload for stacked/dm
devices, rather copy operation is completed through emulation.
One of the in-kernel use case for copy-offload is implemented by this
patchset. dm-kcopyd infra has been changed to leverage the copy-offload if
it is natively available. Other future use-cases could be F2FS GC, BTRFS
relocation/balance and copy_file_range.
Following limits are added to queue limits and are exposed via sysfs to
userspace, which user can use to form a payload.
- copy_offload:
configurable, can be used set emulation or copy offload
0 to disable copy offload,
1 to enable copy offloading support. Offload can be only
enabled, if underlaying device supports offload
- max_copy_sectors:
total copy length supported by copy offload feature in device.
0 indicates copy offload is not supported.
- max_copy_nr_ranges:
maximum number of source range entries supported by copy offload
feature in device
- max_copy_range_sectors:
maximum copy length per source range entry
*blkdev_issue_copy* takes source bdev, no of sources, array of source
ranges (in sectors), destination bdev and destination offset(in sectors).
If both source and destination block devices are same and queue parameter
copy_offload is 1, then copy is done through native copy offloading.
Copy emulation is used in otherwise.
Changes from RFC v5
1. Handle copy larger than maximum copy limits
2. Create copy context and submit copy offload asynchronously
3. Remove BLKDEV_COPY_NOEMULATION opt-in option of copy offload and
check for copy support before submission from dm and other layers
4. Allocate maximum possible allocatable buffer for copy emulation
rather failing very large copy offload.
5. Fix copy_offload sysfs to be either have 0 or 1
Changes from RFC v4
1. Extend dm-kcopyd to leverage copy-offload, while copying within the
same device. The other approach was to have copy-emulation by moving
dm-kcopyd to block layer. But it also required moving core dm-io infra,
causing a massive churn across multiple dm-targets.
2. Remove export in bio_map_kern()
3. Change copy_offload sysfs to accept 0 or else
4. Rename copy support flag to QUEUE_FLAG_SIMPLE_COPY
5. Rename payload entries, add source bdev field to be used while
partition remapping, remove copy_size
6. Change the blkdev_issue_copy() interface to accept destination and
source values in sector rather in bytes
7. Add payload to bio using bio_map_kern() for copy_offload case
8. Add check to return error if one of the source range length is 0
9. Add BLKDEV_COPY_NOEMULATION flag to allow user to not try copy
emulation incase of copy offload is not supported. Caller can his use
his existing copying logic to complete the io.
10. Bug fix copy checks and reduce size of rcu_lock()
Changes from RFC v3
1. gfp_flag fixes.
2. Export bio_map_kern() and use it to allocate and add pages to bio.
3. Move copy offload, reading to buf, writing from buf to separate functions
4. Send read bio of copy offload by chaining them and submit asynchronously
5. Add gendisk->part0 and part->bd_start_sect changes to blk_check_copy().
6. Move single source range limit check to blk_check_copy()
7. Rename __blkdev_issue_copy() to blkdev_issue_copy and remove old helper.
8. Change blkdev_issue_copy() interface generic to accepts destination bdev
to support XCOPY as well.
9. Add invalidate_kernel_vmap_range() after reading data for vmalloc'ed memory.
10. Fix buf allocoation logic to allocate buffer for the total size of copy.
11. Reword patch commit description.
Changes from RFC v2
1. Add emulation support for devices not supporting copy.
2. Add *copy_offload* sysfs entry to enable and disable copy_offload
in devices supporting simple copy.
3. Remove simple copy support for stacked devices.
Changes from RFC v1:
1. Fix memory leak in __blkdev_issue_copy
2. Unmark blk_check_copy inline
3. Fix line break in blk_check_copy_eod
4. Remove p checks and made code more readable
5. Don't use bio_set_op_attrs and remove op and set
bi_opf directly
6. Use struct_size to calculate total_size
7. Fix partition remap of copy destination
8. Remove mcl,mssrl,msrc from nvme_ns
9. Initialize copy queue limits to 0 in nvme_config_copy
10. Remove return in QUEUE_FLAG_COPY check
11. Remove unused OCFS
Nitesh Shetty (4):
block: Introduce queue limits for copy-offload support
block: copy offload support infrastructure
block: Introduce a new ioctl for simple copy
block: add emulation for simple copy
SelvaKumar S (3):
block: make bio_map_kern() non static
nvme: add simple copy support
dm kcopyd: add simple copy offload support
block/blk-core.c | 84 ++++++++-
block/blk-lib.c | 352 ++++++++++++++++++++++++++++++++++++++
block/blk-map.c | 2 +-
block/blk-settings.c | 4 +
block/blk-sysfs.c | 51 ++++++
block/blk-zoned.c | 1 +
block/bounce.c | 1 +
block/ioctl.c | 33 ++++
drivers/md/dm-kcopyd.c | 56 +++++-
drivers/nvme/host/core.c | 83 +++++++++
drivers/nvme/host/trace.c | 19 ++
include/linux/bio.h | 1 +
include/linux/blk_types.h | 20 +++
include/linux/blkdev.h | 21 +++
include/linux/nvme.h | 43 ++++-
include/uapi/linux/fs.h | 20 +++
16 files changed, 775 insertions(+), 16 deletions(-)
--
2.25.1
From: SelvaKumar S <hidden> Date: 2021-08-17 11:28:50
Make bio_map_kern() non static, so that copy offload/emulation can use
it to add vmalloced memory to bio.
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Nitesh Shetty <redacted>
---
block/blk-map.c | 2 +-
include/linux/blkdev.h | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
From: SelvaKumar S <hidden> Date: 2021-08-17 11:29:12
From: Nitesh Shetty <redacted>
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
---
block/blk-core.c | 84 ++++++++++++-
block/blk-lib.c | 252 ++++++++++++++++++++++++++++++++++++++
block/blk-zoned.c | 1 +
block/bounce.c | 1 +
include/linux/bio.h | 1 +
include/linux/blk_types.h | 20 +++
include/linux/blkdev.h | 13 ++
include/uapi/linux/fs.h | 12 ++
8 files changed, 378 insertions(+), 6 deletions(-)
@@ -704,6 +704,17 @@ static noinline int should_fail_bio(struct bio *bio)}ALLOW_ERROR_INJECTION(should_fail_bio,ERRNO);+staticinlineintbio_check_copy_eod(structbio*bio,sector_tstart,+sector_tnr_sectors,sector_tmax_sect)+{+if(nr_sectors&&max_sect&&+(nr_sectors>max_sect||start>max_sect-nr_sectors)){+handle_bad_sector(bio,max_sect);+return-EIO;+}+return0;+}+/**Checkwhetherthisbioextendsbeyondtheendofthedeviceorpartition.*Thismaywellhappen-thekernelcallsbread()withoutcheckingthesizeof
@@ -723,6 +734,61 @@ static inline int bio_check_eod(struct bio *bio)return0;}+/*+*checkforeodlimitsandremaprangesifneeded+*/+staticintblk_check_copy(structbio*bio)+{+structblk_copy_payload*payload=bio_data(bio);+sector_tdst_max_sect,dst_start_sect,copy_size=0;+sector_tsrc_max_sect,src_start_sect;+structblock_device*bd_part;+inti,ret=-EIO;++rcu_read_lock();++bd_part=bio->bi_bdev;+if(unlikely(!bd_part))+gotoerr;++dst_max_sect=bdev_nr_sectors(bd_part);+dst_start_sect=bd_part->bd_start_sect;++src_max_sect=bdev_nr_sectors(payload->src_bdev);+src_start_sect=payload->src_bdev->bd_start_sect;++if(unlikely(should_fail_request(bd_part,bio->bi_iter.bi_size)))+gotoerr;++if(unlikely(bio_check_ro(bio)))+gotoerr;++rcu_read_unlock();++for(i=0;i<payload->copy_nr_ranges;i++){+ret=bio_check_copy_eod(bio,payload->range[i].src,+payload->range[i].len,src_max_sect);+if(unlikely(ret))+gotoout;++payload->range[i].src+=src_start_sect;+copy_size+=payload->range[i].len;+}++/* check if copy length crosses eod */+ret=bio_check_copy_eod(bio,bio->bi_iter.bi_sector,+copy_size,dst_max_sect);+if(unlikely(ret))+gotoout;++bio->bi_iter.bi_sector+=dst_start_sect;+return0;+err:+rcu_read_unlock();+out:+returnret;+}+/**Remapblocknofpartitionptoblockn+start(p)ofthedisk.*/
@@ -799,13 +865,15 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)if(should_fail_bio(bio))gotoend_io;-if(unlikely(bio_check_ro(bio)))-gotoend_io;-if(!bio_flagged(bio,BIO_REMAPPED)){-if(unlikely(bio_check_eod(bio)))-gotoend_io;-if(bdev->bd_partno&&unlikely(blk_partition_remap(bio)))+if(likely(!op_is_copy(bio->bi_opf))){+if(unlikely(bio_check_ro(bio)))gotoend_io;+if(!bio_flagged(bio,BIO_REMAPPED)){+if(unlikely(bio_check_eod(bio)))+gotoend_io;+if(bdev->bd_partno&&unlikely(blk_partition_remap(bio)))+gotoend_io;+}}/*
@@ -829,6 +897,10 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)if(!blk_queue_discard(q))gotonot_supported;break;+caseREQ_OP_COPY:+if(unlikely(blk_check_copy(bio)))+gotoend_io;+break;caseREQ_OP_SECURE_ERASE:if(!blk_queue_secure_erase(q))gotonot_supported;
@@ -176,6 +176,7 @@ static struct bio *bounce_clone_bio(struct bio *bio_src)bio->bi_iter.bi_size=bio_src->bi_iter.bi_size;switch(bio_op(bio)){+caseREQ_OP_COPY:caseREQ_OP_DISCARD:caseREQ_OP_SECURE_ERASE:caseREQ_OP_WRITE_ZEROES:
@@ -347,6 +347,8 @@ enum req_opf {REQ_OP_ZONE_RESET=15,/* reset all the zone present on the device */REQ_OP_ZONE_RESET_ALL=17,+/* copy ranges within device */+REQ_OP_COPY=19,/* Driver private requests */REQ_OP_DRV_IN=34,
@@ -470,6 +472,11 @@ static inline bool op_is_discard(unsigned int op)return(op&REQ_OP_MASK)==REQ_OP_DISCARD;}+staticinlineboolop_is_copy(unsignedintop)+{+return(op&REQ_OP_MASK)==REQ_OP_COPY;+}+/**Checkifabioorrequestoperationisazonemanagementoperation,with*theexceptionofREQ_OP_ZONE_RESET_ALLwhichistreatedasaspecialcase
@@ -64,6 +64,18 @@ struct fstrim_range {__u64minlen;};+/* Maximum no of entries supported */+#define MAX_COPY_NR_RANGE (1 << 12)++/* maximum total copy length */+#define MAX_COPY_TOTAL_LENGTH (1 << 21)++/* Source range entry for copy */+structrange_entry{+__u64src;+__u64len;+};+/* extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions */#define FILE_DEDUPE_RANGE_SAME 0#define FILE_DEDUPE_RANGE_DIFFERS 1
From: SelvaKumar S <hidden> Date: 2021-08-17 11:29:26
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
From: SelvaKumar S <hidden> Date: 2021-08-17 11:29:30
From: Nitesh Shetty <redacted>
For the devices which does not support simple copy, copy emulation is
added. Also for stacked devices, copy is performed via emulation.
Copy-emulation is implemented by allocating maximum possible memory
less than or equal to total copy size. The source ranges are read
into memory by chaining bio for each source ranges and submitting them
async and the last bio waits for completion. After data is read, it is
written to the destination and the process is repeated till no source
ranges left.
bio_map_kern() is used to allocate bio and add pages of copy buffer to
bio. As bio->bi_private and bio->bi_end_io are needed for chaining the
bio and gets over-written, invalidate_kernel_vmap_range() for read is
called in the caller.
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
---
block/blk-lib.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
@@ -398,6 +496,8 @@ int blkdev_issue_copy(struct block_device *src_bdev, int nr_srcs,if(blk_check_offload_scc(src_q,dest_q))ret=blk_copy_offload_scc(src_bdev,nr_srcs,src_rlist,dest_bdev,dest,gfp_mask);+else+ret=blk_copy_emulate(src_bdev,nr_srcs,src_rlist,dest_bdev,dest,gfp_mask);returnret;}
From: SelvaKumar S <hidden> Date: 2021-08-17 11:29:32
Add support for TP 4065a ("Simple Copy Command"), v2020.05.04 ("Ratified")
For device supporting native simple copy, this implementation accepts
the payload passed from the block layer and convert payload to form
simple copy command and submit to the device.
Set the device copy limits to queue limits. By default copy_offload
is disabled.
End-to-end protection is done by setting both PRINFOR and PRINFOW
to 0.
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: Javier González <redacted>
---
drivers/nvme/host/core.c | 83 +++++++++++++++++++++++++++++++++++++++
drivers/nvme/host/trace.c | 19 +++++++++
include/linux/nvme.h | 43 ++++++++++++++++++--
3 files changed, 142 insertions(+), 3 deletions(-)
From: SelvaKumar S <hidden> Date: 2021-08-17 11:29:41
Introduce copy_jobs to use copy-offload, if supported by underlying devices
otherwise fall back to existing method.
run_copy_jobs() calls block layer copy offload API, if both source and
destination request queue are same and support copy offload.
On successful completion, destination regions copied count is made zero,
failed regions are processed via existing method.
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Nitesh Shetty <redacted>
---
drivers/md/dm-kcopyd.c | 56 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 50 insertions(+), 6 deletions(-)
On Tue, Aug 17, 2021 at 03:44:20PM +0530, SelvaKumar S wrote:
quoted hunk
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
If you have a "reserved" field, you HAVE to check that it is 0. If not,
you can never use it in the future.
Also, you can spell it out, we have lots of vowels :)
thanks,
greg k-h
On Tue, Aug 17, 2021 at 03:44:20PM +0530, SelvaKumar S wrote:
quoted hunk
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
No error checking for huge values of nr_range? Is that wise? You
really want userspace to be able to allocate "all" of the kernel memory
in the system?
thanks,
greg k-h
On Tue, Aug 17, 2021 at 6:40 PM Greg KH [off-list ref] wrote:
On Tue, Aug 17, 2021 at 03:44:20PM +0530, SelvaKumar S wrote:
quoted
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
No error checking for huge values of nr_range? Is that wise? You
really want userspace to be able to allocate "all" of the kernel memory
in the system?
thanks,
greg k-h
We added a kernel imposed limit MAX_COPY_NR_RANGE for that purpose,
but missed adding the check here.
Will have that fixed. Thanks for pointing this out.
Nitesh Shetty
From: Bart Van Assche <bvanassche@acm.org> Date: 2021-08-17 17:14:33
On 8/17/21 3:14 AM, SelvaKumar S wrote:
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate
operations for reading and writing is fundamentally incompatible with
the device mapper. I think we need a copy-offloading implementation that
is compatible with the device mapper.
Storing the parameters of the copy operation in the bio payload is
incompatible with the current implementation of bio_split().
In other words, I think there are fundamental problems with this patch
series.
Bart.
Introduce copy_jobs to use copy-offload, if supported by underlying devices
otherwise fall back to existing method.
dm-kcopyd is usually used on the dm-linear target. And this patchset
doesn't support passing copy requests through the linear target - so this
patch doesn't seem useful.
Mikulas
quoted hunk
run_copy_jobs() calls block layer copy offload API, if both source and
destination request queue are same and support copy offload.
On successful completion, destination regions copied count is made zero,
failed regions are processed via existing method.
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Nitesh Shetty <redacted>
---
drivers/md/dm-kcopyd.c | 56 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 50 insertions(+), 6 deletions(-)
block/blk-lib.c:197:5: warning: no previous prototype for function 'blk_copy_offload_submit_bio' [-Wmissing-prototypes]
int blk_copy_offload_submit_bio(struct block_device *bdev,
^
block/blk-lib.c:197:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int blk_copy_offload_submit_bio(struct block_device *bdev,
^
static
quoted
block/blk-lib.c:250:5: warning: no previous prototype for function 'blk_copy_offload_scc' [-Wmissing-prototypes]
int blk_copy_offload_scc(struct block_device *src_bdev, int nr_srcs,
^
block/blk-lib.c:250:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int blk_copy_offload_scc(struct block_device *src_bdev, int nr_srcs,
^
static
2 warnings generated.
vim +/blk_copy_offload_submit_bio +197 block/blk-lib.c
196
> 197 int blk_copy_offload_submit_bio(struct block_device *bdev,
198 struct blk_copy_payload *payload, int payload_size,
199 struct cio *cio, gfp_t gfp_mask)
200 {
201 struct request_queue *q = bdev_get_queue(bdev);
202 struct bio *bio;
203
204 bio = bio_map_kern(q, payload, payload_size, gfp_mask);
205 if (IS_ERR(bio))
206 return PTR_ERR(bio);
207
208 bio_set_dev(bio, bdev);
209 bio->bi_opf = REQ_OP_COPY | REQ_NOMERGE;
210 bio->bi_iter.bi_sector = payload->dest;
211 bio->bi_end_io = cio_bio_end_io;
212 bio->bi_private = cio;
213 atomic_inc(&cio->refcount);
214 submit_bio(bio);
215
216 return 0;
217 }
218
219 /* Go through all the enrties inside user provided payload, and determine the
220 * maximum number of entries in a payload, based on device's scc-limits.
221 */
222 static inline int blk_max_payload_entries(int nr_srcs, struct range_entry *rlist,
223 int max_nr_srcs, sector_t max_copy_range_sectors, sector_t max_copy_len)
224 {
225 sector_t range_len, copy_len = 0, remaining = 0;
226 int ri = 0, pi = 1, max_pi = 0;
227
228 for (ri = 0; ri < nr_srcs; ri++) {
229 for (remaining = rlist[ri].len; remaining > 0; remaining -= range_len) {
230 range_len = min3(remaining, max_copy_range_sectors,
231 max_copy_len - copy_len);
232 pi++;
233 copy_len += range_len;
234
235 if ((pi == max_nr_srcs) || (copy_len == max_copy_len)) {
236 max_pi = max(max_pi, pi);
237 pi = 1;
238 copy_len = 0;
239 }
240 }
241 }
242
243 return max(max_pi, pi);
244 }
245
246 /*
247 * blk_copy_offload_scc - Use device's native copy offload feature
248 * Go through user provide payload, prepare new payload based on device's copy offload limits.
249 */
> 250 int blk_copy_offload_scc(struct block_device *src_bdev, int nr_srcs,
251 struct range_entry *rlist, struct block_device *dest_bdev,
252 sector_t dest, gfp_t gfp_mask)
253 {
254 struct request_queue *q = bdev_get_queue(dest_bdev);
255 struct cio *cio = NULL;
256 struct blk_copy_payload *payload;
257 sector_t range_len, copy_len = 0, remaining = 0;
258 sector_t src_blk, cdest = dest;
259 sector_t max_copy_range_sectors, max_copy_len;
260 int ri = 0, pi = 0, ret = 0, payload_size, max_pi, max_nr_srcs;
261
262 cio = kzalloc(sizeof(struct cio), GFP_KERNEL);
263 if (!cio)
264 return -ENOMEM;
265 atomic_set(&cio->refcount, 0);
266
267 max_nr_srcs = q->limits.max_copy_nr_ranges;
268 max_copy_range_sectors = q->limits.max_copy_range_sectors;
269 max_copy_len = q->limits.max_copy_sectors;
270
271 max_pi = blk_max_payload_entries(nr_srcs, rlist, max_nr_srcs,
272 max_copy_range_sectors, max_copy_len);
273 payload_size = struct_size(payload, range, max_pi);
274
275 payload = kvmalloc(payload_size, gfp_mask);
276 if (!payload) {
277 ret = -ENOMEM;
278 goto free_cio;
279 }
280 payload->src_bdev = src_bdev;
281
282 for (ri = 0; ri < nr_srcs; ri++) {
283 for (remaining = rlist[ri].len, src_blk = rlist[ri].src; remaining > 0;
284 remaining -= range_len, src_blk += range_len) {
285
286 range_len = min3(remaining, max_copy_range_sectors,
287 max_copy_len - copy_len);
288 payload->range[pi].len = range_len;
289 payload->range[pi].src = src_blk;
290 pi++;
291 copy_len += range_len;
292
293 /* Submit current payload, if crossing device copy limits */
294 if ((pi == max_nr_srcs) || (copy_len == max_copy_len)) {
295 payload->dest = cdest;
296 payload->copy_nr_ranges = pi;
297 ret = blk_copy_offload_submit_bio(dest_bdev, payload,
298 payload_size, cio, gfp_mask);
299 if (ret)
300 goto free_payload;
301
302 /* reset index, length and allocate new payload */
303 pi = 0;
304 cdest += copy_len;
305 copy_len = 0;
306 payload = kvmalloc(payload_size, gfp_mask);
307 if (!payload) {
308 ret = -ENOMEM;
309 goto free_cio;
310 }
311 payload->src_bdev = src_bdev;
312 }
313 }
314 }
315
316 if (pi) {
317 payload->dest = cdest;
318 payload->copy_nr_ranges = pi;
319 ret = blk_copy_offload_submit_bio(dest_bdev, payload, payload_size, cio, gfp_mask);
320 if (ret)
321 goto free_payload;
322 }
323
324 /* Wait for completion of all IO's*/
325 ret = cio_await_completion(cio);
326
327 return ret;
328
329 free_payload:
330 kvfree(payload);
331 free_cio:
332 cio_await_completion(cio);
333 return ret;
334 }
335
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate operations
for reading and writing is fundamentally incompatible with the device mapper.
I think we need a copy-offloading implementation that is compatible with the
device mapper.
I once wrote a copy offload implementation that is compatible with device
mapper. The copy operation creates two bios (one for reading and one for
writing), passes them independently through device mapper and pairs them
at the physical device driver.
It's here: http://people.redhat.com/~mpatocka/patches/kernel/xcopy/current
I verified that it works with iSCSI. Would you be interested in continuing
this work?
Mikulas
Storing the parameters of the copy operation in the bio payload is
incompatible with the current implementation of bio_split().
In other words, I think there are fundamental problems with this patch series.
Bart.
From: Douglas Gilbert <dgilbert@interlog.com> Date: 2021-08-17 22:02:36
On 2021-08-17 4:41 p.m., Mikulas Patocka wrote:
On Tue, 17 Aug 2021, Bart Van Assche wrote:
quoted
On 8/17/21 3:14 AM, SelvaKumar S wrote:
quoted
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate operations
for reading and writing is fundamentally incompatible with the device mapper.
I think we need a copy-offloading implementation that is compatible with the
device mapper.
I once wrote a copy offload implementation that is compatible with device
mapper. The copy operation creates two bios (one for reading and one for
writing), passes them independently through device mapper and pairs them
at the physical device driver.
It's here: http://people.redhat.com/~mpatocka/patches/kernel/xcopy/current
In my copy solution the read-side and write-side bio pairs share the same
storage (i.e. ram) This gets around the need to copy data between the bio_s.
See:
https://sg.danny.cz/sg/sg_v40.html
in Section 8 on Request sharing. This technique can be efficiently extend to
source --> destination1,destination2,... copies.
Doug Gilbert
I verified that it works with iSCSI. Would you be interested in continuing
this work?
Mikulas
quoted
Storing the parameters of the copy operation in the bio payload is
incompatible with the current implementation of bio_split().
In other words, I think there are fundamental problems with this patch series.
Bart.
From: Bart Van Assche <bvanassche@acm.org> Date: 2021-08-17 22:06:41
On 8/17/21 2:53 PM, Douglas Gilbert wrote:
On 2021-08-17 4:41 p.m., Mikulas Patocka wrote:
quoted
On Tue, 17 Aug 2021, Bart Van Assche wrote:
quoted
On 8/17/21 3:14 AM, SelvaKumar S wrote:
quoted
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate
operations
for reading and writing is fundamentally incompatible with the device
mapper.
I think we need a copy-offloading implementation that is compatible
with the
device mapper.
I once wrote a copy offload implementation that is compatible with device
mapper. The copy operation creates two bios (one for reading and one for
writing), passes them independently through device mapper and pairs them
at the physical device driver.
It's here:
http://people.redhat.com/~mpatocka/patches/kernel/xcopy/current
In my copy solution the read-side and write-side bio pairs share the
same storage (i.e. ram) This gets around the need to copy data between
the bio_s.
See:
https://sg.danny.cz/sg/sg_v40.html
in Section 8 on Request sharing. This technique can be efficiently
extend to
source --> destination1,destination2,... copies.
Doug Gilbert
quoted
I verified that it works with iSCSI. Would you be interested in
continuing
this work?
Hi Mikulas and Doug,
Yes, I'm interested in continuing Mikulas' work on copy offloading. I
will take a look at Doug's approach too for sharing buffers between
read-side and write-side bios. It may take a few months however before I
can find the time to work on this.
Thanks,
Bart.
From: kernel test robot <hidden> Date: 2021-08-17 22:11:05
Hi SelvaKumar,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on block/for-next]
[also build test WARNING on dm/for-next next-20210817]
[cannot apply to linus/master linux-nvme/for-next v5.14-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/SelvaKumar-S/block-make-bio_map_kern-non-static/20210817-193111
base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
config: hexagon-randconfig-r013-20210816 (attached as .config)
compiler: clang version 12.0.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/c307f1051a72122d636502c6885df8b2b25ed697
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review SelvaKumar-S/block-make-bio_map_kern-non-static/20210817-193111
git checkout c307f1051a72122d636502c6885df8b2b25ed697
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=hexagon
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All warnings (new ones prefixed by >>):
block/blk-lib.c:197:5: warning: no previous prototype for function 'blk_copy_offload_submit_bio' [-Wmissing-prototypes]
int blk_copy_offload_submit_bio(struct block_device *bdev,
^
block/blk-lib.c:197:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int blk_copy_offload_submit_bio(struct block_device *bdev,
^
static
block/blk-lib.c:250:5: warning: no previous prototype for function 'blk_copy_offload_scc' [-Wmissing-prototypes]
int blk_copy_offload_scc(struct block_device *src_bdev, int nr_srcs,
^
block/blk-lib.c:250:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int blk_copy_offload_scc(struct block_device *src_bdev, int nr_srcs,
^
static
quoted
block/blk-lib.c:336:5: warning: no previous prototype for function 'blk_submit_rw_buf' [-Wmissing-prototypes]
int blk_submit_rw_buf(struct block_device *bdev, void *buf, sector_t buf_len,
^
block/blk-lib.c:336:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int blk_submit_rw_buf(struct block_device *bdev, void *buf, sector_t buf_len,
^
static
3 warnings generated.
vim +/blk_submit_rw_buf +336 block/blk-lib.c
335
> 336 int blk_submit_rw_buf(struct block_device *bdev, void *buf, sector_t buf_len,
337 sector_t sector, unsigned int op, gfp_t gfp_mask)
338 {
339 struct request_queue *q = bdev_get_queue(bdev);
340 struct bio *bio, *parent = NULL;
341 sector_t max_hw_len = min_t(unsigned int, queue_max_hw_sectors(q),
342 queue_max_segments(q) << (PAGE_SHIFT - SECTOR_SHIFT));
343 sector_t len, remaining;
344 int ret;
345
346 for (remaining = buf_len; remaining > 0; remaining -= len) {
347 len = min_t(int, max_hw_len, remaining);
348 retry:
349 bio = bio_map_kern(q, buf, len << SECTOR_SHIFT, gfp_mask);
350 if (IS_ERR(bio)) {
351 len >>= 1;
352 if (len)
353 goto retry;
354 return PTR_ERR(bio);
355 }
356
357 bio->bi_iter.bi_sector = sector;
358 bio->bi_opf = op;
359 bio_set_dev(bio, bdev);
360 bio->bi_end_io = NULL;
361 bio->bi_private = NULL;
362
363 if (parent) {
364 bio_chain(parent, bio);
365 submit_bio(parent);
366 }
367 parent = bio;
368 sector += len;
369 }
370 ret = submit_bio_wait(bio);
371 bio_put(bio);
372
373 return ret;
374 }
375
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
From: "Darrick J. Wong" <djwong@kernel.org> Date: 2021-08-17 23:36:15
On Tue, Aug 17, 2021 at 03:44:20PM +0530, SelvaKumar S wrote:
quoted hunk
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
If the maximum number of elements in the range list is 1<<12, there's no
need for this to be larger than a u16, right?
+ __u64 range_list;
Pointers embedded in a structure are /not/ a good idea, because this
will create a lot of compatibility headaches for 32-bit binaries running
on 64-bit kernels. Please just make the size of this header structure
a multiple of 8 bytes and put the range_entry list immediately after it.
struct copy_range {
__s64 dest_offset;
__u32 nr_range_entries;
__u32 flags;
__u64 reserved[2];
};
struct __user range_entry *re = ((struct range_entry *)(copyhead + 1));
copy_from_user(&urk, re...);
--D
quoted hunk
+ __u64 rsvd;
+};
+
/* extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions */
#define FILE_DEDUPE_RANGE_SAME 0
#define FILE_DEDUPE_RANGE_DIFFERS 1
@@ -197,6 +204,7 @@ struct fsxattr { #define BLKROTATIONAL _IO(0x12,126) #define BLKZEROOUT _IO(0x12,127) #define BLKGETDISKSEQ _IOR(0x12,128,__u64)+#define BLKCOPY _IOWR(0x12, 129, struct copy_range) /* * A jump here: 130-136 are reserved for zoned block devices * (see uapi/linux/blkzoned.h)
From: "Darrick J. Wong" <djwong@kernel.org> Date: 2021-08-17 23:38:00
On Tue, Aug 17, 2021 at 03:44:16PM +0530, SelvaKumar S wrote:
This started out as an attempt to support NVMe Simple Copy Command (SCC),
and evolved during the RFC review process.
The patchset, at this point, contains -
1. SCC support in NVMe driver
2. Block-layer infra for copy-offload operation
3. ioctl interface to user-space
4. copy-emulation infra in the block-layer
5. copy-offload plumbing to dm-kcopyd (thus creating couple of in-kernel
users such as dm-clone)
The SCC specification, i.e. TP4065a can be found in following link
https://nvmexpress.org/wp-content/uploads/NVM-Express-1.4-Ratified-TPs.zip
Simple copy is a copy offloading feature and can be used to copy multiple
contiguous ranges (source_ranges) of LBA's to a single destination LBA
within the device, reducing traffic between host and device.
We define a block ioctl for copy and copy payload format similar to
discard. For device supporting native simple copy, we attach the control
information as payload to the bio and submit to the device. Copy emulation
is implemented incase underlaying device does not support copy offload or
based on sysfs choice. Copy emulation is done by reading each source range
into buffer and writing it to the destination.
Seems useful. Would you mind adapting the loop driver to call
copy_file_range (for files) so that anyone interested in making a
filesystem use this capability (cough) can write fstests?
--D
At present this implementation does not support copy offload for stacked/dm
devices, rather copy operation is completed through emulation.
One of the in-kernel use case for copy-offload is implemented by this
patchset. dm-kcopyd infra has been changed to leverage the copy-offload if
it is natively available. Other future use-cases could be F2FS GC, BTRFS
relocation/balance and copy_file_range.
Following limits are added to queue limits and are exposed via sysfs to
userspace, which user can use to form a payload.
- copy_offload:
configurable, can be used set emulation or copy offload
0 to disable copy offload,
1 to enable copy offloading support. Offload can be only
enabled, if underlaying device supports offload
- max_copy_sectors:
total copy length supported by copy offload feature in device.
0 indicates copy offload is not supported.
- max_copy_nr_ranges:
maximum number of source range entries supported by copy offload
feature in device
- max_copy_range_sectors:
maximum copy length per source range entry
*blkdev_issue_copy* takes source bdev, no of sources, array of source
ranges (in sectors), destination bdev and destination offset(in sectors).
If both source and destination block devices are same and queue parameter
copy_offload is 1, then copy is done through native copy offloading.
Copy emulation is used in otherwise.
Changes from RFC v5
1. Handle copy larger than maximum copy limits
2. Create copy context and submit copy offload asynchronously
3. Remove BLKDEV_COPY_NOEMULATION opt-in option of copy offload and
check for copy support before submission from dm and other layers
4. Allocate maximum possible allocatable buffer for copy emulation
rather failing very large copy offload.
5. Fix copy_offload sysfs to be either have 0 or 1
Changes from RFC v4
1. Extend dm-kcopyd to leverage copy-offload, while copying within the
same device. The other approach was to have copy-emulation by moving
dm-kcopyd to block layer. But it also required moving core dm-io infra,
causing a massive churn across multiple dm-targets.
2. Remove export in bio_map_kern()
3. Change copy_offload sysfs to accept 0 or else
4. Rename copy support flag to QUEUE_FLAG_SIMPLE_COPY
5. Rename payload entries, add source bdev field to be used while
partition remapping, remove copy_size
6. Change the blkdev_issue_copy() interface to accept destination and
source values in sector rather in bytes
7. Add payload to bio using bio_map_kern() for copy_offload case
8. Add check to return error if one of the source range length is 0
9. Add BLKDEV_COPY_NOEMULATION flag to allow user to not try copy
emulation incase of copy offload is not supported. Caller can his use
his existing copying logic to complete the io.
10. Bug fix copy checks and reduce size of rcu_lock()
Changes from RFC v3
1. gfp_flag fixes.
2. Export bio_map_kern() and use it to allocate and add pages to bio.
3. Move copy offload, reading to buf, writing from buf to separate functions
4. Send read bio of copy offload by chaining them and submit asynchronously
5. Add gendisk->part0 and part->bd_start_sect changes to blk_check_copy().
6. Move single source range limit check to blk_check_copy()
7. Rename __blkdev_issue_copy() to blkdev_issue_copy and remove old helper.
8. Change blkdev_issue_copy() interface generic to accepts destination bdev
to support XCOPY as well.
9. Add invalidate_kernel_vmap_range() after reading data for vmalloc'ed memory.
10. Fix buf allocoation logic to allocate buffer for the total size of copy.
11. Reword patch commit description.
Changes from RFC v2
1. Add emulation support for devices not supporting copy.
2. Add *copy_offload* sysfs entry to enable and disable copy_offload
in devices supporting simple copy.
3. Remove simple copy support for stacked devices.
Changes from RFC v1:
1. Fix memory leak in __blkdev_issue_copy
2. Unmark blk_check_copy inline
3. Fix line break in blk_check_copy_eod
4. Remove p checks and made code more readable
5. Don't use bio_set_op_attrs and remove op and set
bi_opf directly
6. Use struct_size to calculate total_size
7. Fix partition remap of copy destination
8. Remove mcl,mssrl,msrc from nvme_ns
9. Initialize copy queue limits to 0 in nvme_config_copy
10. Remove return in QUEUE_FLAG_COPY check
11. Remove unused OCFS
Nitesh Shetty (4):
block: Introduce queue limits for copy-offload support
block: copy offload support infrastructure
block: Introduce a new ioctl for simple copy
block: add emulation for simple copy
SelvaKumar S (3):
block: make bio_map_kern() non static
nvme: add simple copy support
dm kcopyd: add simple copy offload support
block/blk-core.c | 84 ++++++++-
block/blk-lib.c | 352 ++++++++++++++++++++++++++++++++++++++
block/blk-map.c | 2 +-
block/blk-settings.c | 4 +
block/blk-sysfs.c | 51 ++++++
block/blk-zoned.c | 1 +
block/bounce.c | 1 +
block/ioctl.c | 33 ++++
drivers/md/dm-kcopyd.c | 56 +++++-
drivers/nvme/host/core.c | 83 +++++++++
drivers/nvme/host/trace.c | 19 ++
include/linux/bio.h | 1 +
include/linux/blk_types.h | 20 +++
include/linux/blkdev.h | 21 +++
include/linux/nvme.h | 43 ++++-
include/uapi/linux/fs.h | 20 +++
16 files changed, 775 insertions(+), 16 deletions(-)
--
2.25.1
On Wed, Aug 18, 2021 at 5:06 AM Darrick J. Wong [off-list ref] wrote:
On Tue, Aug 17, 2021 at 03:44:20PM +0530, SelvaKumar S wrote:
quoted
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
If the maximum number of elements in the range list is 1<<12, there's no
need for this to be larger than a u16, right?
quoted
+ __u64 range_list;
Pointers embedded in a structure are /not/ a good idea, because this
will create a lot of compatibility headaches for 32-bit binaries running
on 64-bit kernels. Please just make the size of this header structure
a multiple of 8 bytes and put the range_entry list immediately after it.
struct copy_range {
__s64 dest_offset;
__u32 nr_range_entries;
__u32 flags;
__u64 reserved[2];
};
struct __user range_entry *re = ((struct range_entry *)(copyhead + 1));
copy_from_user(&urk, re...);
--D
Thanks, this is better. 'Reserved' field was there to be used for
future extension of the interface.
Now that you mentioned 'flags', it seems we can do away with
'reserved' fields altogether?
Regards,
Nitesh Shetty
On Wed, Aug 18, 2021 at 5:07 AM Darrick J. Wong [off-list ref] wrote:
On Tue, Aug 17, 2021 at 03:44:16PM +0530, SelvaKumar S wrote:
quoted
This started out as an attempt to support NVMe Simple Copy Command (SCC),
and evolved during the RFC review process.
The patchset, at this point, contains -
1. SCC support in NVMe driver
2. Block-layer infra for copy-offload operation
3. ioctl interface to user-space
4. copy-emulation infra in the block-layer
5. copy-offload plumbing to dm-kcopyd (thus creating couple of in-kernel
users such as dm-clone)
The SCC specification, i.e. TP4065a can be found in following link
https://nvmexpress.org/wp-content/uploads/NVM-Express-1.4-Ratified-TPs.zip
Simple copy is a copy offloading feature and can be used to copy multiple
contiguous ranges (source_ranges) of LBA's to a single destination LBA
within the device, reducing traffic between host and device.
We define a block ioctl for copy and copy payload format similar to
discard. For device supporting native simple copy, we attach the control
information as payload to the bio and submit to the device. Copy emulation
is implemented incase underlaying device does not support copy offload or
based on sysfs choice. Copy emulation is done by reading each source range
into buffer and writing it to the destination.
Seems useful. Would you mind adapting the loop driver to call
copy_file_range (for files) so that anyone interested in making a
filesystem use this capability (cough) can write fstests?
We are planning to look into copy_file_range plumbing after settling
on the current series,
which already became heavyweight for the first drop.
Nitesh Shetty
--D
quoted
At present this implementation does not support copy offload for stacked/dm
devices, rather copy operation is completed through emulation.
One of the in-kernel use case for copy-offload is implemented by this
patchset. dm-kcopyd infra has been changed to leverage the copy-offload if
it is natively available. Other future use-cases could be F2FS GC, BTRFS
relocation/balance and copy_file_range.
Following limits are added to queue limits and are exposed via sysfs to
userspace, which user can use to form a payload.
- copy_offload:
configurable, can be used set emulation or copy offload
0 to disable copy offload,
1 to enable copy offloading support. Offload can be only
enabled, if underlaying device supports offload
- max_copy_sectors:
total copy length supported by copy offload feature in device.
0 indicates copy offload is not supported.
- max_copy_nr_ranges:
maximum number of source range entries supported by copy offload
feature in device
- max_copy_range_sectors:
maximum copy length per source range entry
*blkdev_issue_copy* takes source bdev, no of sources, array of source
ranges (in sectors), destination bdev and destination offset(in sectors).
If both source and destination block devices are same and queue parameter
copy_offload is 1, then copy is done through native copy offloading.
Copy emulation is used in otherwise.
Changes from RFC v5
1. Handle copy larger than maximum copy limits
2. Create copy context and submit copy offload asynchronously
3. Remove BLKDEV_COPY_NOEMULATION opt-in option of copy offload and
check for copy support before submission from dm and other layers
4. Allocate maximum possible allocatable buffer for copy emulation
rather failing very large copy offload.
5. Fix copy_offload sysfs to be either have 0 or 1
Changes from RFC v4
1. Extend dm-kcopyd to leverage copy-offload, while copying within the
same device. The other approach was to have copy-emulation by moving
dm-kcopyd to block layer. But it also required moving core dm-io infra,
causing a massive churn across multiple dm-targets.
2. Remove export in bio_map_kern()
3. Change copy_offload sysfs to accept 0 or else
4. Rename copy support flag to QUEUE_FLAG_SIMPLE_COPY
5. Rename payload entries, add source bdev field to be used while
partition remapping, remove copy_size
6. Change the blkdev_issue_copy() interface to accept destination and
source values in sector rather in bytes
7. Add payload to bio using bio_map_kern() for copy_offload case
8. Add check to return error if one of the source range length is 0
9. Add BLKDEV_COPY_NOEMULATION flag to allow user to not try copy
emulation incase of copy offload is not supported. Caller can his use
his existing copying logic to complete the io.
10. Bug fix copy checks and reduce size of rcu_lock()
Changes from RFC v3
1. gfp_flag fixes.
2. Export bio_map_kern() and use it to allocate and add pages to bio.
3. Move copy offload, reading to buf, writing from buf to separate functions
4. Send read bio of copy offload by chaining them and submit asynchronously
5. Add gendisk->part0 and part->bd_start_sect changes to blk_check_copy().
6. Move single source range limit check to blk_check_copy()
7. Rename __blkdev_issue_copy() to blkdev_issue_copy and remove old helper.
8. Change blkdev_issue_copy() interface generic to accepts destination bdev
to support XCOPY as well.
9. Add invalidate_kernel_vmap_range() after reading data for vmalloc'ed memory.
10. Fix buf allocoation logic to allocate buffer for the total size of copy.
11. Reword patch commit description.
Changes from RFC v2
1. Add emulation support for devices not supporting copy.
2. Add *copy_offload* sysfs entry to enable and disable copy_offload
in devices supporting simple copy.
3. Remove simple copy support for stacked devices.
Changes from RFC v1:
1. Fix memory leak in __blkdev_issue_copy
2. Unmark blk_check_copy inline
3. Fix line break in blk_check_copy_eod
4. Remove p checks and made code more readable
5. Don't use bio_set_op_attrs and remove op and set
bi_opf directly
6. Use struct_size to calculate total_size
7. Fix partition remap of copy destination
8. Remove mcl,mssrl,msrc from nvme_ns
9. Initialize copy queue limits to 0 in nvme_config_copy
10. Remove return in QUEUE_FLAG_COPY check
11. Remove unused OCFS
Nitesh Shetty (4):
block: Introduce queue limits for copy-offload support
block: copy offload support infrastructure
block: Introduce a new ioctl for simple copy
block: add emulation for simple copy
SelvaKumar S (3):
block: make bio_map_kern() non static
nvme: add simple copy support
dm kcopyd: add simple copy offload support
block/blk-core.c | 84 ++++++++-
block/blk-lib.c | 352 ++++++++++++++++++++++++++++++++++++++
block/blk-map.c | 2 +-
block/blk-settings.c | 4 +
block/blk-sysfs.c | 51 ++++++
block/blk-zoned.c | 1 +
block/bounce.c | 1 +
block/ioctl.c | 33 ++++
drivers/md/dm-kcopyd.c | 56 +++++-
drivers/nvme/host/core.c | 83 +++++++++
drivers/nvme/host/trace.c | 19 ++
include/linux/bio.h | 1 +
include/linux/blk_types.h | 20 +++
include/linux/blkdev.h | 21 +++
include/linux/nvme.h | 43 ++++-
include/uapi/linux/fs.h | 20 +++
16 files changed, 775 insertions(+), 16 deletions(-)
--
2.25.1
From: "Darrick J. Wong" <djwong@kernel.org> Date: 2021-08-18 16:17:52
On Wed, Aug 18, 2021 at 09:07:54PM +0530, Nitesh Shetty wrote:
On Wed, Aug 18, 2021 at 5:06 AM Darrick J. Wong [off-list ref] wrote:
quoted
On Tue, Aug 17, 2021 at 03:44:20PM +0530, SelvaKumar S wrote:
quoted
From: Nitesh Shetty <redacted>
Add new BLKCOPY ioctl that offloads copying of one or more sources ranges
to a destination in the device. COPY ioctl accepts a 'copy_range'
structure that contains destination (in sectors), no of sources and
pointer to the array of source ranges. Each source range is represented by
'range_entry' that contains start and length of source ranges (in sectors)
MAX_COPY_NR_RANGE, limits the number of entries for the IOCTL and
MAX_COPY_TOTAL_LENGTH limits the total copy length, IOCTL can handle.
Example code, to issue BLKCOPY:
/* Sample example to copy three source-ranges [0, 8] [16, 8] [32,8] to
* [64,24], on the same device */
int main(void)
{
int ret, fd;
struct range_entry source_range[] = {{.src = 0, .len = 8},
{.src = 16, .len = 8}, {.src = 32, .len = 8},};
struct copy_range cr;
cr.dest = 64;
cr.nr_range = 3;
cr.range_list = (__u64)&source_range;
fd = open("/dev/nvme0n1", O_RDWR);
if (fd < 0) return 1;
ret = ioctl(fd, BLKCOPY, &cr);
if (ret < 0) printf("copy failure\n");
close(fd);
return ret;
}
Signed-off-by: Nitesh Shetty <redacted>
Signed-off-by: SelvaKumar S <redacted>
Signed-off-by: Kanchan Joshi <redacted>
---
block/ioctl.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fs.h | 8 ++++++++
2 files changed, 41 insertions(+)
If the maximum number of elements in the range list is 1<<12, there's no
need for this to be larger than a u16, right?
quoted
+ __u64 range_list;
Pointers embedded in a structure are /not/ a good idea, because this
will create a lot of compatibility headaches for 32-bit binaries running
on 64-bit kernels. Please just make the size of this header structure
a multiple of 8 bytes and put the range_entry list immediately after it.
struct copy_range {
__s64 dest_offset;
__u32 nr_range_entries;
__u32 flags;
__u64 reserved[2];
};
struct __user range_entry *re = ((struct range_entry *)(copyhead + 1));
copy_from_user(&urk, re...);
--D
Thanks, this is better. 'Reserved' field was there to be used for
future extension of the interface.
Now that you mentioned 'flags', it seems we can do away with
'reserved' fields altogether?
Bart, Mikulas
On Tue, Aug 17, 2021 at 10:44 PM Bart Van Assche [off-list ref] wrote:
On 8/17/21 3:14 AM, SelvaKumar S wrote:
quoted
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate
operations for reading and writing is fundamentally incompatible with
the device mapper. I think we need a copy-offloading implementation that
is compatible with the device mapper.
While each read/write command is for a single contiguous range of
device, with simple-copy we get to operate on multiple discontiguous
ranges, with a single command.
That seemed like a good opportunity to reduce control-plane traffic
(compared to read/write operations) as well.
With a separate read-and-write bio approach, each source-range will
spawn at least one read, one write and eventually one SCC command. And
it only gets worse as there could be many such discontiguous ranges (for
GC use-case at least) coming from user-space in a single payload.
Overall sequence will be
- Receive a payload from user-space
- Disassemble into many read-write pair bios at block-layer
- Assemble those (somehow) in NVMe to reduce simple-copy commands
- Send commands to device
We thought payload could be a good way to reduce the
disassembly/assembly work and traffic between block-layer to nvme.
How do you see this tradeoff? What seems necessary for device-mapper
usecase, appears to be a cost when device-mapper isn't used.
Especially for SCC (since copy is within single ns), device-mappers
may not be too compelling anyway.
Must device-mapper support be a requirement for the initial support atop SCC?
Or do you think it will still be a progress if we finalize the
user-space interface to cover all that is foreseeable.And for
device-mapper compatible transport between block-layer and NVMe - we
do it in the later stage when NVMe too comes up with better copy
capabilities?
--
Joshi
From: Bart Van Assche <bvanassche@acm.org> Date: 2021-08-20 21:18:34
On 8/20/21 3:39 AM, Kanchan Joshi wrote:
Bart, Mikulas
On Tue, Aug 17, 2021 at 10:44 PM Bart Van Assche [off-list ref] wrote:
quoted
On 8/17/21 3:14 AM, SelvaKumar S wrote:
quoted
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate
operations for reading and writing is fundamentally incompatible with
the device mapper. I think we need a copy-offloading implementation that
is compatible with the device mapper.
While each read/write command is for a single contiguous range of
device, with simple-copy we get to operate on multiple discontiguous
ranges, with a single command.
That seemed like a good opportunity to reduce control-plane traffic
(compared to read/write operations) as well.
With a separate read-and-write bio approach, each source-range will
spawn at least one read, one write and eventually one SCC command. And
it only gets worse as there could be many such discontiguous ranges (for
GC use-case at least) coming from user-space in a single payload.
Overall sequence will be
- Receive a payload from user-space
- Disassemble into many read-write pair bios at block-layer
- Assemble those (somehow) in NVMe to reduce simple-copy commands
- Send commands to device
We thought payload could be a good way to reduce the
disassembly/assembly work and traffic between block-layer to nvme.
How do you see this tradeoff? What seems necessary for device-mapper
usecase, appears to be a cost when device-mapper isn't used.
Especially for SCC (since copy is within single ns), device-mappers
may not be too compelling anyway.
Must device-mapper support be a requirement for the initial support atop SCC?
Or do you think it will still be a progress if we finalize the
user-space interface to cover all that is foreseeable.And for
device-mapper compatible transport between block-layer and NVMe - we
do it in the later stage when NVMe too comes up with better copy
capabilities?
Hi Kanchan,
These days there might be more systems that run the device mapper on top
of the NVMe driver or a SCSI driver than systems that do use the device
mapper. It is common practice these days to use dm-crypt on personal
workstations and laptops. LVM (dm-linear) is popular because it is more
flexible than a traditional partition table. Android phones use
dm-verity on top of hardware encryption. In other words, not supporting
the device mapper means that a very large number of use cases is
excluded. So I think supporting the device mapper from the start is
important, even if that means combining individual bios at the bottom of
the storage stack into simple copy commands.
Thanks,
Bart.
Hi Bart,Mikulas,Martin,Douglas,
We will go through your previous work and use this thread as a medium for
further discussion, if we come across issues to be sorted out.
Thank you,
Nitesh Shetty
On Sat, Aug 21, 2021 at 2:48 AM Bart Van Assche [off-list ref] wrote:
On 8/20/21 3:39 AM, Kanchan Joshi wrote:
quoted
Bart, Mikulas
On Tue, Aug 17, 2021 at 10:44 PM Bart Van Assche [off-list ref] wrote:
quoted
On 8/17/21 3:14 AM, SelvaKumar S wrote:
quoted
Introduce REQ_OP_COPY, a no-merge copy offload operation. Create
bio with control information as payload and submit to the device.
Larger copy operation may be divided if necessary by looking at device
limits. REQ_OP_COPY(19) is a write op and takes zone_write_lock when
submitted to zoned device.
Native copy offload is not supported for stacked devices.
Using a single operation for copy-offloading instead of separate
operations for reading and writing is fundamentally incompatible with
the device mapper. I think we need a copy-offloading implementation that
is compatible with the device mapper.
While each read/write command is for a single contiguous range of
device, with simple-copy we get to operate on multiple discontiguous
ranges, with a single command.
That seemed like a good opportunity to reduce control-plane traffic
(compared to read/write operations) as well.
With a separate read-and-write bio approach, each source-range will
spawn at least one read, one write and eventually one SCC command. And
it only gets worse as there could be many such discontiguous ranges (for
GC use-case at least) coming from user-space in a single payload.
Overall sequence will be
- Receive a payload from user-space
- Disassemble into many read-write pair bios at block-layer
- Assemble those (somehow) in NVMe to reduce simple-copy commands
- Send commands to device
We thought payload could be a good way to reduce the
disassembly/assembly work and traffic between block-layer to nvme.
How do you see this tradeoff? What seems necessary for device-mapper
usecase, appears to be a cost when device-mapper isn't used.
Especially for SCC (since copy is within single ns), device-mappers
may not be too compelling anyway.
Must device-mapper support be a requirement for the initial support atop SCC?
Or do you think it will still be a progress if we finalize the
user-space interface to cover all that is foreseeable.And for
device-mapper compatible transport between block-layer and NVMe - we
do it in the later stage when NVMe too comes up with better copy
capabilities?
Hi Kanchan,
These days there might be more systems that run the device mapper on top
of the NVMe driver or a SCSI driver than systems that do use the device
mapper. It is common practice these days to use dm-crypt on personal
workstations and laptops. LVM (dm-linear) is popular because it is more
flexible than a traditional partition table. Android phones use
dm-verity on top of hardware encryption. In other words, not supporting
the device mapper means that a very large number of use cases is
excluded. So I think supporting the device mapper from the start is
important, even if that means combining individual bios at the bottom of
the storage stack into simple copy commands.
Thanks,
Bart.