vringh: iterate on iotlb_translate to handle large translations
authorStefano Garzarella <sgarzare@redhat.com>
Fri, 24 Jun 2022 07:56:56 +0000 (09:56 +0200)
committerMichael S. Tsirkin <mst@redhat.com>
Thu, 11 Aug 2022 08:06:37 +0000 (04:06 -0400)
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>
drivers/vhost/vringh.c

index eab55accf381f83ad96ffaaa0998f276dca8551a..11f59dd06a74e1de5a8bc257dbe84aaf1993e39e 100644 (file)
@@ -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;
-       struct bio_vec iov[16];
-       int ret;
+       u64 total_translated = 0;
 
-       ret = iotlb_translate(vrh, (u64)(uintptr_t)src,
-                             len, iov, 16, VHOST_MAP_RO);
-       if (ret < 0)
-               return ret;
+       while (total_translated < len) {
+               struct bio_vec iov[16];
+               struct iov_iter iter;
+               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,
                                void *src, size_t len)
 {
-       struct iov_iter iter;
-       struct bio_vec iov[16];
-       int ret;
+       u64 total_translated = 0;
 
-       ret = iotlb_translate(vrh, (u64)(uintptr_t)dst,
-                             len, iov, 16, VHOST_MAP_WO);
-       if (ret < 0)
-               return ret;
+       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 - 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, len);
+               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 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;