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: Stefano Garzarella <sgarzare@redhat.com> Message-Id: <20220624075656.13997-1-sgarzare@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
96ef18a24b
commit
309bba39c9
|
@ -1095,7 +1095,8 @@ EXPORT_SYMBOL(vringh_need_notify_kern);
|
||||||
#if IS_REACHABLE(CONFIG_VHOST_IOTLB)
|
#if IS_REACHABLE(CONFIG_VHOST_IOTLB)
|
||||||
|
|
||||||
static int iotlb_translate(const struct vringh *vrh,
|
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)
|
int iov_size, u32 perm)
|
||||||
{
|
{
|
||||||
struct vhost_iotlb_map *map;
|
struct vhost_iotlb_map *map;
|
||||||
|
@ -1136,43 +1137,76 @@ static int iotlb_translate(const struct vringh *vrh,
|
||||||
|
|
||||||
spin_unlock(vrh->iotlb_lock);
|
spin_unlock(vrh->iotlb_lock);
|
||||||
|
|
||||||
|
if (translated)
|
||||||
|
*translated = min(len, s);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int copy_from_iotlb(const struct vringh *vrh, void *dst,
|
static inline int copy_from_iotlb(const struct vringh *vrh, void *dst,
|
||||||
void *src, size_t len)
|
void *src, size_t len)
|
||||||
{
|
{
|
||||||
struct iov_iter iter;
|
u64 total_translated = 0;
|
||||||
struct bio_vec iov[16];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = iotlb_translate(vrh, (u64)(uintptr_t)src,
|
while (total_translated < len) {
|
||||||
len, iov, 16, VHOST_MAP_RO);
|
struct bio_vec iov[16];
|
||||||
if (ret < 0)
|
struct iov_iter iter;
|
||||||
return ret;
|
u64 translated;
|
||||||
|
int ret;
|
||||||
|
|
||||||
iov_iter_bvec(&iter, READ, iov, ret, len);
|
ret = iotlb_translate(vrh, (u64)(uintptr_t)src,
|
||||||
|
len - total_translated, &translated,
|
||||||
|
iov, ARRAY_SIZE(iov), VHOST_MAP_RO);
|
||||||
|
if (ret == -ENOBUFS)
|
||||||
|
ret = ARRAY_SIZE(iov);
|
||||||
|
else if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = copy_from_iter(dst, len, &iter);
|
iov_iter_bvec(&iter, READ, iov, ret, translated);
|
||||||
|
|
||||||
return ret;
|
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,
|
static inline int copy_to_iotlb(const struct vringh *vrh, void *dst,
|
||||||
void *src, size_t len)
|
void *src, size_t len)
|
||||||
{
|
{
|
||||||
struct iov_iter iter;
|
u64 total_translated = 0;
|
||||||
struct bio_vec iov[16];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = iotlb_translate(vrh, (u64)(uintptr_t)dst,
|
while (total_translated < len) {
|
||||||
len, iov, 16, VHOST_MAP_WO);
|
struct bio_vec iov[16];
|
||||||
if (ret < 0)
|
struct iov_iter iter;
|
||||||
return ret;
|
u64 translated;
|
||||||
|
int ret;
|
||||||
|
|
||||||
iov_iter_bvec(&iter, WRITE, iov, ret, len);
|
ret = iotlb_translate(vrh, (u64)(uintptr_t)dst,
|
||||||
|
len - total_translated, &translated,
|
||||||
|
iov, ARRAY_SIZE(iov), VHOST_MAP_WO);
|
||||||
|
if (ret == -ENOBUFS)
|
||||||
|
ret = ARRAY_SIZE(iov);
|
||||||
|
else if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return copy_to_iter(src, len, &iter);
|
iov_iter_bvec(&iter, WRITE, iov, ret, translated);
|
||||||
|
|
||||||
|
ret = copy_to_iter(src, translated, &iter);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
src += translated;
|
||||||
|
dst += translated;
|
||||||
|
total_translated += translated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_translated;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int getu16_iotlb(const struct vringh *vrh,
|
static inline int getu16_iotlb(const struct vringh *vrh,
|
||||||
|
@ -1183,7 +1217,7 @@ static inline int getu16_iotlb(const struct vringh *vrh,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Atomic read is needed for getu16 */
|
/* 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);
|
&iov, 1, VHOST_MAP_RO);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1204,7 +1238,7 @@ static inline int putu16_iotlb(const struct vringh *vrh,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Atomic write is needed for putu16 */
|
/* 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);
|
&iov, 1, VHOST_MAP_WO);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue