linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Yinghai Lu <yinghai@kernel.org>
To: Jesse Barnes <jbarnes@virtuousgeek.org>, x86 <x86@kernel.org>
Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org,
	Yinghai Lu <yinghai@kernel.org>,
	David Woodhouse <dwmw2@infradead.org>,
	Vinod Koul <vinod.koul@intel.com>,
	Dan Williams <dan.j.williams@intel.com>,
	iommu@lists.linux-foundation.org
Subject: [PATCH 07/23] pci, dmar: Update dmar units devices list during hotplug
Date: Mon,  5 Mar 2012 23:13:44 -0800	[thread overview]
Message-ID: <1331018040-30725-8-git-send-email-yinghai@kernel.org> (raw)
In-Reply-To: <1331018040-30725-1-git-send-email-yinghai@kernel.org>

When do pci remove/rescan on system that have more iommus, got

[  894.089745] Set context mapping for c4:00.0
[  894.110890] mpt2sas3: Allocated physical memory: size(4293 kB)
[  894.112556] mpt2sas3: Current Controller Queue Depth(1883), Max Controller Queue Depth(2144)
[  894.127278] mpt2sas3: Scatter Gather Elements per IO(128)
[  894.361295] DRHD: handling fault status reg 2
[  894.364053] DMAR:[DMA Read] Request device [c4:00.0] fault addr fffbe000
[  894.364056] DMAR:[fault reason 02] Present bit in context entry is cl

it turns out when remove/rescan, pci dev will be freed and will get another new dev.
but drhd units still keep old one... so dmar_find_matched_drhd_unit will
return wrong drhd and iommu for the device that is not on first iommu.

So need to update devices in drhd_units during pci remove/rescan.

Could save domain/bus/device/function aside in the list and according that info
restore new dev to drhd_units later.
Then dmar_find_matched_drdh_unit and device_to_iommu could return right drhd and iommu.

Add remove_dev_from_drhd/restore_dev_to_drhd functions to do the real work.
call them in device ADD_DEVICE and UNBOUND_DRIVER

Need to do the samething to atsr.  (expose dmar_atsr_units and add atsru->segment)

After patch, will right iommu for the new dev and will not get DMAR error any more.

-v2: add pci_dev_put/pci_dev_get to make refcount consistent.

Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: iommu@lists.linux-foundation.org
---
 drivers/iommu/intel-iommu.c |  187 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/dmar.h        |    4 +
 2 files changed, 181 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index c9c6053..39ef2ce 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -665,6 +665,159 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
 	return NULL;
 }
 
