obj = kzalloc(size, GFP_KERNEL_ACCOUNT); if (!obj) return ERR_PTR(-ENOMEM);
obj->type = type; /* Starts out bias'd by 1 until it is removed from the xarray */
refcount_set(&obj->wait_cnt, 1);
refcount_set(&obj->users, 1);
/* * Reserve an ID in the xarray but do not publish the pointer yet since * the caller hasn't initialized it yet. Once the pointer is published * in the xarray and visible to other threads we can't reliably destroy * it anymore, so the caller must complete all errorable operations * before calling iommufd_object_finalize().
*/
rc = xa_alloc(&ictx->objects, &obj->id, XA_ZERO_ENTRY, xa_limit_31b,
GFP_KERNEL_ACCOUNT); if (rc) goto out_free; return obj;
out_free:
kfree(obj); return ERR_PTR(rc);
}
/* Something is coded wrong if this is hit */ if (WARN_ON(ucmd->new_obj)) return ERR_PTR(-EBUSY);
/* * An abort op means that its caller needs to invoke it within a lock in * the caller. So it doesn't work with _iommufd_object_alloc_ucmd() that * will invoke the abort op in iommufd_object_abort_and_destroy(), which * must be outside the caller's lock.
*/ if (WARN_ON(iommufd_object_ops[type].abort)) return ERR_PTR(-EOPNOTSUPP);
new_obj = _iommufd_object_alloc(ucmd->ictx, size, type); if (IS_ERR(new_obj)) return new_obj;
ucmd->new_obj = new_obj; return new_obj;
}
/* * Allow concurrent access to the object. * * Once another thread can see the object pointer it can prevent object * destruction. Expect for special kernel-only objects there is no in-kernel way * to reliably destroy a single object. Thus all APIs that are creating objects * must use iommufd_object_abort() to handle their errors and only call * iommufd_object_finalize() once object creation cannot fail.
*/ void iommufd_object_finalize(struct iommufd_ctx *ictx, struct iommufd_object *obj)
{
XA_STATE(xas, &ictx->objects, obj->id); void *old;
xa_lock(&ictx->objects);
old = xas_store(&xas, obj);
xa_unlock(&ictx->objects); /* obj->id was returned from xa_alloc() so the xas_store() cannot fail */
WARN_ON(old != XA_ZERO_ENTRY);
}
/* Undo _iommufd_object_alloc() if iommufd_object_finalize() was not called */ void iommufd_object_abort(struct iommufd_ctx *ictx, struct iommufd_object *obj)
{
XA_STATE(xas, &ictx->objects, obj->id); void *old;
xa_lock(&ictx->objects);
old = xas_store(&xas, NULL);
xa_unlock(&ictx->objects);
WARN_ON(old != XA_ZERO_ENTRY);
if (WARN_ON(!refcount_dec_and_test(&obj->users))) return;
kfree(obj);
}
/* * Abort an object that has been fully initialized and needs destroy, but has * not been finalized.
*/ void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx, struct iommufd_object *obj)
{ conststruct iommufd_object_ops *ops = &iommufd_object_ops[obj->type];
/* * A file should hold a users refcount while the file is open * and put it back in its release. The file should hold a * pointer to obj in their private data. Normal fput() is * deferred to a workqueue and can get out of order with the * following kfree(obj). Using the sync version ensures the * release happens immediately. During abort we require the file * refcount is one at this point - meaning the object alloc * function cannot do anything to allow another thread to take a * refcount prior to a guaranteed success.
*/ if (*filep)
__fput_sync(*filep);
}
if (ops->abort)
ops->abort(obj); else
ops->destroy(obj);
iommufd_object_abort(ictx, obj);
}
if (iommufd_object_ops[to_destroy->type].pre_destroy)
iommufd_object_ops[to_destroy->type].pre_destroy(to_destroy);
if (wait_event_timeout(ictx->destroy_wait,
refcount_read(&to_destroy->wait_cnt) == 0,
msecs_to_jiffies(60000))) return 0;
pr_crit("Time out waiting for iommufd object to become free\n");
refcount_inc(&to_destroy->wait_cnt); return -EBUSY;
}
/* * Remove the given object id from the xarray if the only reference to the * object is held by the xarray.
*/ int iommufd_object_remove(struct iommufd_ctx *ictx, struct iommufd_object *to_destroy, u32 id, unsignedint flags)
{ struct iommufd_object *obj;
XA_STATE(xas, &ictx->objects, id); bool zerod_wait_cnt = false; int ret;
/* * The purpose of the wait_cnt is to ensure deterministic destruction * of objects used by external drivers and destroyed by this function. * Incrementing this wait_cnt should either be short lived, such as * during ioctl execution, or be revoked and blocked during * pre_destroy(), such as vdev holding the idev's refcount.
*/ if (flags & REMOVE_WAIT) {
ret = iommufd_object_dec_wait(ictx, to_destroy); if (ret) { /* * We have a bug. Put back the callers reference and * defer cleaning this object until close.
*/
refcount_dec(&to_destroy->users); return ret;
}
zerod_wait_cnt = true;
}
xa_lock(&ictx->objects);
obj = xas_load(&xas); if (to_destroy) { /* * If the caller is holding a ref on obj we put it here under * the spinlock.
*/
refcount_dec(&obj->users);
if (WARN_ON(obj != to_destroy)) {
ret = -ENOENT; goto err_xa;
}
} elseif (xa_is_zero(obj) || !obj) {
ret = -ENOENT; goto err_xa;
}
if (!refcount_dec_if_one(&obj->users)) {
ret = -EBUSY; goto err_xa;
}
/* * Since users is zero any positive wait_cnt must be racing * iommufd_put_object(), or we have a bug.
*/ if (!zerod_wait_cnt) {
ret = iommufd_object_dec_wait(ictx, obj); if (WARN_ON(ret)) return ret;
}
ictx = kzalloc(sizeof(*ictx), GFP_KERNEL_ACCOUNT); if (!ictx) return -ENOMEM;
/* * For compatibility with VFIO when /dev/vfio/vfio is opened we default * to the same rlimit accounting as vfio uses.
*/ if (IS_ENABLED(CONFIG_IOMMUFD_VFIO_CONTAINER) &&
filp->private_data == &vfio_misc_dev) {
ictx->account_mode = IOPT_PAGES_ACCOUNT_MM;
pr_info_once("IOMMUFD is providing /dev/vfio/vfio, not VFIO.\n");
}
/* * The objects in the xarray form a graph of "users" counts, and we have * to destroy them in a depth first manner. Leaf objects will reduce the * users count of interior objects when they are destroyed. * * Repeatedly destroying all the "1 users" leaf objects will progress * until the entire list is destroyed. If this can't progress then there * is some bug related to object refcounting.
*/ while (!xa_empty(&ictx->objects)) { unsignedint destroyed = 0; unsignedlong index; bool empty = true;
/* * We can't use xa_empty() to end the loop as the tombstones * are stored as XA_ZERO_ENTRY in the xarray. However * xa_for_each() automatically converts them to NULL and skips * them causing xa_empty() to be kept false. Thus once * xa_for_each() finds no further !NULL entries the loop is * done.
*/
xa_for_each(&ictx->objects, index, obj) {
empty = false; if (!refcount_dec_if_one(&obj->users)) continue;
/* The vm_pgoff must be pre-allocated from mt_mmap, and given to user space */ staticint iommufd_fops_mmap(struct file *filp, struct vm_area_struct *vma)
{ struct iommufd_ctx *ictx = filp->private_data;
size_t length = vma->vm_end - vma->vm_start; struct iommufd_mmap *immap; int rc;
if (!PAGE_ALIGNED(length)) return -EINVAL; if (!(vma->vm_flags & VM_SHARED)) return -EINVAL; if (vma->vm_flags & VM_EXEC) return -EPERM;
mtree_lock(&ictx->mt_mmap); /* vma->vm_pgoff carries a page-shifted start position to an immap */
immap = mtree_load(&ictx->mt_mmap, vma->vm_pgoff << PAGE_SHIFT); if (!immap || !refcount_inc_not_zero(&immap->owner->users)) {
mtree_unlock(&ictx->mt_mmap); return -ENXIO;
}
mtree_unlock(&ictx->mt_mmap);
/* * mtree_load() returns the immap for any contained mmio_addr, so only * allow the exact immap thing to be mapped
*/ if (vma->vm_pgoff != immap->vm_pgoff || length != immap->length) {
rc = -ENXIO; goto err_refcount;
}
/** * iommufd_ctx_get - Get a context reference * @ictx: Context to get * * The caller must already hold a valid reference to ictx.
*/ void iommufd_ctx_get(struct iommufd_ctx *ictx)
{
get_file(ictx->file);
}
EXPORT_SYMBOL_NS_GPL(iommufd_ctx_get, "IOMMUFD");
/** * iommufd_ctx_from_file - Acquires a reference to the iommufd context * @file: File to obtain the reference from * * Returns a pointer to the iommufd_ctx, otherwise ERR_PTR. The struct file * remains owned by the caller and the caller must still do fput. On success * the caller is responsible to call iommufd_ctx_put().
*/ struct iommufd_ctx *iommufd_ctx_from_file(struct file *file)
{ struct iommufd_ctx *ictx;
/** * iommufd_ctx_from_fd - Acquires a reference to the iommufd context * @fd: File descriptor to obtain the reference from * * Returns a pointer to the iommufd_ctx, otherwise ERR_PTR. On success * the caller is responsible to call iommufd_ctx_put().
*/ struct iommufd_ctx *iommufd_ctx_from_fd(int fd)
{ struct file *file;
file = fget(fd); if (!file) return ERR_PTR(-EBADF);
if (file->f_op != &iommufd_fops) {
fput(file); return ERR_PTR(-EBADFD);
} /* fget is the same as iommufd_ctx_get() */ return file->private_data;
}
EXPORT_SYMBOL_NS_GPL(iommufd_ctx_from_fd, "IOMMUFD");
/** * iommufd_ctx_put - Put back a reference * @ictx: Context to put back
*/ void iommufd_ctx_put(struct iommufd_ctx *ictx)
{
fput(ictx->file);
}
EXPORT_SYMBOL_NS_GPL(iommufd_ctx_put, "IOMMUFD");
ret = misc_register(&iommu_misc_dev); if (ret) return ret;
if (IS_ENABLED(CONFIG_IOMMUFD_VFIO_CONTAINER)) {
ret = misc_register(&vfio_misc_dev); if (ret) goto err_misc;
}
ret = iommufd_test_init(); if (ret) goto err_vfio_misc; return 0;
err_vfio_misc: if (IS_ENABLED(CONFIG_IOMMUFD_VFIO_CONTAINER))
misc_deregister(&vfio_misc_dev);
err_misc:
misc_deregister(&iommu_misc_dev); return ret;
}
staticvoid __exit iommufd_exit(void)
{
iommufd_test_exit(); if (IS_ENABLED(CONFIG_IOMMUFD_VFIO_CONTAINER))
misc_deregister(&vfio_misc_dev);
misc_deregister(&iommu_misc_dev);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.