Unverified Commit 50daeac2 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!14381 virtiofs: use pages instead of pointer for kernel direct IO

parents 436c66fb 03bc8adc
Loading
Loading
Loading
Loading
+43 −19
Original line number Diff line number Diff line
@@ -615,7 +615,7 @@ void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos,
	args->out_args[0].size = count;
}

static void fuse_release_user_pages(struct fuse_args_pages *ap,
static void fuse_release_user_pages(struct fuse_args_pages *ap, ssize_t nres,
				    bool should_dirty)
{
	unsigned int i;
@@ -625,6 +625,9 @@ static void fuse_release_user_pages(struct fuse_args_pages *ap,
			set_page_dirty_lock(ap->pages[i]);
		put_page(ap->pages[i]);
	}

	if (nres > 0 && ap->args.invalidate_vmap)
		invalidate_kernel_vmap_range(ap->args.vmap_base, nres);
}

static void fuse_io_release(struct kref *kref)
@@ -723,25 +726,29 @@ static void fuse_aio_complete_req(struct fuse_mount *fm, struct fuse_args *args,
	struct fuse_io_args *ia = container_of(args, typeof(*ia), ap.args);
	struct fuse_io_priv *io = ia->io;
	ssize_t pos = -1;

	fuse_release_user_pages(&ia->ap, io->should_dirty);
	size_t nres;

	if (err) {
		/* Nothing */
	} else if (io->write) {
		if (ia->write.out.size > ia->write.in.size) {
			err = -EIO;
		} else if (ia->write.in.size != ia->write.out.size) {
		} else {
			nres = ia->write.out.size;
			if (ia->write.in.size != ia->write.out.size)
				pos = ia->write.in.offset - io->offset +
				      ia->write.out.size;
		}
	} else {
		u32 outsize = args->out_args[0].size;

		nres = outsize;
		if (ia->read.in.size != outsize)
			pos = ia->read.in.offset - io->offset + outsize;
	}

	fuse_release_user_pages(&ia->ap, err ?: nres, io->should_dirty);

	fuse_aio_complete(io, err, pos);
	fuse_io_free(ia);
}
@@ -1394,26 +1401,39 @@ static inline size_t fuse_get_frag_size(const struct iov_iter *ii,

static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
			       size_t *nbytesp, int write,
			       unsigned int max_pages)
			       unsigned int max_pages,
			       bool use_pages_for_kvec_io)
{
	bool flush_or_invalidate = false;
	size_t nbytes = 0;  /* # bytes already packed in req */
	ssize_t ret = 0;

	/* Special case for kernel I/O: can copy directly into the buffer */
	/* Special case for kernel I/O: can copy directly into the buffer.
	 * However if the implementation of fuse_conn requires pages instead of
	 * pointer (e.g., virtio-fs), use iov_iter_extract_pages() instead.
	 */
	if (iov_iter_is_kvec(ii)) {
		unsigned long user_addr = fuse_get_user_addr(ii);
		void *user_addr = (void *)fuse_get_user_addr(ii);

		if (!use_pages_for_kvec_io) {
			size_t frag_size = fuse_get_frag_size(ii, *nbytesp);

			if (write)
			ap->args.in_args[1].value = (void *) user_addr;
				ap->args.in_args[1].value = user_addr;
			else
			ap->args.out_args[0].value = (void *) user_addr;
				ap->args.out_args[0].value = user_addr;

			iov_iter_advance(ii, frag_size);
			*nbytesp = frag_size;
			return 0;
		}

		if (is_vmalloc_addr(user_addr)) {
			ap->args.vmap_base = user_addr;
			flush_or_invalidate = true;
		}
	}

	while (nbytes < *nbytesp && ap->num_pages < max_pages) {
		unsigned npages;
		size_t start;
@@ -1438,6 +1458,10 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
			(PAGE_SIZE - ret) & (PAGE_SIZE - 1);
	}

	if (write && flush_or_invalidate)
		flush_kernel_vmap_range(ap->args.vmap_base, nbytes);

	ap->args.invalidate_vmap = !write && flush_or_invalidate;
	ap->args.user_pages = true;
	if (write)
		ap->args.in_pages = true;
@@ -1489,7 +1513,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
		size_t nbytes = min(count, nmax);

		err = fuse_get_user_pages(&ia->ap, iter, &nbytes, write,
					  max_pages);
					  max_pages, fc->use_pages_for_kvec_io);
		if (err && !nbytes)
			break;

@@ -1503,7 +1527,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
		}

		if (!io->async || nres < 0) {
			fuse_release_user_pages(&ia->ap, io->should_dirty);
			fuse_release_user_pages(&ia->ap, nres, io->should_dirty);
			fuse_io_free(ia);
		}
		ia = NULL;
+6 −0
Original line number Diff line number Diff line
@@ -273,9 +273,12 @@ struct fuse_args {
#ifndef __GENKSYMS__
	bool user_pages:1;
#endif
	bool invalidate_vmap:1;
	struct fuse_in_arg in_args[3];
	struct fuse_arg out_args[2];
	void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
	/* Used for kvec iter backed by vmalloc address */
	void *vmap_base;
};

struct fuse_args_pages {
@@ -766,6 +769,9 @@ struct fuse_conn {
	/* Auto-mount submounts announced by the server */
	unsigned int auto_submounts:1;

	/* Use pages instead of pointer for kernel I/O */
	unsigned int use_pages_for_kvec_io:1;

	/** The number of requests waiting for completion */
	atomic_t num_waiting;

+1 −0
Original line number Diff line number Diff line
@@ -1464,6 +1464,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
	fc->release = fuse_free_conn;
	fc->delete_stale = true;
	fc->auto_submounts = true;
	fc->use_pages_for_kvec_io = true;

	/* Tell FUSE to split requests that exceed the virtqueue's size */
	fc->max_pages_limit = min_t(unsigned int, fc->max_pages_limit,