Commit 309bba39 authored by Stefano Garzarella's avatar Stefano Garzarella Committed by Michael S. Tsirkin
Browse files

vringh: iterate on iotlb_translate to handle large translations



iotlb_translate() can return -ENOBUFS if the bio_vec is not big enough
to contain all the ranges for translation.
This can happen for example if the VMM maps a large bounce buffer,
without using hugepages, that requires more than 16 ranges to translate
the addresses.

To handle this case, let's extend iotlb_translate() to also return the
number of bytes successfully translated.
In copy_from_iotlb()/copy_to_iotlb() loops by calling iotlb_translate()
several times until we complete the translation.

Signed-off-by: default avatarStefano Garzarella <sgarzare@redhat.com>
Message-Id: <20220624075656.13997-1-sgarzare@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 96ef18a2
Loading
Loading
Loading
Loading
+56 −22
Original line number Diff line number Diff line
@@ -1095,7 +1095,8 @@ EXPORT_SYMBOL(vringh_need_notify_kern);
#if IS_REACHABLE(CONFIG_VHOST_IOTLB)

static int iotlb_translate(const struct vringh *vrh,
			   u64 addr, u64 len, struct bio_vec iov[],
			   u64 addr, u64 len, u64 *translated,
			   struct bio_vec iov[],
			   int iov_size, u32 perm)
{
	struct vhost_iotlb_map *map;
@@ -1136,43 +1137,76 @@ static int iotlb_translate(const struct vringh *vrh,

	spin_unlock(vrh->iotlb_lock);

	if (translated)
		*translated = min(len, s);

	return ret;
}

static inline int copy_from_iotlb(const struct vringh *vrh, void *dst,
				  void *src, size_t len)
{
	struct iov_iter iter;
	u64 total_translated = 0;

	while (total_translated < len) {
		struct bio_vec iov[16];
		struct iov_iter iter;
		u64 translated;
		int ret;

		ret = iotlb_translate(vrh, (u64)(uintptr_t)src,
			      len, iov, 16, VHOST_MAP_RO);
	if (ret < 0)
				      len - total_translated, &translated,
				      iov, ARRAY_SIZE(iov), VHOST_MAP_RO);
		if (ret == -ENOBUFS)
			ret = ARRAY_SIZE(iov);
		else if (ret < 0)
			return ret;

	iov_iter_bvec(&iter, READ, iov, ret, len);

	ret = copy_from_iter(dst, len, &iter);
		iov_iter_bvec(&iter, READ, iov, ret, translated);

		ret = copy_from_iter(dst, translated, &iter);
		if (ret < 0)
			return ret;

		src += translated;
		dst += translated;
		total_translated += translated;
	}

	return total_translated;
}

static inline int copy_to_iotlb(const struct vringh *vrh, void *dst,
				void *src, size_t len)
{
	struct iov_iter iter;
	u64 total_translated = 0;

	while (total_translated < len) {
		struct bio_vec iov[16];
		struct iov_iter iter;
		u64 translated;
		int ret;

		ret = iotlb_translate(vrh, (u64)(uintptr_t)dst,
			      len, iov, 16, VHOST_MAP_WO);
				      len - total_translated, &translated,
				      iov, ARRAY_SIZE(iov), VHOST_MAP_WO);
		if (ret == -ENOBUFS)
			ret = ARRAY_SIZE(iov);
		else if (ret < 0)
			return ret;

		iov_iter_bvec(&iter, WRITE, iov, ret, translated);

		ret = copy_to_iter(src, translated, &iter);
		if (ret < 0)
			return ret;

	iov_iter_bvec(&iter, WRITE, iov, ret, len);
		src += translated;
		dst += translated;
		total_translated += translated;
	}

	return copy_to_iter(src, len, &iter);
	return total_translated;
}

static inline int getu16_iotlb(const struct vringh *vrh,
@@ -1183,7 +1217,7 @@ static inline int getu16_iotlb(const struct vringh *vrh,
	int ret;

	/* Atomic read is needed for getu16 */
	ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p),
	ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), NULL,
			      &iov, 1, VHOST_MAP_RO);
	if (ret < 0)
		return ret;
@@ -1204,7 +1238,7 @@ static inline int putu16_iotlb(const struct vringh *vrh,
	int ret;

	/* Atomic write is needed for putu16 */
	ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p),
	ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), NULL,
			      &iov, 1, VHOST_MAP_WO);
	if (ret < 0)
		return ret;