From nobody Wed Dec 17 11:31:07 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2AB47CE7AFB for ; Thu, 28 Sep 2023 04:32:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230350AbjI1EcY (ORCPT ); Thu, 28 Sep 2023 00:32:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41594 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230340AbjI1Eb5 (ORCPT ); Thu, 28 Sep 2023 00:31:57 -0400 Received: from mgamail.intel.com (mgamail.intel.com [192.55.52.93]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1773A1B7; Wed, 27 Sep 2023 21:31:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1695875502; x=1727411502; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Z8MS2UEdSrrUu/Tulcngi9beSpLfxMa1Pwy5rRaxjBI=; b=gXDiDnF4q1rNC7L4MXAGLWnemrifWnClig0HNNhuMSpPj8ujiastjqnS IbcGshtl4qvII5sNjvxZKLJrR3ZM+R4QccvIsd3nrZ4+J3FDnOOS+xTYH rP9tqcwEoqMntcwZGoH8lp2UtYqdlDt2QVoRaIucPhinVsqW+JedjBs4Y nNaIUG7sXc3CLozellSThwvqP2KgA9U0eUIXnBU7uxt1v7+AGrQwvQyht nIFzOXJehXqlV6aOztNZEgUZl8+DSLUdOAokoQPQgK4dl1OHWI2vj3AOe wxMexhFLlscdZUnYUt6UWgxzSREqkk1M0z7Qx2kNws6SmjwbknWckkLFS w==; X-IronPort-AV: E=McAfee;i="6600,9927,10846"; a="379260563" X-IronPort-AV: E=Sophos;i="6.03,182,1694761200"; d="scan'208";a="379260563" Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 27 Sep 2023 21:31:41 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10846"; a="923069079" X-IronPort-AV: E=Sophos;i="6.03,182,1694761200"; d="scan'208";a="923069079" Received: from allen-box.sh.intel.com ([10.239.159.127]) by orsmga005.jf.intel.com with ESMTP; 27 Sep 2023 21:31:37 -0700 From: Lu Baolu To: Joerg Roedel , Will Deacon , Robin Murphy , Jason Gunthorpe , Kevin Tian , Jean-Philippe Brucker , Nicolin Chen Cc: Yi Liu , Jacob Pan , iommu@lists.linux.dev, kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Lu Baolu Subject: [PATCH v6 11/12] iommu: Consolidate per-device fault data management Date: Thu, 28 Sep 2023 12:27:33 +0800 Message-Id: <20230928042734.16134-12-baolu.lu@linux.intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230928042734.16134-1-baolu.lu@linux.intel.com> References: <20230928042734.16134-1-baolu.lu@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" The per-device fault data is a data structure that is used to store information about faults that occur on a device. This data is allocated when IOPF is enabled on the device and freed when IOPF is disabled. The data is used in the paths of iopf reporting, handling, responding, and draining. The fault data is protected by two locks: - dev->iommu->lock: This lock is used to protect the allocation and freeing of the fault data. - dev->iommu->fault_parameter->lock: This lock is used to protect the fault data itself. To make the code simpler and easier to maintain, consolidate the lock mechanism into two helper functions. The dev->iommu->fault_parameter->lock lock is also used in iopf_queue_discard_partial() to improve code readability. This does not fix any existing issues, as iopf_queue_discard_partial() is only used in the VT-d driver's prq_event_thread(), which is a single-threaded path that reports the IOPFs. Signed-off-by: Lu Baolu Reviewed-by: Kevin Tian --- include/linux/iommu.h | 3 + drivers/iommu/io-pgfault.c | 122 +++++++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 46 deletions(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d802bdc9877b..de35a5393f4f 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -486,6 +486,8 @@ struct iommu_device { /** * struct iommu_fault_param - per-device IOMMU fault data * @lock: protect pending faults list + * @users: user counter to manage the lifetime of the data, this field + * is protected by dev->iommu->lock. * @dev: the device that owns this param * @queue: IOPF queue * @queue_list: index into queue->devices @@ -495,6 +497,7 @@ struct iommu_device { */ struct iommu_fault_param { struct mutex lock; + int users; =20 struct device *dev; struct iopf_queue *queue; diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c index 5d9e83d6e14d..59555dc5ac50 100644 --- a/drivers/iommu/io-pgfault.c +++ b/drivers/iommu/io-pgfault.c @@ -26,6 +26,49 @@ void iopf_free_group(struct iopf_group *group) } EXPORT_SYMBOL_GPL(iopf_free_group); =20 +/* + * Return the fault parameter of a device if it exists. Otherwise, return = NULL. + * On a successful return, the caller takes a reference of this parameter = and + * should put it after use by calling iopf_put_dev_fault_param(). + */ +static struct iommu_fault_param *iopf_get_dev_fault_param(struct device *d= ev) +{ + struct dev_iommu *param =3D dev->iommu; + struct iommu_fault_param *fault_param; + + if (!param) + return NULL; + + mutex_lock(¶m->lock); + fault_param =3D param->fault_param; + if (fault_param) + fault_param->users++; + mutex_unlock(¶m->lock); + + return fault_param; +} + +/* Caller must hold a reference of the fault parameter. */ +static void iopf_put_dev_fault_param(struct iommu_fault_param *fault_param) +{ + struct device *dev =3D fault_param->dev; + struct dev_iommu *param =3D dev->iommu; + + mutex_lock(¶m->lock); + if (WARN_ON(fault_param->users <=3D 0 || + fault_param !=3D param->fault_param)) { + mutex_unlock(¶m->lock); + return; + } + + if (--fault_param->users =3D=3D 0) { + param->fault_param =3D NULL; + kfree(fault_param); + put_device(dev); + } + mutex_unlock(¶m->lock); +} + /** * iommu_handle_iopf - IO Page Fault handler * @fault: fault event @@ -72,23 +115,14 @@ static int iommu_handle_iopf(struct iommu_fault *fault= , struct device *dev) struct iopf_group *group; struct iommu_domain *domain; struct iopf_fault *iopf, *next; - struct iommu_fault_param *iopf_param; - struct dev_iommu *param =3D dev->iommu; + struct iommu_fault_param *iopf_param =3D dev->iommu->fault_param; =20 - lockdep_assert_held(¶m->lock); + lockdep_assert_held(&iopf_param->lock); =20 if (fault->type !=3D IOMMU_FAULT_PAGE_REQ) /* Not a recoverable page fault */ return -EOPNOTSUPP; =20 - /* - * As long as we're holding param->lock, the queue can't be unlinked - * from the device and therefore cannot disappear. - */ - iopf_param =3D param->fault_param; - if (!iopf_param) - return -ENODEV; - if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE)) { iopf =3D kzalloc(sizeof(*iopf), GFP_KERNEL); if (!iopf) @@ -167,18 +201,15 @@ static int iommu_handle_iopf(struct iommu_fault *faul= t, struct device *dev) */ int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt) { - struct dev_iommu *param =3D dev->iommu; + struct iommu_fault_param *fault_param; struct iopf_fault *evt_pending =3D NULL; - struct iommu_fault_param *fparam; int ret =3D 0; =20 - if (!param || !evt) + fault_param =3D iopf_get_dev_fault_param(dev); + if (!fault_param) return -EINVAL; =20 - /* we only report device fault if there is a handler registered */ - mutex_lock(¶m->lock); - fparam =3D param->fault_param; - + mutex_lock(&fault_param->lock); if (evt->fault.type =3D=3D IOMMU_FAULT_PAGE_REQ && (evt->fault.prm.flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE)) { evt_pending =3D kmemdup(evt, sizeof(struct iopf_fault), @@ -187,20 +218,18 @@ int iommu_report_device_fault(struct device *dev, str= uct iopf_fault *evt) ret =3D -ENOMEM; goto done_unlock; } - mutex_lock(&fparam->lock); - list_add_tail(&evt_pending->list, &fparam->faults); - mutex_unlock(&fparam->lock); + list_add_tail(&evt_pending->list, &fault_param->faults); } =20 ret =3D iommu_handle_iopf(&evt->fault, dev); if (ret && evt_pending) { - mutex_lock(&fparam->lock); list_del(&evt_pending->list); - mutex_unlock(&fparam->lock); kfree(evt_pending); } done_unlock: - mutex_unlock(¶m->lock); + mutex_unlock(&fault_param->lock); + iopf_put_dev_fault_param(fault_param); + return ret; } EXPORT_SYMBOL_GPL(iommu_report_device_fault); @@ -212,19 +241,20 @@ int iommu_page_response(struct device *dev, int ret =3D -EINVAL; struct iopf_fault *evt; struct iommu_fault_page_request *prm; - struct dev_iommu *param =3D dev->iommu; + struct iommu_fault_param *fault_param; const struct iommu_ops *ops =3D dev_iommu_ops(dev); bool has_pasid =3D msg->flags & IOMMU_PAGE_RESP_PASID_VALID; =20 if (!ops->page_response) return -ENODEV; =20 - if (!param || !param->fault_param) - return -EINVAL; + fault_param =3D iopf_get_dev_fault_param(dev); + if (!fault_param) + return -ENODEV; =20 /* Only send response if there is a fault report pending */ - mutex_lock(¶m->fault_param->lock); - if (list_empty(¶m->fault_param->faults)) { + mutex_lock(&fault_param->lock); + if (list_empty(&fault_param->faults)) { dev_warn_ratelimited(dev, "no pending PRQ, drop response\n"); goto done_unlock; } @@ -232,7 +262,7 @@ int iommu_page_response(struct device *dev, * Check if we have a matching page request pending to respond, * otherwise return -EINVAL */ - list_for_each_entry(evt, ¶m->fault_param->faults, list) { + list_for_each_entry(evt, &fault_param->faults, list) { prm =3D &evt->fault.prm; if (prm->grpid !=3D msg->grpid) continue; @@ -260,7 +290,9 @@ int iommu_page_response(struct device *dev, } =20 done_unlock: - mutex_unlock(¶m->fault_param->lock); + mutex_unlock(&fault_param->lock); + iopf_put_dev_fault_param(fault_param); + return ret; } EXPORT_SYMBOL_GPL(iommu_page_response); @@ -279,22 +311,15 @@ EXPORT_SYMBOL_GPL(iommu_page_response); */ int iopf_queue_flush_dev(struct device *dev) { - int ret =3D 0; - struct iommu_fault_param *iopf_param; - struct dev_iommu *param =3D dev->iommu; + struct iommu_fault_param *iopf_param =3D iopf_get_dev_fault_param(dev); =20 - if (!param) + if (!iopf_param) return -ENODEV; =20 - mutex_lock(¶m->lock); - iopf_param =3D param->fault_param; - if (iopf_param) - flush_workqueue(iopf_param->queue->wq); - else - ret =3D -ENODEV; - mutex_unlock(¶m->lock); + flush_workqueue(iopf_param->queue->wq); + iopf_put_dev_fault_param(iopf_param); =20 - return ret; + return 0; } EXPORT_SYMBOL_GPL(iopf_queue_flush_dev); =20 @@ -318,11 +343,13 @@ int iopf_queue_discard_partial(struct iopf_queue *que= ue) =20 mutex_lock(&queue->lock); list_for_each_entry(iopf_param, &queue->devices, queue_list) { + mutex_lock(&iopf_param->lock); list_for_each_entry_safe(iopf, next, &iopf_param->partial, list) { list_del(&iopf->list); kfree(iopf); } + mutex_unlock(&iopf_param->lock); } mutex_unlock(&queue->lock); return 0; @@ -361,6 +388,7 @@ int iopf_queue_add_device(struct iopf_queue *queue, str= uct device *dev) INIT_LIST_HEAD(&fault_param->faults); INIT_LIST_HEAD(&fault_param->partial); fault_param->dev =3D dev; + fault_param->users =3D 1; list_add(&fault_param->queue_list, &queue->devices); fault_param->queue =3D queue; =20 @@ -413,9 +441,11 @@ int iopf_queue_remove_device(struct iopf_queue *queue,= struct device *dev) list_for_each_entry_safe(iopf, next, &fault_param->partial, list) kfree(iopf); =20 - param->fault_param =3D NULL; - kfree(fault_param); - put_device(dev); + if (--fault_param->users =3D=3D 0) { + param->fault_param =3D NULL; + kfree(fault_param); + put_device(dev); + } unlock: mutex_unlock(¶m->lock); mutex_unlock(&queue->lock); --=20 2.34.1