|
19 | 19 | #include <linux/iomap.h> |
20 | 20 | #include <linux/module.h> |
21 | 21 | #include <linux/io_uring/cmd.h> |
| 22 | +#include <linux/splice.h> |
22 | 23 | #include "blk.h" |
23 | 24 |
|
24 | 25 | 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) |
861 | 862 | return ret; |
862 | 863 | } |
863 | 864 |
|
| 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(¶ms) == 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 | + |
864 | 917 | #define BLKDEV_FALLOC_FL_SUPPORTED \ |
865 | 918 | (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \ |
866 | 919 | FALLOC_FL_ZERO_RANGE | FALLOC_FL_WRITE_ZEROES) |
@@ -967,6 +1020,7 @@ const struct file_operations def_blk_fops = { |
967 | 1020 | .fallocate = blkdev_fallocate, |
968 | 1021 | .uring_cmd = blkdev_uring_cmd, |
969 | 1022 | .fop_flags = FOP_BUFFER_RASYNC, |
| 1023 | + .copy_file_range = blkdev_copy_file_range, |
970 | 1024 | }; |
971 | 1025 |
|
972 | 1026 | static __init int blkdev_init(void) |
|
0 commit comments