|
11 | 11 | #include <linux/blktrace_api.h>
|
12 | 12 | #include <linux/pr.h>
|
13 | 13 | #include <linux/uaccess.h>
|
| 14 | +#include <linux/pagemap.h> |
| 15 | +#include <linux/io_uring/cmd.h> |
| 16 | +#include <uapi/linux/blkdev.h> |
14 | 17 | #include "blk.h"
|
15 | 18 |
|
16 | 19 | static int blkpg_do_ioctl(struct block_device *bdev,
|
@@ -748,3 +751,112 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
748 | 751 | return ret;
|
749 | 752 | }
|
750 | 753 | #endif
|
| 754 | + |
| 755 | +struct blk_iou_cmd { |
| 756 | + int res; |
| 757 | + bool nowait; |
| 758 | +}; |
| 759 | + |
| 760 | +static void blk_cmd_complete(struct io_uring_cmd *cmd, unsigned int issue_flags) |
| 761 | +{ |
| 762 | + struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd); |
| 763 | + |
| 764 | + if (bic->res == -EAGAIN && bic->nowait) |
| 765 | + io_uring_cmd_issue_blocking(cmd); |
| 766 | + else |
| 767 | + io_uring_cmd_done(cmd, bic->res, 0, issue_flags); |
| 768 | +} |
| 769 | + |
| 770 | +static void bio_cmd_bio_end_io(struct bio *bio) |
| 771 | +{ |
| 772 | + struct io_uring_cmd *cmd = bio->bi_private; |
| 773 | + struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd); |
| 774 | + |
| 775 | + if (unlikely(bio->bi_status) && !bic->res) |
| 776 | + bic->res = blk_status_to_errno(bio->bi_status); |
| 777 | + |
| 778 | + io_uring_cmd_do_in_task_lazy(cmd, blk_cmd_complete); |
| 779 | + bio_put(bio); |
| 780 | +} |
| 781 | + |
| 782 | +static int blkdev_cmd_discard(struct io_uring_cmd *cmd, |
| 783 | + struct block_device *bdev, |
| 784 | + uint64_t start, uint64_t len, bool nowait) |
| 785 | +{ |
| 786 | + struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd); |
| 787 | + gfp_t gfp = nowait ? GFP_NOWAIT : GFP_KERNEL; |
| 788 | + sector_t sector = start >> SECTOR_SHIFT; |
| 789 | + sector_t nr_sects = len >> SECTOR_SHIFT; |
| 790 | + struct bio *prev = NULL, *bio; |
| 791 | + int err; |
| 792 | + |
| 793 | + if (!bdev_max_discard_sectors(bdev)) |
| 794 | + return -EOPNOTSUPP; |
| 795 | + if (!(file_to_blk_mode(cmd->file) & BLK_OPEN_WRITE)) |
| 796 | + return -EBADF; |
| 797 | + if (bdev_read_only(bdev)) |
| 798 | + return -EPERM; |
| 799 | + err = blk_validate_byte_range(bdev, start, len); |
| 800 | + if (err) |
| 801 | + return err; |
| 802 | + |
| 803 | + err = filemap_invalidate_pages(bdev->bd_mapping, start, |
| 804 | + start + len - 1, nowait); |
| 805 | + if (err) |
| 806 | + return err; |
| 807 | + |
| 808 | + while (true) { |
| 809 | + bio = blk_alloc_discard_bio(bdev, §or, &nr_sects, gfp); |
| 810 | + if (!bio) |
| 811 | + break; |
| 812 | + if (nowait) { |
| 813 | + /* |
| 814 | + * Don't allow multi-bio non-blocking submissions as |
| 815 | + * subsequent bios may fail but we won't get a direct |
| 816 | + * indication of that. Normally, the caller should |
| 817 | + * retry from a blocking context. |
| 818 | + */ |
| 819 | + if (unlikely(nr_sects)) { |
| 820 | + bio_put(bio); |
| 821 | + return -EAGAIN; |
| 822 | + } |
| 823 | + bio->bi_opf |= REQ_NOWAIT; |
| 824 | + } |
| 825 | + |
| 826 | + prev = bio_chain_and_submit(prev, bio); |
| 827 | + } |
| 828 | + if (unlikely(!prev)) |
| 829 | + return -EAGAIN; |
| 830 | + if (unlikely(nr_sects)) |
| 831 | + bic->res = -EAGAIN; |
| 832 | + |
| 833 | + prev->bi_private = cmd; |
| 834 | + prev->bi_end_io = bio_cmd_bio_end_io; |
| 835 | + submit_bio(prev); |
| 836 | + return -EIOCBQUEUED; |
| 837 | +} |
| 838 | + |
| 839 | +int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) |
| 840 | +{ |
| 841 | + struct block_device *bdev = I_BDEV(cmd->file->f_mapping->host); |
| 842 | + struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd); |
| 843 | + const struct io_uring_sqe *sqe = cmd->sqe; |
| 844 | + u32 cmd_op = cmd->cmd_op; |
| 845 | + uint64_t start, len; |
| 846 | + |
| 847 | + if (unlikely(sqe->ioprio || sqe->__pad1 || sqe->len || |
| 848 | + sqe->rw_flags || sqe->file_index)) |
| 849 | + return -EINVAL; |
| 850 | + |
| 851 | + bic->res = 0; |
| 852 | + bic->nowait = issue_flags & IO_URING_F_NONBLOCK; |
| 853 | + |
| 854 | + start = READ_ONCE(sqe->addr); |
| 855 | + len = READ_ONCE(sqe->addr3); |
| 856 | + |
| 857 | + switch (cmd_op) { |
| 858 | + case BLOCK_URING_CMD_DISCARD: |
| 859 | + return blkdev_cmd_discard(cmd, bdev, start, len, bic->nowait); |
| 860 | + } |
| 861 | + return -EINVAL; |
| 862 | +} |
0 commit comments