+#ifdef CONFIG_HOTPLUG
+struct dev_dmaru {
+	struct list_head list;
+	void *dmaru;
+	int index;
+	int segment;
+	unsigned char bus;
+	unsigned int devfn;
+};
+
+static int
+save_dev_dmaru(int segment, unsigned char bus, unsigned int devfn,
+		 void *dmaru, int index, struct list_head *lh)
+{
+	struct dev_dmaru *m;
+
+	m = kzalloc(sizeof(*m), GFP_KERNEL);
+	if (!m)
+		return -ENOMEM;
+
+	m->segment = segment;
+	m->bus     = bus;
+	m->devfn   = devfn;
+	m->dmaru   = dmaru;
+	m->index   = index;
+
+	list_add(&m->list, lh);
+
+	return 0;
+}
+
+static void
+*get_dev_dmaru(int segment, unsigned char bus, unsigned int devfn,
+		int *index, struct list_head *lh)
+{
+	struct dev_dmaru *m;
+	void *dmaru = NULL;
+
+	list_for_each_entry(m, lh, list) {
+		if (m->segment == segment &&
+		    m->bus == bus && m->devfn == devfn) {
+			*index = m->index;
+			dmaru  = m->dmaru;
+			list_del(&m->list);
+			kfree(m);
+			break;
+		}
+	}
+
+	return dmaru;
+}
+
+static LIST_HEAD(saved_dev_drhd_list);
+
+static void remove_dev_from_drhd(struct pci_dev *dev)
+{
+	struct dmar_drhd_unit *drhd = NULL;
+	int segment = pci_domain_nr(dev->bus);
+	int i;
+
+	for_each_drhd_unit(drhd) {
+		if (drhd->ignored)
+			continue;
+		if (segment != drhd->segment)
+			continue;
+
+		for (i = 0; i < drhd->devices_cnt; i++) {
+			if (drhd->devices[i] == dev) {
+				/* save it at first if it is in drhd */
+				save_dev_dmaru(segment, dev->bus->number,
+						dev->devfn, drhd, i,
+						&saved_dev_drhd_list);
+				/* always remove it */
+				pci_dev_put(dev);
+				drhd->devices[i] = NULL;
+				return;
+			}
+		}
+	}
+}
+
+static void restore_dev_to_drhd(struct pci_dev *dev)
+{
+	struct dmar_drhd_unit *drhd = NULL;
+	int i;
+
+	/* find the stored drhd */
+	drhd = get_dev_dmaru(pci_domain_nr(dev->bus), dev->bus->number,
+				 dev->devfn, &i, &saved_dev_drhd_list);
+	/* restore that into drhd */
+	if (drhd)
+		drhd->devices[i] = pci_dev_get(dev);
+}
+#else
+static void remove_dev_from_drhd(struct pci_dev *dev)
+{
+}
+
+static void restore_dev_to_drhd(struct pci_dev *dev)
+{
+}
+#endif
+
+#if defined(CONFIG_DMAR) && defined(CONFIG_HOTPLUG)
+static LIST_HEAD(saved_dev_atsr_list);
+
+static void remove_dev_from_atsr(struct pci_dev *dev)
+{
+	struct dmar_atsr_unit *atsr = NULL;
+	int segment = pci_domain_nr(dev->bus);
+	int i;
+
+	for_each_atsr_unit(atsr) {
+		if (segment != atsr->segment)
+			continue;
+
+		for (i = 0; i < atsr->devices_cnt; i++) {
+			if (atsr->devices[i] == dev) {
+				/* save it at first if it is in drhd */
+				save_dev_dmaru(segment, dev->bus->number,
+						dev->devfn, atsr, i,
+						&saved_dev_atsr_list);
+				/* always remove it */
+				pci_dev_put(dev);
+				atsr->devices[i] = NULL;
+				return;
+			}
+		}
+	}
+}
+
+static void restore_dev_to_atsr(struct pci_dev *dev)
+{
+	struct dmar_atsr_unit *atsr = NULL;
+	int i;
+
+	/* find the stored atsr */
+	atsr = get_dev_dmaru(pci_domain_nr(dev->bus), dev->bus->number,
+				 dev->devfn, &i, &saved_dev_atsr_list);
+	/* restore that into atsr */
+	if (atsr)
+		atsr->devices[i] = pci_dev_get(dev);
+}
+#else
+static void remove_dev_from_atsr(struct pci_dev *dev)
+{
+}
+
+static void restore_dev_to_atsr(struct pci_dev *dev)
+{
+}
+#endif
+
 static void domain_flush_cache(struct dmar_domain *domain,
 			       void *addr, int size)
 {
@@ -3474,6 +3627,7 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
 
 	atsru->hdr = hdr;
 	atsru->include_all = atsr->flags & 0x1;
+	atsru->segment = atsr->segment;
 
 	list_add(&atsru->list, &dmar_atsr_units);
 
@@ -3505,16 +3659,13 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
 {
 	int i;
 	struct pci_bus *bus;
-	struct acpi_dmar_atsr *atsr;
 	struct dmar_atsr_unit *atsru;
 
 	dev = pci_physfn(dev);
 
-	list_for_each_entry(atsru, &dmar_atsr_units, list) {
-		atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
-		if (atsr->segment == pci_domain_nr(dev->bus))
+	list_for_each_entry(atsru, &dmar_atsr_units, list)
+		if (atsru->segment == pci_domain_nr(dev->bus))
 			goto found;
-	}
 
 	return 0;
 
@@ -3574,20 +3725,36 @@ static int device_notifier(struct notifier_block *nb,
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct dmar_domain *domain;
 
-	if (iommu_no_mapping(dev))
+	if (unlikely(dev->bus != &pci_bus_type))
 		return 0;
 
-	domain = find_domain(pdev);
-	if (!domain)
-		return 0;
+	switch (action) {
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		if (iommu_no_mapping(dev))
+			goto out;
+
+		if (iommu_pass_through)
+			goto out;
+
+		domain = find_domain(pdev);
+		if (!domain)
+			goto out;
 
-	if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) {
 		domain_remove_one_dev_info(domain, pdev);
 
 		if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
 		    !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
 		    list_empty(&domain->devices))
 			domain_exit(domain);
+out:
+		remove_dev_from_drhd(pdev);
+		remove_dev_from_atsr(pdev);
+
+		break;
+	case BUS_NOTIFY_ADD_DEVICE:
+		restore_dev_to_drhd(pdev);
+		restore_dev_to_atsr(pdev);
+		break;
 	}
 
 	return 0;
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index 731a609..57e1375 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -236,9 +236,13 @@ struct dmar_atsr_unit {
 	struct acpi_dmar_header *hdr;	/* ACPI header */
 	struct pci_dev **devices;	/* target devices */
 	int devices_cnt;		/* target device count */
+	u16 segment;			/* PCI domain */
 	u8 include_all:1;		/* include all ports */
 };
 
+#define for_each_atsr_unit(atsr) \
+	list_for_each_entry(atsr, &dmar_atsr_units, list)
+
 int dmar_parse_rmrr_atsr_dev(void);
 extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header);
 extern int dmar_parse_one_atsr(struct acpi_dmar_header *header);
-- 
1.7.7


  parent reply	other threads:[~2012-03-06  7:15 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-06  7:13 [PATCH 00/23] PCI, x86: pci root bus hotplug support Yinghai Lu
2012-03-06  7:13 ` [PATCH 01/23] PCI, sys: Use device_type and attr_groups with pci dev Yinghai Lu
2012-03-06  7:13 ` [PATCH 02/23] PCI, sysfs: create rescan_bridge under /sys/.../pci/devices/... for pci bridges Yinghai Lu
2012-03-09  0:52   ` Bjorn Helgaas
2012-03-09  6:42     ` Yinghai Lu
2012-03-09 17:07       ` Bjorn Helgaas
2012-03-06  7:13 ` [PATCH 03/23] PCI: Add pci_bus_add_single_device() Yinghai Lu
2012-03-06  7:13 ` [PATCH 04/23] PCI: make pci_rescan_bus_bridge_resize use pci_scan_bridge instead Yinghai Lu
2012-03-06  7:13 ` [PATCH 05/23] PCI: clean up rescan_bus_bridge_resize Yinghai Lu
2012-03-06  7:13 ` [PATCH 06/23] PCI: rescan with bus or bridge using callback method too Yinghai Lu
2012-03-09  0:56   ` Bjorn Helgaas
2012-03-09  6:53     ` Yinghai Lu
2012-03-09 17:22       ` Bjorn Helgaas
2012-03-09 19:05         ` Yinghai Lu
2012-03-09 19:11           ` Bjorn Helgaas
2012-03-06  7:13 ` Yinghai Lu [this message]
2012-03-09  1:06   ` [PATCH 07/23] pci, dmar: Update dmar units devices list during hotplug Bjorn Helgaas
2012-03-09  7:06     ` Yinghai Lu
2012-03-09 17:25       ` Bjorn Helgaas
2012-03-09 17:32         ` Yinghai Lu
2012-03-09 17:37           ` Bjorn Helgaas
2012-03-09 18:29             ` Yinghai Lu
2012-03-06  7:13 ` [PATCH 08/23] PNPACPI: Fix device ref leaking in acpi_pnp_match Yinghai Lu
2012-03-07  3:53   ` Bjorn Helgaas
2012-03-06  7:13 ` [PATCH 09/23] IOMMU: Fix tboot force iommu logic Yinghai Lu
2012-03-06  7:13 ` [PATCH 10/23] PCI, x86: Fix non acpi path pci_sysdata leaking with release_fn Yinghai Lu
2012-03-06  7:13 ` [PATCH 11/23] PCI: separate out pci_assign_unassigned_bus_resources() Yinghai Lu
2012-03-09  1:08   ` Bjorn Helgaas
2012-03-06  7:13 ` [PATCH 12/23] PCI: Move back pci_rescan_bus() Yinghai Lu
2012-03-06  7:13 ` [PATCH 13/23] PCI: move pci_stop_and_remove_behind_bridge down Yinghai Lu
2012-03-06  7:13 ` [PATCH 14/23] PCI: add __pci_remove_bus_devices() Yinghai Lu
2012-03-09  1:11   ` Bjorn Helgaas
2012-03-09  7:17     ` Yinghai Lu
2012-03-09 17:28       ` Bjorn Helgaas
2012-03-09 19:00         ` Yinghai Lu
2012-03-06  7:13 ` [PATCH 15/23] PCI: add pci_stop_and_remove_bus() Yinghai Lu
2012-03-06  7:13 ` [PATCH 16/23] PCI: add pci bus removal through /sys/.../pci_bus/.../remove Yinghai Lu
2012-03-08  0:03   ` Bjorn Helgaas
2012-03-08  0:53     ` Yinghai Lu
2012-03-08  4:45       ` Bjorn Helgaas
2012-03-08 15:45         ` Greg Kroah-Hartman
2012-03-06  7:13 ` [PATCH 17/23] PCI, ACPI: make acpi_pci_root_remove remove pci root bus too Yinghai Lu
2012-03-06  7:13 ` [PATCH 18/23] PCI, ACPI: add acpi_pci_root_rescan() Yinghai Lu
2012-03-07  4:40   ` Bjorn Helgaas
2012-03-06  7:13 ` [PATCH 19/23] PCI: add /sys/bus/pci/rescan_root Yinghai Lu
2012-03-07  4:31   ` Bjorn Helgaas
2012-03-07  6:37     ` Yinghai Lu
2012-03-06  7:13 ` [PATCH 20/23] PCI: add __pci_scan_root_bus() that can skip bus_add Yinghai Lu
2012-03-06  7:13 ` [PATCH 21/23] x86, PCI: add __pci_scan_root_bus_on_node() " Yinghai Lu
2012-03-06  7:13 ` [PATCH 22/23] x86, PCI: add __pcibios_scan_specific_bus " Yinghai Lu
2012-03-06  7:14 ` [PATCH 23/23] x86, PCI: add pcibios_root_rescan Yinghai Lu
2012-03-06 23:13   ` Bjorn Helgaas
2012-03-07  0:09     ` Yinghai Lu
2012-03-07  3:42       ` Bjorn Helgaas
2012-03-07  3:49       ` Bjorn Helgaas
2012-03-07  6:29         ` Yinghai Lu
2012-03-07 23:32           ` Bjorn Helgaas
2012-03-08  0:58             ` Yinghai Lu
2012-03-08  4:27               ` Bjorn Helgaas
2012-03-08  8:40                 ` Jan Beulich
2012-03-07  4:44 ` [PATCH 00/23] PCI, x86: pci root bus hotplug support Bjorn Helgaas
2012-03-07  6:58   ` Yinghai Lu
2012-03-09  0:43     ` Bjorn Helgaas
2012-03-09  8:19       ` Yinghai Lu
2012-03-09 17:34         ` Bjorn Helgaas
2012-03-09 18:55           ` Yinghai Lu
2012-03-09 19:10             ` Bjorn Helgaas
2012-03-09 19:29               ` Yinghai Lu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1331018040-30725-8-git-send-email-yinghai@kernel.org \
    --to=yinghai@kernel.org \
    --cc=dan.j.williams@intel.com \
    --cc=dwmw2@infradead.org \
    --cc=iommu@lists.linux-foundation.org \
    --cc=jbarnes@virtuousgeek.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=vinod.koul@intel.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).