Skip to content

Commit e6d41ee

Browse files
Xu Yilunjgunthorpe
authored andcommitted
iommufd: Add iommufd_object_tombstone_user() helper
Add the iommufd_object_tombstone_user() helper, which allows the caller to destroy an iommufd object created by userspace. This is useful on some destroy paths when the kernel caller finds the object should have been removed by userspace but is still alive. With this helper, the caller destroys the object but leave the object ID reserved (so called tombstone). The tombstone prevents repurposing the object ID without awareness of the original user. Since this happens for abnormal userspace behavior, for simplicity, the tombstoned object ID would be permanently leaked until iommufd_fops_release(). I.e. the original user gets an error when calling ioctl(IOMMU_DESTROY) on that ID. The first use case would be to ensure the iommufd_vdevice can't outlive the associated iommufd_device. Link: https://patch.msgid.link/r/[email protected] Suggested-by: Jason Gunthorpe <[email protected]> Reviewed-by: Lu Baolu <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Co-developed-by: "Aneesh Kumar K.V (Arm)" <[email protected]> Signed-off-by: "Aneesh Kumar K.V (Arm)" <[email protected]> Tested-by: Nicolin Chen <[email protected]> Signed-off-by: Xu Yilun <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent a64bae6 commit e6d41ee

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ void iommufd_object_finalize(struct iommufd_ctx *ictx,
202202
struct iommufd_object *obj);
203203

204204
enum {
205-
REMOVE_WAIT_SHORTTERM = 1,
205+
REMOVE_WAIT_SHORTTERM = BIT(0),
206+
REMOVE_OBJ_TOMBSTONE = BIT(1),
206207
};
207208
int iommufd_object_remove(struct iommufd_ctx *ictx,
208209
struct iommufd_object *to_destroy, u32 id,
@@ -228,6 +229,26 @@ static inline void iommufd_object_destroy_user(struct iommufd_ctx *ictx,
228229
WARN_ON(ret);
229230
}
230231

232+
/*
233+
* Similar to iommufd_object_destroy_user(), except that the object ID is left
234+
* reserved/tombstoned.
235+
*/
236+
static inline void iommufd_object_tombstone_user(struct iommufd_ctx *ictx,
237+
struct iommufd_object *obj)
238+
{
239+
int ret;
240+
241+
ret = iommufd_object_remove(ictx, obj, obj->id,
242+
REMOVE_WAIT_SHORTTERM | REMOVE_OBJ_TOMBSTONE);
243+
244+
/*
245+
* If there is a bug and we couldn't destroy the object then we did put
246+
* back the caller's users refcount and will eventually try to free it
247+
* again during close.
248+
*/
249+
WARN_ON(ret);
250+
}
251+
231252
/*
232253
* The HWPT allocated by autodomains is used in possibly many devices and
233254
* is automatically destroyed when its refcount reaches zero.

drivers/iommu/iommufd/main.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ int iommufd_object_remove(struct iommufd_ctx *ictx,
225225
goto err_xa;
226226
}
227227

228-
xas_store(&xas, NULL);
228+
xas_store(&xas, (flags & REMOVE_OBJ_TOMBSTONE) ? XA_ZERO_ENTRY : NULL);
229229
if (ictx->vfio_ioas == container_of(obj, struct iommufd_ioas, obj))
230230
ictx->vfio_ioas = NULL;
231231
xa_unlock(&ictx->objects);
@@ -311,19 +311,41 @@ static int iommufd_fops_release(struct inode *inode, struct file *filp)
311311
while (!xa_empty(&ictx->objects)) {
312312
unsigned int destroyed = 0;
313313
unsigned long index;
314+
bool empty = true;
314315

316+
/*
317+
* We can't use xa_empty() to end the loop as the tombstones
318+
* are stored as XA_ZERO_ENTRY in the xarray. However
319+
* xa_for_each() automatically converts them to NULL and skips
320+
* them causing xa_empty() to be kept false. Thus once
321+
* xa_for_each() finds no further !NULL entries the loop is
322+
* done.
323+
*/
315324
xa_for_each(&ictx->objects, index, obj) {
325+
empty = false;
316326
if (!refcount_dec_if_one(&obj->users))
317327
continue;
328+
318329
destroyed++;
319330
xa_erase(&ictx->objects, index);
320331
iommufd_object_ops[obj->type].destroy(obj);
321332
kfree(obj);
322333
}
334+
335+
if (empty)
336+
break;
337+
323338
/* Bug related to users refcount */
324339
if (WARN_ON(!destroyed))
325340
break;
326341
}
342+
343+
/*
344+
* There may be some tombstones left over from
345+
* iommufd_object_tombstone_user()
346+
*/
347+
xa_destroy(&ictx->objects);
348+
327349
WARN_ON(!xa_empty(&ictx->groups));
328350

329351
mutex_destroy(&ictx->sw_msi_lock);

0 commit comments

Comments
 (0)