Commit db78436b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull iommufd updates from Jason Gunthorpe:
 "Collection of small cleanup and one fix:

   - Sort headers and struct forward declarations

   - Fix random selftest failures in some cases due to dirty tracking
     tests

   - Have the reserved IOVA regions mechanism work when a HWPT is used
     as a nesting parent. This updates the nesting parent's IOAS with
     the reserved regions of the device and will also install the ITS
     doorbell page on ARM.

   - Add missed validation of parent domain ops against the current
     iommu

   - Fix a syzkaller bug related to integer overflow during ALIGN()

   - Tidy two iommu_domain attach paths"

* tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd:
  iommu: Set iommu_attach_handle->domain in core
  iommufd: Avoid duplicated __iommu_group_set_core_domain() call
  iommufd: Protect against overflow of ALIGN() during iova allocation
  iommufd: Reorder struct forward declarations
  iommufd: Check the domain owner of the parent before creating a nesting domain
  iommufd/device: Enforce reserved IOVA also when attached to hwpt_nested
  iommufd/selftest: Fix buffer read overrrun in the dirty test
  iommufd: Reorder include files
parents 54d7e819 79805c1b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3578,6 +3578,7 @@ int iommu_replace_group_handle(struct iommu_group *group,
		ret = xa_reserve(&group->pasid_array, IOMMU_NO_PASID, GFP_KERNEL);
		if (ret)
			goto err_unlock;
		handle->domain = new_domain;
	}

	ret = __iommu_group_set_domain(group, new_domain);
+28 −28
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
 */
#include <linux/iommu.h>
#include <linux/iommufd.h>
#include <linux/slab.h>
#include <linux/iommu.h>
#include <uapi/linux/iommufd.h>
#include "../iommu-priv.h"

#include "../iommu-priv.h"
#include "io_pagetable.h"
#include "iommufd_private.h"

@@ -327,8 +327,9 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
	return 0;
}

static int iommufd_hwpt_paging_attach(struct iommufd_hwpt_paging *hwpt_paging,
				      struct iommufd_device *idev)
static int
iommufd_device_attach_reserved_iova(struct iommufd_device *idev,
				    struct iommufd_hwpt_paging *hwpt_paging)
{
	int rc;

@@ -354,6 +355,7 @@ static int iommufd_hwpt_paging_attach(struct iommufd_hwpt_paging *hwpt_paging,
int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
				struct iommufd_device *idev)
{
	struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
	int rc;

	mutex_lock(&idev->igroup->lock);
@@ -363,8 +365,8 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
		goto err_unlock;
	}

	if (hwpt_is_paging(hwpt)) {
		rc = iommufd_hwpt_paging_attach(to_hwpt_paging(hwpt), idev);
	if (hwpt_paging) {
		rc = iommufd_device_attach_reserved_iova(idev, hwpt_paging);
		if (rc)
			goto err_unlock;
	}
@@ -387,9 +389,8 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
	mutex_unlock(&idev->igroup->lock);
	return 0;
err_unresv:
	if (hwpt_is_paging(hwpt))
		iopt_remove_reserved_iova(&to_hwpt_paging(hwpt)->ioas->iopt,
					  idev->dev);
	if (hwpt_paging)
		iopt_remove_reserved_iova(&hwpt_paging->ioas->iopt, idev->dev);
err_unlock:
	mutex_unlock(&idev->igroup->lock);
	return rc;
@@ -399,6 +400,7 @@ struct iommufd_hw_pagetable *
iommufd_hw_pagetable_detach(struct iommufd_device *idev)
{
	struct iommufd_hw_pagetable *hwpt = idev->igroup->hwpt;
	struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);

	mutex_lock(&idev->igroup->lock);
	list_del(&idev->group_item);
@@ -406,9 +408,8 @@ iommufd_hw_pagetable_detach(struct iommufd_device *idev)
		iommufd_hwpt_detach_device(hwpt, idev);
		idev->igroup->hwpt = NULL;
	}
	if (hwpt_is_paging(hwpt))
		iopt_remove_reserved_iova(&to_hwpt_paging(hwpt)->ioas->iopt,
					  idev->dev);
	if (hwpt_paging)
		iopt_remove_reserved_iova(&hwpt_paging->ioas->iopt, idev->dev);
	mutex_unlock(&idev->igroup->lock);

