Skip to content

Commit e23a621

Browse files
nicolincawilliam
authored andcommitted
iommufd/device: Add iommufd_access_detach() API
Previously, the detach routine is only done by the destroy(). And it was called by vfio_iommufd_emulated_unbind() when the device runs close(), so all the mappings in iopt were cleaned in that setup, when the call trace reaches this detach() routine. Now, there's a need of a detach uAPI, meaning that it does not only need a new iommufd_access_detach() API, but also requires access->ops->unmap() call as a cleanup. So add one. However, leaving that unprotected can introduce some potential of a race condition during the pin_/unpin_pages() call, where access->ioas->iopt is getting referenced. So, add an ioas_lock to protect the context of iopt referencings. Also, to allow the iommufd_access_unpin_pages() callback to happen via this unmap() call, add an ioas_unpin pointer, so the unpin routine won't be affected by the "access->ioas = NULL" trick. Reviewed-by: Kevin Tian <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Tested-by: Terrence Xu <[email protected]> Tested-by: Nicolin Chen <[email protected]> Tested-by: Matthew Rosato <[email protected]> Tested-by: Yanting Jiang <[email protected]> Tested-by: Shameer Kolothum <[email protected]> Tested-by: Zhenzhong Duan <[email protected]> Signed-off-by: Nicolin Chen <[email protected]> Signed-off-by: Yi Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alex Williamson <[email protected]>
1 parent 9048c73 commit e23a621

File tree

3 files changed

+72
-5
lines changed

3 files changed

+72
-5
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ iommufd_access_create(struct iommufd_ctx *ictx,
486486
iommufd_ctx_get(ictx);
487487
iommufd_object_finalize(ictx, &access->obj);
488488
*id = access->obj.id;
489+
mutex_init(&access->ioas_lock);
489490
return access;
490491
}
491492
EXPORT_SYMBOL_NS_GPL(iommufd_access_create, IOMMUFD);
@@ -505,26 +506,60 @@ void iommufd_access_destroy(struct iommufd_access *access)
505506
}
506507
EXPORT_SYMBOL_NS_GPL(iommufd_access_destroy, IOMMUFD);
507508

509+
void iommufd_access_detach(struct iommufd_access *access)
510+
{
511+
struct iommufd_ioas *cur_ioas = access->ioas;
512+
513+
mutex_lock(&access->ioas_lock);
514+
if (WARN_ON(!access->ioas))
515+
goto out;
516+
/*
517+
* Set ioas to NULL to block any further iommufd_access_pin_pages().
518+
* iommufd_access_unpin_pages() can continue using access->ioas_unpin.
519+
*/
520+
access->ioas = NULL;
521+
522+
if (access->ops->unmap) {
523+
mutex_unlock(&access->ioas_lock);
524+
access->ops->unmap(access->data, 0, ULONG_MAX);
525+
mutex_lock(&access->ioas_lock);
526+
}
527+
iopt_remove_access(&cur_ioas->iopt, access);
528+
refcount_dec(&cur_ioas->obj.users);
529+
out:
530+
access->ioas_unpin = NULL;
531+
mutex_unlock(&access->ioas_lock);
532+
}
533+
EXPORT_SYMBOL_NS_GPL(iommufd_access_detach, IOMMUFD);
534+
508535
int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id)
509536
{
510537
struct iommufd_ioas *new_ioas;
511538
int rc = 0;
512539

513-
if (access->ioas)
540+
mutex_lock(&access->ioas_lock);
541+
if (WARN_ON(access->ioas || access->ioas_unpin)) {
542+
mutex_unlock(&access->ioas_lock);
514543
return -EINVAL;
544+
}
515545

516546
new_ioas = iommufd_get_ioas(access->ictx, ioas_id);
517-
if (IS_ERR(new_ioas))
547+
if (IS_ERR(new_ioas)) {
548+
mutex_unlock(&access->ioas_lock);
518549
return PTR_ERR(new_ioas);
550+
}
519551

520552
rc = iopt_add_access(&new_ioas->iopt, access);
521553
if (rc) {
554+
mutex_unlock(&access->ioas_lock);
522555
iommufd_put_object(&new_ioas->obj);
523556
return rc;
524557
}
525558
iommufd_ref_to_users(&new_ioas->obj);
526559

527560
access->ioas = new_ioas;
561+
access->ioas_unpin = new_ioas;
562+
mutex_unlock(&access->ioas_lock);
528563
return 0;
529564
}
530565
EXPORT_SYMBOL_NS_GPL(iommufd_access_attach, IOMMUFD);
@@ -579,15 +614,26 @@ void iommufd_access_notify_unmap(struct io_pagetable *iopt, unsigned long iova,
579614
void iommufd_access_unpin_pages(struct iommufd_access *access,
580615
unsigned long iova, unsigned long length)
581616
{
582-
struct io_pagetable *iopt = &access->ioas->iopt;
583617
struct iopt_area_contig_iter iter;
618+
struct io_pagetable *iopt;
584619
unsigned long last_iova;
585620
struct iopt_area *area;
586621

587622
if (WARN_ON(!length) ||
588623
WARN_ON(check_add_overflow(iova, length - 1, &last_iova)))
589624
return;
590625

626+
mutex_lock(&access->ioas_lock);
627+
/*
628+
* The driver must be doing something wrong if it calls this before an
629+
* iommufd_access_attach() or after an iommufd_access_detach().
630+
*/
631+
if (WARN_ON(!access->ioas_unpin)) {
632+
mutex_unlock(&access->ioas_lock);
633+
return;
634+
}
635+
iopt = &access->ioas_unpin->iopt;
636+
591637
down_read(&iopt->iova_rwsem);
592638
iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova)
593639
iopt_area_remove_access(
@@ -597,6 +643,7 @@ void iommufd_access_unpin_pages(struct iommufd_access *access,
597643
min(last_iova, iopt_area_last_iova(area))));
598644
WARN_ON(!iopt_area_contig_done(&iter));
599645
up_read(&iopt->iova_rwsem);
646+
mutex_unlock(&access->ioas_lock);
600647
}
601648
EXPORT_SYMBOL_NS_GPL(iommufd_access_unpin_pages, IOMMUFD);
602649

