vfio/ccw: Add length to DMA_UNMAP checks
authorEric Farman <farman@linux.ibm.com>
Thu, 28 Jul 2022 20:49:12 +0000 (22:49 +0200)
committerAlex Williamson <alex.williamson@redhat.com>
Mon, 1 Aug 2022 19:36:46 +0000 (13:36 -0600)
As pointed out with the simplification of the
VFIO_IOMMU_NOTIFY_DMA_UNMAP notifier [1], the length
parameter was never used to check against the pinned
pages.

Let's correct that, and see if a page is within the
affected range instead of simply the first page of
the range.

[1] https://lore.kernel.org/kvm/20220720170457.39cda0d0.alex.williamson@redhat.com/

Signed-off-by: Eric Farman <farman@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/20220728204914.2420989-2-farman@linux.ibm.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/s390/cio/vfio_ccw_cp.c
drivers/s390/cio/vfio_ccw_cp.h
drivers/s390/cio/vfio_ccw_ops.c

index 8963f452f963c97628250448b40627c0dd04f74f..7b02e97f4b2914761229bc179696899a5f277ea0 100644 (file)
@@ -170,13 +170,18 @@ static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vde
        kfree(pa->pa_iova);
 }
 
-static bool page_array_iova_pinned(struct page_array *pa, unsigned long iova)
+static bool page_array_iova_pinned(struct page_array *pa, u64 iova, u64 length)
 {
+       u64 iova_pfn_start = iova >> PAGE_SHIFT;
+       u64 iova_pfn_end = (iova + length - 1) >> PAGE_SHIFT;
+       u64 pfn;
        int i;
 
-       for (i = 0; i < pa->pa_nr; i++)
-               if (pa->pa_iova[i] == iova)
+       for (i = 0; i < pa->pa_nr; i++) {
+               pfn = pa->pa_iova[i] >> PAGE_SHIFT;
+               if (pfn >= iova_pfn_start && pfn <= iova_pfn_end)
                        return true;
+       }
 
        return false;
 }
@@ -899,11 +904,12 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
  * cp_iova_pinned() - check if an iova is pinned for a ccw chain.
  * @cp: channel_program on which to perform the operation
  * @iova: the iova to check
+ * @length: the length to check from @iova
  *
  * If the @iova is currently pinned for the ccw chain, return true;
  * else return false.
  */
-bool cp_iova_pinned(struct channel_program *cp, u64 iova)
+bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length)
 {
        struct ccwchain *chain;
        int i;
@@ -913,7 +919,7 @@ bool cp_iova_pinned(struct channel_program *cp, u64 iova)
 
        list_for_each_entry(chain, &cp->ccwchain_list, next) {
                for (i = 0; i < chain->ch_len; i++)
-                       if (page_array_iova_pinned(chain->ch_pa + i, iova))
+                       if (page_array_iova_pinned(chain->ch_pa + i, iova, length))
                                return true;
        }
 
index 3194d887e08e6cf2f85906ff43f15912debdb868..54d26e242533409c275843c5eb79c1603b8d5d17 100644 (file)
@@ -46,6 +46,6 @@ void cp_free(struct channel_program *cp);
 int cp_prefetch(struct channel_program *cp);
 union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm);
 void cp_update_scsw(struct channel_program *cp, union scsw *scsw);
-bool cp_iova_pinned(struct channel_program *cp, u64 iova);
+bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length);
 
 #endif
index 0047fd88f93858dc1ced8c0108d892e29701ba86..3f67fa103c7fd344b67b9481bf1fd11a72009c48 100644 (file)
@@ -39,7 +39,7 @@ static void vfio_ccw_dma_unmap(struct vfio_device *vdev, u64 iova, u64 length)
                container_of(vdev, struct vfio_ccw_private, vdev);
 
        /* Drivers MUST unpin pages in response to an invalidation. */
-       if (!cp_iova_pinned(&private->cp, iova))
+       if (!cp_iova_pinned(&private->cp, iova, length))
                return;
 
        vfio_ccw_mdev_reset(private);