Message ID | 20240208082307.15759-4-yi.l.liu@intel.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel+bounces-57607-ouuuleilei=gmail.com@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:50ea:b0:106:860b:bbdd with SMTP id r10csp22060dyd; Thu, 8 Feb 2024 00:28:29 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCXljzwv8IOJJA5zVEsdeZa69x2/vNs28p+yYbJF4LV4DHc6WAcIG0YDMJF08/LfuA3lMcklqvEfq68Dxmcu1MoBqiswlg== X-Google-Smtp-Source: AGHT+IGdhpqaP6QbD6wpj+FxWWrHWvMaqVF+mqqaW9R7vE1jKMmEP1j0Hhpw2t2Pbjr9VUAIvT5s X-Received: by 2002:a05:6870:be8c:b0:218:9e23:6826 with SMTP id nx12-20020a056870be8c00b002189e236826mr10783960oab.3.1707380909791; Thu, 08 Feb 2024 00:28:29 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1707380909; cv=pass; d=google.com; s=arc-20160816; b=JPCnwLE9dKM+me92JyBP4l3NSGD91+C2fZ94a25DdwDkEVYVYRM8ttH5dtapolTJvD gLkLQfq+A4dvoSeScz/uE/mxLWf3TVdUy2saC/aEHEYKEqtvckh+rW+XaSDJTWr1OXZA hQLgG+g1TuXCgeIX+cWZsrpB3kQVHRGn1+LmFNfJHTZI5BT8lqx20tZwNuUBp/Tn0qp2 RJIv28Kvncl7nilI85SkYFGEFKT3A+1PSdDV4Q6Lrge1hW9q+zDA7M6U/hgGdsVHqo1w +t1u9DEumPJ5S2O1NUIqmlurLXWCjKJdgiMroMo6j1H5oSowHANK5uJLxMUDuqS+LAky UFHQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=ni76zA2Ft+Pri4MG8zT5PHduJlj2MCoAAvDoTxR1IX0=; fh=6S30KlT0SH32Pn7OMqG15u5Tz8XJIjMfDz1DMnKLS+k=; b=YTaKQvjhKTIqEoYGseCxyPmdRqSK3pBAqg74LcVJQDSvcsDvcK03ouYQezoDcpap/F Vb+UbZ/tL6bh/Z2Vtp6iKOd0u8eFlDtpxTZ6vIO3R6+C35pSN24dDyAXNL4XNTsQ0E+B isHIvtTnh/Mq02AnnGPTXzpGLWbvxanlTmogEVNPXa9qQFasYkr3AemxYHthlo6nANsn ui5yW+4xCe8F0AmzeDFiNUSetTvgl80rGZbZ+Uej2T8RIlANx88BovtYqNuaGBt+GlVy p1PvzRU6aaljL1bUhH3lDfKoo12Ot4Lgszk0DQZDr8gzqRStfv0jagT6pJfQGfI3ytNj nZsg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=KafITziR; arc=pass (i=1 spf=pass spfdomain=intel.com dkim=pass dkdomain=intel.com dmarc=pass fromdomain=intel.com); spf=pass (google.com: domain of linux-kernel+bounces-57607-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-57607-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com X-Forwarded-Encrypted: i=2; AJvYcCUEeMBbGaGMNak4yCu/D18r+HRheGGYVufQfKLZY+iZdvvd4m+n40XdNKr/ylaxzzbeuUtmsGhQh3YrkJ40UkKzRrVlWQ== Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id p25-20020a639519000000b005dc1c316cf5si321665pgd.357.2024.02.08.00.28.29 for <ouuuleilei@gmail.com> (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Feb 2024 00:28:29 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-57607-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=KafITziR; arc=pass (i=1 spf=pass spfdomain=intel.com dkim=pass dkdomain=intel.com dmarc=pass fromdomain=intel.com); spf=pass (google.com: domain of linux-kernel+bounces-57607-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-57607-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id B5BD9B248D2 for <ouuuleilei@gmail.com>; Thu, 8 Feb 2024 08:25:26 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4C9CB6DCFD; Thu, 8 Feb 2024 08:23:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="KafITziR" Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5D5C36A348; Thu, 8 Feb 2024 08:23:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.15 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707380595; cv=none; b=LKroM8zLWYPamuuPb34BC25eDnjc1K1dyIcceBcvq40WvjKm1eegKMALZdNST4U6aOSGyG7iBN70HHaMCuiJyv/YWXwmeR/L22mwsPoxFpSXzVZc+YLzYVcoiWnNm/jVeUpoK3TEBZs21xoIpeOyl1VaKZBzYFySCcf+zmdKT5E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707380595; c=relaxed/simple; bh=rkAIlr3b517pIibgb/ERZPXd9p33fw79H8+yLPYzn6Y=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LPTiUF10Xnf+zvdyVyhwtwAQdphtThq90e+inx+QP7E2N2b2ImjkwjP0S7vWojhIK5XKXsdNMsDENyn/rNcLlkuT2TBh1geinNTus/BJjM7Fpt0X5S5psOn6RGr7Sa4RlahJFrh6BvZh099rGgDO8QsrcufQP4idr2Mb68pM71s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=KafITziR; arc=none smtp.client-ip=198.175.65.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1707380594; x=1738916594; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=rkAIlr3b517pIibgb/ERZPXd9p33fw79H8+yLPYzn6Y=; b=KafITziRG6TwsCCYFb918pNXiwe4PbU7q8V/uSsZKUlEyTKwLpKn+fxH y4detufcB7WSEqmmERD6lycbwQj0V+VB58rtQMKBTa6k3FUsPjDuM/99C uaUiL3ADwAnyzyOUnrcokIKJYevOSeQIjq9hfwg6TSm0GyeRVzH+pB/rr z3aFybx91nwsYq28Okd7HHD4DNOpTmNz7kYxu2r/SYJhJQgZBrOhHTmRs 5TPEBqWxMTWolQI1rFWnEhTMi8B1X+izVkNaOVLCaftJXSPQmP1jLpkGJ pjztp/XGoWNoUrkD9kncXDZHfAZ06f49ER707SM3fHqCcO0jpwTGj2aKz g==; X-IronPort-AV: E=McAfee;i="6600,9927,10977"; a="5036318" X-IronPort-AV: E=Sophos;i="6.05,253,1701158400"; d="scan'208";a="5036318" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa107.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Feb 2024 00:23:13 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.05,253,1701158400"; d="scan'208";a="6252111" Received: from 984fee00a4c6.jf.intel.com ([10.165.58.231]) by orviesa003.jf.intel.com with ESMTP; 08 Feb 2024 00:23:12 -0800 From: Yi Liu <yi.l.liu@intel.com> To: joro@8bytes.org, jgg@nvidia.com, kevin.tian@intel.com, baolu.lu@linux.intel.com Cc: alex.williamson@redhat.com, robin.murphy@arm.com, eric.auger@redhat.com, nicolinc@nvidia.com, kvm@vger.kernel.org, chao.p.peng@linux.intel.com, yi.l.liu@intel.com, yi.y.sun@linux.intel.com, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, zhenzhong.duan@intel.com, joao.m.martins@oracle.com Subject: [PATCH rc 3/8] iommu/vt-d: Add missing iotlb flush for parent domain Date: Thu, 8 Feb 2024 00:23:02 -0800 Message-Id: <20240208082307.15759-4-yi.l.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240208082307.15759-1-yi.l.liu@intel.com> References: <20240208082307.15759-1-yi.l.liu@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: <linux-kernel.vger.kernel.org> List-Subscribe: <mailto:linux-kernel+subscribe@vger.kernel.org> List-Unsubscribe: <mailto:linux-kernel+unsubscribe@vger.kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1790318644856969937 X-GMAIL-MSGID: 1790318644856969937 |
Series |
Add missing cache flush and dirty tracking set for nested parent domain
|
|
Commit Message
Yi Liu
Feb. 8, 2024, 8:23 a.m. UTC
If a domain is used as the parent in nested translation its mappings might
be cached using DID of the nested domain. But the existing code ignores
this fact to only invalidate the iotlb entries tagged by the domain's own
DID.
Loop the s1_domains list, if any, to invalidate all iotlb entries related
to the target s2 address range. According to VT-d spec there is no need for
software to explicitly flush the affected s1 cache. It's implicitly done by
HW when s2 cache is invalidated.
Fixes: b41e38e22539 ("iommu/vt-d: Add nested domain allocation")
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/intel/iommu.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
Comments
> From: Liu, Yi L <yi.l.liu@intel.com> > Sent: Thursday, February 8, 2024 4:23 PM > > +/* > + * Flush the relevant caches in nested translation if the domain > + * also serves as a parent > + */ > +static void parent_domain_flush(struct dmar_domain *domain, > + unsigned long pfn, > + unsigned long pages, int ih) > +{ > + struct dmar_domain *s1_domain; > + > + spin_lock(&domain->s1_lock); > + list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) { > + struct iommu_domain_info *info; > + unsigned long i; > + > + xa_for_each(&s1_domain->iommu_array, i, info) > + __iommu_flush_iotlb_psi(info->iommu, info->did, > + pfn, pages, ih); > + } As Jason suggested before this xarray lacks of proper locking. but given it's rc fix I'm fine with it. @Baolu we do need fix it soon so this problem won't further expand like here. Reviewed-by: Kevin Tian <kevin.tian@intel.com>
On 2024/2/8 16:38, Tian, Kevin wrote: >> From: Liu, Yi L<yi.l.liu@intel.com> >> Sent: Thursday, February 8, 2024 4:23 PM >> >> +/* >> + * Flush the relevant caches in nested translation if the domain >> + * also serves as a parent >> + */ >> +static void parent_domain_flush(struct dmar_domain *domain, >> + unsigned long pfn, >> + unsigned long pages, int ih) >> +{ >> + struct dmar_domain *s1_domain; >> + >> + spin_lock(&domain->s1_lock); >> + list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) { >> + struct iommu_domain_info *info; >> + unsigned long i; >> + >> + xa_for_each(&s1_domain->iommu_array, i, info) >> + __iommu_flush_iotlb_psi(info->iommu, info->did, >> + pfn, pages, ih); >> + } > As Jason suggested before this xarray lacks of proper locking. > > but given it's rc fix I'm fine with it. @Baolu we do need fix it soon so > this problem won't further expand like here. Yes. I planned to fix the locking issue in a separated series. Best regards, baolu
On Thu, Feb 08, 2024 at 12:23:02AM -0800, Yi Liu wrote: > If a domain is used as the parent in nested translation its mappings might > be cached using DID of the nested domain. But the existing code ignores > this fact to only invalidate the iotlb entries tagged by the domain's own > DID. > Loop the s1_domains list, if any, to invalidate all iotlb entries related > to the target s2 address range. According to VT-d spec there is no need for > software to explicitly flush the affected s1 cache. It's implicitly done by > HW when s2 cache is invalidated. I had to look this up to understand what it means.. The HW caches per-DID and if you invalidate the DID's S2 then the HW flushes the S1 as well within that DID only. It doesn't mean that the S2 is globally shared across all the nesting translations (like ARM does), and you still have to iterate over every nesting DID. In light of that this design seems to have gone a bit off.. A domain should have a list of places that need invalidation, specifically a list of DIDs and ATCs that need an invalidation to be issued. Instead we now somehow have 4 different lists in the domain the invalidation code iterates over? So I would think this: struct dmar_domain { struct xarray iommu_array; /* Attached IOMMU array */ struct list_head devices; /* all devices' list */ struct list_head dev_pasids; /* all attached pasids */ struct list_head s1_domains; Would make sense to be collapsed into one logical list of attached devices: struct intel_iommu_domain_attachment { unsigned int did; ioasid_t pasid; struct device_domain_info *info; list_head item; }; When you attach a S1/S2 nest you allocate two of the above structs and one is linked on the S1 and one is linked on the S2.. Jason
On 2024/2/21 23:19, Jason Gunthorpe wrote: > On Thu, Feb 08, 2024 at 12:23:02AM -0800, Yi Liu wrote: >> If a domain is used as the parent in nested translation its mappings might >> be cached using DID of the nested domain. But the existing code ignores >> this fact to only invalidate the iotlb entries tagged by the domain's own >> DID. > >> Loop the s1_domains list, if any, to invalidate all iotlb entries related >> to the target s2 address range. According to VT-d spec there is no need for >> software to explicitly flush the affected s1 cache. It's implicitly done by >> HW when s2 cache is invalidated. > > I had to look this up to understand what it means.. The HW caches > per-DID and if you invalidate the DID's S2 then the HW flushes the S1 > as well within that DID only. yes. > > It doesn't mean that the S2 is globally shared across all the nesting > translations (like ARM does), and you still have to iterate over every > nesting DID. > > In light of that this design seems to have gone a bit off.. > > A domain should have a list of places that need invalidation, > specifically a list of DIDs and ATCs that need an invalidation to be > issued. > > Instead we now somehow have 4 different lists in the domain the > invalidation code iterates over? > > So I would think this: > > struct dmar_domain { > struct xarray iommu_array; /* Attached IOMMU array */ > struct list_head devices; /* all devices' list */ > struct list_head dev_pasids; /* all attached pasids */ > struct list_head s1_domains; > > Would make sense to be collapsed into one logical list of attached > devices: > > struct intel_iommu_domain_attachment { > unsigned int did; > ioasid_t pasid; > struct device_domain_info *info; > list_head item; > }; > > When you attach a S1/S2 nest you allocate two of the above structs and > one is linked on the S1 and one is linked on the S2.. yes, this shall work. ATC flushing should be fine. But there can be a drawback when flushing DIDs. VT-d side, if there are multiple devices behind the same iommu unit, just need one DID flushing is enough. But after the above change, the number of DID flushing would increase per the number of devices. Although no functional issue, but it submits duplicated invalidation.
On Thu, Feb 22, 2024 at 04:34:10PM +0800, Yi Liu wrote: > > It doesn't mean that the S2 is globally shared across all the nesting > > translations (like ARM does), and you still have to iterate over every > > nesting DID. > > > > In light of that this design seems to have gone a bit off.. > > > > A domain should have a list of places that need invalidation, > > specifically a list of DIDs and ATCs that need an invalidation to be > > issued. > > > > Instead we now somehow have 4 different lists in the domain the > > invalidation code iterates over? > > > > So I would think this: > > > > struct dmar_domain { > > struct xarray iommu_array; /* Attached IOMMU array */ > > struct list_head devices; /* all devices' list */ > > struct list_head dev_pasids; /* all attached pasids */ > > struct list_head s1_domains; > > > > Would make sense to be collapsed into one logical list of attached > > devices: > > > > struct intel_iommu_domain_attachment { > > unsigned int did; > > ioasid_t pasid; > > struct device_domain_info *info; > > list_head item; > > }; > > > > When you attach a S1/S2 nest you allocate two of the above structs and > > one is linked on the S1 and one is linked on the S2.. > > yes, this shall work. ATC flushing should be fine. But there can be a > drawback when flushing DIDs. VT-d side, if there are multiple devices > behind the same iommu unit, just need one DID flushing is enough. But > after the above change, the number of DID flushing would increase per > the number of devices. Although no functional issue, but it submits > duplicated invalidation. At least the three server drivers all have this same problem, I would like to seem some core code to help solve it, since it is tricky and the RCU idea is good.. Collapsing invalidations can be done by sorting, I think this was Robin's suggestion. But we could also easially maintain multiple list threads hidden inside the API, or allocate a multi-level list. Something very approximately like this: Jason diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index d14413916f93a0..7b2de139e7c437 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -38,6 +38,8 @@ #include "iommu-sva.h" +#include <linux/iommu-alist.h> + static struct kset *iommu_group_kset; static DEFINE_IDA(iommu_group_ida); static DEFINE_IDA(iommu_global_pasid_ida); diff --git a/include/linux/iommu-alist.h b/include/linux/iommu-alist.h new file mode 100644 index 00000000000000..7a9243f8b2b5f8 --- /dev/null +++ b/include/linux/iommu-alist.h @@ -0,0 +1,126 @@ +#include <linux/list.h> +#include <linux/rculist.h> +#include <linux/iommu.h> + +/* + * Place in an iommu_domain to keep track of what devices have been attached to + * it. + */ +struct iommu_attach_list { + spinlock_t lock; + struct list_head all; +}; + +/* + * Allocate for every time a device is attached to a domain + */ +struct iommu_attach_elm { + /* + * Note that this pointer is accessed under RCU, the driver has to be + * careful to rcu free it. + */ + struct iommu_device *iommu_device; + ioasid_t pasid; + u8 using_atc; + struct list_head all_item; + + /* May not be accessed under RCU! */ + struct device *device; +}; + +void iommu_attach_init(struct iommu_attach_list *alist) +{ + spin_lock_init(&alist->lock); +} + +int iommu_attach_add(struct iommu_attach_list *alist, + struct iommu_attach_elm *elm) +{ + struct list_head *insert_after; + + spin_lock(&alist->lock); + insert_after = list_find_sorted_insertion(alist, elm, cmp); + list_add_rcu(&elm->all_item, insert_after); + spin_unlock(&alist->lock); +} + +void iommu_attach_del_free(struct iommu_attach_list *alist, struct iommu_attach_elm *elm) +{ + spin_lock(&alist->lock); + list_del_rcu(&elm->all_item); + spin_unlock(&alist->lock); + /* assumes 0 offset */ + kfree_rcu_mightsleep(elm); +} + +static struct iommu_attach_elm * +__iommu_attach_next_iommu(struct iommu_attach_list *alist, + struct iommu_attach_elm *pos, + struct iommu_attach_elm *last) +{ + struct iommu_attach_elm *next; + + do { + next = list_next_or_null_rcu(&alist->all, &pos->all_item, + struct iommu_attach_elm, all_item); + if (!next) + return NULL; + if (!last) + return next; + } while (pos->iommu_device == next->iommu_device); + return next; +} + +/* assumes 0 offset */ +#define iommu_attach_next_iommu(alist, pos, last, member) \ + container_of(__iommu_attach_next_iommu(alist, &(pos)->member, \ + &(last)->member), \ + typeof(*pos), member) + +#define iommu_attach_for_each_iommu(pos, last, alist, member) \ + for (({ \ + RCU_LOCKDEP_WARN( \ + !rcu_read_lock_any_held(), \ + "RCU-list traversed in non-reader section!"); \ + }), \ + pos = list_first_or_null_rcu(&(alist)->all, typeof(*pos), \ + member.all_item), \ + last = NULL; \ + pos; pos = iommu_attach_next_iommu(alist, pos, last, member)) + +/* Example */ +struct driver_domain { + struct iommu_domain domain; + struct iommu_attach_list alist; +}; + +struct driver_attach_elm { + struct iommu_attach_elm aelm; + unsigned int cache_tag; +}; + +static void example(struct driver_domain *domain) +{ + struct driver_attach_elm *elm; + struct driver_attach_elm *pos, *last; + bool need_atc; + + iommu_attach_init(&domain->alist); + + elm = kzalloc(sizeof(*elm), GFP_KERNEL); + iommu_attach_add(&domain->alist, &elm->aelm); + + rcu_read_lock(); + iommu_attach_for_each_iommu(pos, last, &domain->alist, aelm) { + invalidate_iommu(elm); + need_atc |= elm->aelm.using_atc; + } + if (need_atc) { + list_for_each_entry_rcu(pos, &domain->alist.all, + aelm.all_item) { + if (pos->aelm.using_atc) + invalidate_atc(elm); + } + } + rcu_read_unlock(); +}
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index eef6a187b651..46174ff5ae22 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -1452,6 +1452,28 @@ static void __mapping_notify_one(struct intel_iommu *iommu, struct dmar_domain * iommu_flush_write_buffer(iommu); } +/* + * Flush the relevant caches in nested translation if the domain + * also serves as a parent + */ +static void parent_domain_flush(struct dmar_domain *domain, + unsigned long pfn, + unsigned long pages, int ih) +{ + struct dmar_domain *s1_domain; + + spin_lock(&domain->s1_lock); + list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) { + struct iommu_domain_info *info; + unsigned long i; + + xa_for_each(&s1_domain->iommu_array, i, info) + __iommu_flush_iotlb_psi(info->iommu, info->did, + pfn, pages, ih); + } + spin_unlock(&domain->s1_lock); +} + static void intel_flush_iotlb_all(struct iommu_domain *domain) { struct dmar_domain *dmar_domain = to_dmar_domain(domain); @@ -1471,6 +1493,9 @@ static void intel_flush_iotlb_all(struct iommu_domain *domain) if (!cap_caching_mode(iommu->cap)) iommu_flush_dev_iotlb(dmar_domain, 0, MAX_AGAW_PFN_WIDTH); } + + if (dmar_domain->nested_parent) + parent_domain_flush(dmar_domain, 0, -1, 0); } static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu) @@ -1994,6 +2019,9 @@ static void switch_to_super_page(struct dmar_domain *domain, iommu_flush_iotlb_psi(info->iommu, domain, start_pfn, lvl_pages, 0, 0); + if (domain->nested_parent) + parent_domain_flush(domain, start_pfn, + lvl_pages, 0); } pte++; @@ -4126,6 +4154,9 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain, start_pfn, nrpages, list_empty(&gather->freelist), 0); + if (dmar_domain->nested_parent) + parent_domain_flush(dmar_domain, start_pfn, nrpages, + list_empty(&gather->freelist)); put_pages_list(&gather->freelist); }