	/* Caller must destroy hwpt */
@@ -440,17 +441,17 @@ iommufd_group_remove_reserved_iova(struct iommufd_group *igroup,
}

static int
iommufd_group_do_replace_paging(struct iommufd_group *igroup,
iommufd_group_do_replace_reserved_iova(struct iommufd_group *igroup,
				       struct iommufd_hwpt_paging *hwpt_paging)
{
	struct iommufd_hw_pagetable *old_hwpt = igroup->hwpt;
	struct iommufd_hwpt_paging *old_hwpt_paging;
	struct iommufd_device *cur;
	int rc;

	lockdep_assert_held(&igroup->lock);

	if (!hwpt_is_paging(old_hwpt) ||
	    hwpt_paging->ioas != to_hwpt_paging(old_hwpt)->ioas) {
	old_hwpt_paging = find_hwpt_paging(igroup->hwpt);
	if (!old_hwpt_paging || hwpt_paging->ioas != old_hwpt_paging->ioas) {
		list_for_each_entry(cur, &igroup->device_list, group_item) {
			rc = iopt_table_enforce_dev_resv_regions(
				&hwpt_paging->ioas->iopt, cur->dev, NULL);
@@ -473,6 +474,8 @@ static struct iommufd_hw_pagetable *
iommufd_device_do_replace(struct iommufd_device *idev,
			  struct iommufd_hw_pagetable *hwpt)
{
	struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
	struct iommufd_hwpt_paging *old_hwpt_paging;
	struct iommufd_group *igroup = idev->igroup;
	struct iommufd_hw_pagetable *old_hwpt;
	unsigned int num_devices;
@@ -491,9 +494,8 @@ iommufd_device_do_replace(struct iommufd_device *idev,
	}

	old_hwpt = igroup->hwpt;
	if (hwpt_is_paging(hwpt)) {
		rc = iommufd_group_do_replace_paging(igroup,
						     to_hwpt_paging(hwpt));
	if (hwpt_paging) {
		rc = iommufd_group_do_replace_reserved_iova(igroup, hwpt_paging);
		if (rc)
			goto err_unlock;
	}
@@ -502,11 +504,10 @@ iommufd_device_do_replace(struct iommufd_device *idev,
	if (rc)
		goto err_unresv;

	if (hwpt_is_paging(old_hwpt) &&
	    (!hwpt_is_paging(hwpt) ||
	     to_hwpt_paging(hwpt)->ioas != to_hwpt_paging(old_hwpt)->ioas))
		iommufd_group_remove_reserved_iova(igroup,
						   to_hwpt_paging(old_hwpt));
	old_hwpt_paging = find_hwpt_paging(old_hwpt);
	if (old_hwpt_paging &&
	    (!hwpt_paging || hwpt_paging->ioas != old_hwpt_paging->ioas))
		iommufd_group_remove_reserved_iova(igroup, old_hwpt_paging);

	igroup->hwpt = hwpt;

@@ -524,9 +525,8 @@ iommufd_device_do_replace(struct iommufd_device *idev,
	/* Caller must destroy old_hwpt */
	return old_hwpt;
err_unresv:
	if (hwpt_is_paging(hwpt))
		iommufd_group_remove_reserved_iova(igroup,
						   to_hwpt_paging(hwpt));
	if (hwpt_paging)
		iommufd_group_remove_reserved_iova(igroup, hwpt_paging);
err_unlock:
	mutex_unlock(&idev->igroup->lock);
	return ERR_PTR(rc);
+2 −3
Original line number Diff line number Diff line
@@ -3,14 +3,14 @@
 */
#define pr_fmt(fmt) "iommufd: " fmt

#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/iommufd.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/iommufd.h>
#include <linux/pci.h>
#include <linux/poll.h>
#include <linux/anon_inodes.h>
#include <uapi/linux/iommufd.h>

#include "../iommu-priv.h"
@@ -161,7 +161,6 @@ static int __fault_domain_replace_dev(struct iommufd_device *idev,
		if (!handle)
			return -ENOMEM;

		handle->handle.domain = hwpt->domain;
		handle->idev = idev;
		ret = iommu_replace_group_handle(idev->igroup->group,
						 hwpt->domain, &handle->handle);
+2 −1
Original line number Diff line number Diff line
@@ -225,7 +225,8 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx,
	if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
	    !user_data->len || !ops->domain_alloc_user)
		return ERR_PTR(-EOPNOTSUPP);
	if (parent->auto_domain || !parent->nest_parent)
	if (parent->auto_domain || !parent->nest_parent ||
	    parent->common.domain->owner != ops)
		return ERR_PTR(-EINVAL);

	hwpt_nested = __iommufd_object_alloc(
+12 −4
Original line number Diff line number Diff line
@@ -8,17 +8,17 @@
 * The datastructure uses the iopt_pages to optimize the storage of the PFNs
 * between the domains and xarray.
 */
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/iommu.h>
#include <linux/iommufd.h>
#include <linux/lockdep.h>
#include <linux/iommu.h>
#include <linux/sched/mm.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <uapi/linux/iommufd.h>

#include "io_pagetable.h"
#include "double_span.h"
#include "io_pagetable.h"

struct iopt_pages_list {
	struct iopt_pages *pages;
@@ -112,6 +112,7 @@ static int iopt_alloc_iova(struct io_pagetable *iopt, unsigned long *iova,
	unsigned long page_offset = uptr % PAGE_SIZE;
	struct interval_tree_double_span_iter used_span;
	struct interval_tree_span_iter allowed_span;
	unsigned long max_alignment = PAGE_SIZE;
	unsigned long iova_alignment;

	lockdep_assert_held(&iopt->iova_rwsem);
@@ -131,6 +132,13 @@ static int iopt_alloc_iova(struct io_pagetable *iopt, unsigned long *iova,
				       roundup_pow_of_two(length),
				       1UL << __ffs64(uptr));

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
	max_alignment = HPAGE_SIZE;
#endif
	/* Protect against ALIGN() overflow */
	if (iova_alignment >= max_alignment)
		iova_alignment = max_alignment;

	if (iova_alignment < iopt->iova_alignment)
		return -EINVAL;

Loading