Use netfs_extract_iter_to_sg() to build a scatterlist from an iterator. Note that if this fits, netfs_extract_iter_to_sg() should move to core code. Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: James E.J. Bottomley <jejb@xxxxxxxxxxxxx> cc: Martin K. Petersen <martin.petersen@xxxxxxxxxx> cc: Christoph Hellwig <hch@xxxxxx> cc: linux-scsi@xxxxxxxxxxxxxxx --- drivers/vhost/scsi.c | 78 +++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 5d10837d19ec..af897cc4036d 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -34,6 +34,7 @@ #include <linux/virtio_scsi.h> #include <linux/llist.h> #include <linux/bitmap.h> +#include <linux/netfs.h> #include "vhost.h" @@ -75,6 +76,9 @@ struct vhost_scsi_cmd { u32 tvc_prot_sgl_count; /* Saved unpacked SCSI LUN for vhost_scsi_target_queue_cmd() */ u32 tvc_lun; + /* Cleanup modes for scatterlists */ + unsigned int tvc_cleanup_mode; + unsigned int tvc_prot_cleanup_mode; /* Pointer to the SGL formatted memory from virtio-scsi */ struct scatterlist *tvc_sgl; struct scatterlist *tvc_prot_sgl; @@ -339,11 +343,13 @@ static void vhost_scsi_release_cmd_res(struct se_cmd *se_cmd) if (tv_cmd->tvc_sgl_count) { for (i = 0; i < tv_cmd->tvc_sgl_count; i++) - put_page(sg_page(&tv_cmd->tvc_sgl[i])); + page_put_unpin(sg_page(&tv_cmd->tvc_sgl[i]), + tv_cmd->tvc_cleanup_mode); } if (tv_cmd->tvc_prot_sgl_count) { for (i = 0; i < tv_cmd->tvc_prot_sgl_count; i++) - put_page(sg_page(&tv_cmd->tvc_prot_sgl[i])); + page_put_unpin(sg_page(&tv_cmd->tvc_prot_sgl[i]), + tv_cmd->tvc_prot_cleanup_mode); } sbitmap_clear_bit(&svq->scsi_tags, se_cmd->map_tag); @@ -631,41 +637,6 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, struct vhost_scsi_tpg *tpg, return cmd; } -/* - * Map a user memory range into a scatterlist - * - * Returns the number of scatterlist entries used or -errno on error. - */ -static int -vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd, - struct iov_iter *iter, - struct scatterlist *sgl, - bool write) -{ - struct page **pages = cmd->tvc_upages; - struct scatterlist *sg = sgl; - ssize_t bytes; - size_t offset; - unsigned int npages = 0, gup_flags = 0; - - gup_flags |= write ? FOLL_SOURCE_BUF : FOLL_DEST_BUF; - - bytes = iov_iter_get_pages(iter, pages, LONG_MAX, - VHOST_SCSI_PREALLOC_UPAGES, &offset, - gup_flags); - /* No pages were pinned */ - if (bytes <= 0) - return bytes < 0 ? bytes : -EFAULT; - - while (bytes) { - unsigned n = min_t(unsigned, PAGE_SIZE - offset, bytes); - sg_set_page(sg++, pages[npages++], n, offset); - bytes -= n; - offset = 0; - } - return npages; -} - static int vhost_scsi_calc_sgls(struct iov_iter *iter, size_t bytes, int max_sgls) { @@ -689,24 +660,19 @@ vhost_scsi_calc_sgls(struct iov_iter *iter, size_t bytes, int max_sgls) static int vhost_scsi_iov_to_sgl(struct vhost_scsi_cmd *cmd, bool write, struct iov_iter *iter, - struct scatterlist *sg, int sg_count) + struct scatterlist *sg, int sg_count, + unsigned int *cleanup_mode) { - struct scatterlist *p = sg; - int ret; + struct sg_table sgt = { .sgl = sg }; + unsigned int gup_flags = write ? FOLL_SOURCE_BUF : FOLL_DEST_BUF; + ssize_t ret; - while (iov_iter_count(iter)) { - ret = vhost_scsi_map_to_sgl(cmd, iter, sg, write); - if (ret < 0) { - while (p < sg) { - struct page *page = sg_page(p++); - if (page) - put_page(page); - } - return ret; - } - sg += ret; - } - return 0; + ret = netfs_extract_iter_to_sg(iter, LONG_MAX, &sgt, sg_count, gup_flags); + if (ret > 0) + sg_mark_end(sg + sgt.nents - 1); + + *cleanup_mode = iov_iter_extract_mode(iter, gup_flags); + return ret; } static int @@ -730,7 +696,8 @@ vhost_scsi_mapal(struct vhost_scsi_cmd *cmd, ret = vhost_scsi_iov_to_sgl(cmd, write, prot_iter, cmd->tvc_prot_sgl, - cmd->tvc_prot_sgl_count); + cmd->tvc_prot_sgl_count, + &cmd->tvc_prot_cleanup_mode); if (ret < 0) { cmd->tvc_prot_sgl_count = 0; return ret; @@ -747,7 +714,8 @@ vhost_scsi_mapal(struct vhost_scsi_cmd *cmd, cmd->tvc_sgl, cmd->tvc_sgl_count); ret = vhost_scsi_iov_to_sgl(cmd, write, data_iter, - cmd->tvc_sgl, cmd->tvc_sgl_count); + cmd->tvc_sgl, cmd->tvc_sgl_count, + &cmd->tvc_cleanup_mode); if (ret < 0) { cmd->tvc_sgl_count = 0; return ret;