mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-02 15:18:19 +00:00
vfio/ccw: Add length to DMA_UNMAP checks
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>
This commit is contained in:
parent
34a255e676
commit
5a4fe7c41b
3 changed files with 13 additions and 7 deletions
|
@ -170,13 +170,18 @@ static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vde
|
||||||
kfree(pa->pa_iova);
|
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;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < pa->pa_nr; i++)
|
for (i = 0; i < pa->pa_nr; i++) {
|
||||||
if (pa->pa_iova[i] == iova)
|
pfn = pa->pa_iova[i] >> PAGE_SHIFT;
|
||||||
|
if (pfn >= iova_pfn_start && pfn <= iova_pfn_end)
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
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_iova_pinned() - check if an iova is pinned for a ccw chain.
|
||||||
* @cp: channel_program on which to perform the operation
|
* @cp: channel_program on which to perform the operation
|
||||||
* @iova: the iova to check
|
* @iova: the iova to check
|
||||||
|
* @length: the length to check from @iova
|
||||||
*
|
*
|
||||||
* If the @iova is currently pinned for the ccw chain, return true;
|
* If the @iova is currently pinned for the ccw chain, return true;
|
||||||
* else return false.
|
* 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;
|
struct ccwchain *chain;
|
||||||
int i;
|
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) {
|
list_for_each_entry(chain, &cp->ccwchain_list, next) {
|
||||||
for (i = 0; i < chain->ch_len; i++)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,6 @@ void cp_free(struct channel_program *cp);
|
||||||
int cp_prefetch(struct channel_program *cp);
|
int cp_prefetch(struct channel_program *cp);
|
||||||
union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm);
|
union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm);
|
||||||
void cp_update_scsw(struct channel_program *cp, union scsw *scsw);
|
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
|
#endif
|
||||||
|
|
|
@ -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);
|
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||||
|
|
||||||
/* Drivers MUST unpin pages in response to an invalidation. */
|
/* 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;
|
return;
|
||||||
|
|
||||||
vfio_ccw_mdev_reset(private);
|
vfio_ccw_mdev_reset(private);
|
||||||
|
|
Loading…
Reference in a new issue