@@ -642,8 +689,8 @@ int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
642689
unsigned long length, struct page **out_pages,
643690
unsigned int flags)
644691
{
645-
struct io_pagetable *iopt = &access->ioas->iopt;
646692
struct iopt_area_contig_iter iter;
693+
struct io_pagetable *iopt;
647694
unsigned long last_iova;
648695
struct iopt_area *area;
649696
int rc;
@@ -658,6 +705,13 @@ int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
658705
if (check_add_overflow(iova, length - 1, &last_iova))
659706
return -EOVERFLOW;
660707

708+
mutex_lock(&access->ioas_lock);
709+
if (!access->ioas) {
710+
mutex_unlock(&access->ioas_lock);
711+
return -ENOENT;
712+
}
713+
iopt = &access->ioas->iopt;
714+
661715
down_read(&iopt->iova_rwsem);
662716
iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova) {
663717
unsigned long last = min(last_iova, iopt_area_last_iova(area));
@@ -688,6 +742,7 @@ int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
688742
}
689743

690744
up_read(&iopt->iova_rwsem);
745+
mutex_unlock(&access->ioas_lock);
691746
return 0;
692747

693748
err_remove:
@@ -702,6 +757,7 @@ int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
702757
iopt_area_last_iova(area))));
703758
}
704759
up_read(&iopt->iova_rwsem);
760+
mutex_unlock(&access->ioas_lock);
705761
return rc;
706762
}
707763
EXPORT_SYMBOL_NS_GPL(iommufd_access_pin_pages, IOMMUFD);
@@ -721,8 +777,8 @@ EXPORT_SYMBOL_NS_GPL(iommufd_access_pin_pages, IOMMUFD);
721777
int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
722778
void *data, size_t length, unsigned int flags)
723779
{
724-
struct io_pagetable *iopt = &access->ioas->iopt;
725780
struct iopt_area_contig_iter iter;
781+
struct io_pagetable *iopt;
726782
struct iopt_area *area;
727783
unsigned long last_iova;
728784
int rc;
@@ -732,6 +788,13 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
732788
if (check_add_overflow(iova, length - 1, &last_iova))
733789
return -EOVERFLOW;
734790

791+
mutex_lock(&access->ioas_lock);
792+
if (!access->ioas) {
793+
mutex_unlock(&access->ioas_lock);
794+
return -ENOENT;
795+
}
796+
iopt = &access->ioas->iopt;
797+
735798
down_read(&iopt->iova_rwsem);
736799
iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova) {
737800
unsigned long last = min(last_iova, iopt_area_last_iova(area));
@@ -758,6 +821,7 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
758821
rc = -ENOENT;
759822
err_out:
760823
up_read(&iopt->iova_rwsem);
824+
mutex_unlock(&access->ioas_lock);
761825
return rc;
762826
}
763827
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ struct iommufd_access {
285285
struct iommufd_object obj;
286286
struct iommufd_ctx *ictx;
287287
struct iommufd_ioas *ioas;
288+
struct iommufd_ioas *ioas_unpin;
289+
struct mutex ioas_lock;
288290
const struct iommufd_access_ops *ops;
289291
void *data;
290292
unsigned long iova_alignment;

include/linux/iommufd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ iommufd_access_create(struct iommufd_ctx *ictx,
4848
const struct iommufd_access_ops *ops, void *data, u32 *id);
4949
void iommufd_access_destroy(struct iommufd_access *access);
5050
int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id);
51+
void iommufd_access_detach(struct iommufd_access *access);
5152

5253
void iommufd_ctx_get(struct iommufd_ctx *ictx);
5354

0 commit comments

Comments
 (0)