Skip to content

Commit 02ec1e7

Browse files
nj-shettykawasaki
authored andcommitted
fs, block: Add copy_file_range() support for block devices
Add copy_file_range() support for block devices. If input and output block devices have been opened with O_DIRECT and if copy offloading is supported use blkdev_copy_offload(). Otherwise use splice_copy_file_range(). Reviewed-by: Hannes Reinecke <[email protected]> Signed-off-by: Anuj Gupta <[email protected]> Signed-off-by: Nitesh Shetty <[email protected]> Signed-off-by: Bart Van Assche <[email protected]>
1 parent bfd7e5f commit 02ec1e7

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

block/fops.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/iomap.h>
2020
#include <linux/module.h>
2121
#include <linux/io_uring/cmd.h>
22+
#include <linux/splice.h>
2223
#include "blk.h"
2324

2425
static inline struct inode *bdev_file_inode(struct file *file)
@@ -861,6 +862,58 @@ static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
861862
return ret;
862863
}
863864

865+
static ssize_t blkdev_copy_file_range(struct file *file_in, loff_t pos_in,
866+
struct file *file_out, loff_t pos_out,
867+
size_t len, unsigned int flags)
868+
{
869+
struct block_device *in_bdev = I_BDEV(bdev_file_inode(file_in));
870+
struct block_device *out_bdev = I_BDEV(bdev_file_inode(file_out));
871+
loff_t in_end, out_end;
872+
int err;
873+
874+
if (check_add_overflow(pos_in, len, &in_end) ||
875+
PAGE_ALIGN(in_end) < in_end ||
876+
check_add_overflow(pos_out, len, &out_end) ||
877+
PAGE_ALIGN(out_end) < out_end)
878+
return -EINVAL;
879+
880+
/*
881+
* filemap_write_and_wait_range() and filemap_invalidate_inode() expect
882+
* that the 'end' argument is rounded up to the next multiple of
883+
* PAGE_SIZE.
884+
*/
885+
in_end = PAGE_ALIGN(in_end);
886+
out_end = PAGE_ALIGN(out_end);
887+
888+
if (bdev_max_copy_sectors(in_bdev) && bdev_max_copy_sectors(out_bdev) &&
889+
file_in->f_iocb_flags & file_out->f_iocb_flags & IOCB_DIRECT) {
890+
struct blk_copy_seg in_seg = { .pos = pos_in, .len = len };
891+
struct blk_copy_seg out_seg = { .pos = pos_out, .len = len };
892+
struct blk_copy_params params = {
893+
.in_bdev = in_bdev,
894+
.out_bdev = out_bdev,
895+
.in_nseg = 1,
896+
.in_segs = &in_seg,
897+
.out_nseg = 1,
898+
.out_segs = &out_seg,
899+
};
900+
err = filemap_write_and_wait_range(file_in->f_mapping, pos_in,
901+
in_end);
902+
if (err)
903+
return err;
904+
err = filemap_invalidate_inode(bdev_file_inode(file_out),
905+
/*flush=*/false,
906+
pos_out, out_end);
907+
if (err)
908+
return err;
909+
if (blkdev_copy_offload(&params) == 0)
910+
return len;
911+
/* If copy offloading fails, fall back to onloading. */
912+
}
913+
914+
return splice_copy_file_range(file_in, pos_in, file_out, pos_out, len);
915+
}
916+
864917
#define BLKDEV_FALLOC_FL_SUPPORTED \
865918
(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \
866919
FALLOC_FL_ZERO_RANGE | FALLOC_FL_WRITE_ZEROES)
@@ -967,6 +1020,7 @@ const struct file_operations def_blk_fops = {
9671020
.fallocate = blkdev_fallocate,
9681021
.uring_cmd = blkdev_uring_cmd,
9691022
.fop_flags = FOP_BUFFER_RASYNC,
1023+
.copy_file_range = blkdev_copy_file_range,
9701024
};
9711025

9721026
static __init int blkdev_init(void)

0 commit comments

Comments
 (0)