2014-01-03 00:14:46

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 00/10] IOMMU: irq-remapping and dmar hotplug support

During pci root bus hot-add, ioapic and iommu need to be parsed at first.
Using acpi_pci_driver->add to do that.

The patches need to apply after for-x86-irq

could get from
git://git.kernel.org/pub/scm/linux/kernel/git/yinghai/linux-yinghai.git for-iommu-3.14

-v2: refreshed after 3.13-rc6

Yinghai Lu (10):
IOMMU: Update dmar units devices list during hotplug
IOMMU: Fix tboot force iommu logic
IOMMU: Don't clean handler data before free_irq()
IOMMU: iommu_unique_seq_id()
ACPI: Add acpi_run_dsm()
IOMMU: Separate free_dmar_iommu from free_iommu
IOMMU: Add init_dmar_one()
IOMMU: Add intel_enable_irq_remapping_one()
IOMMU: Add dmar_parse_one_drhd()
IOMMU: Add intel iommu irq-remapping and dmar hotplug support

drivers/acpi/bus.c | 77 +++++++
drivers/acpi/pci_root.c | 2 +
drivers/iommu/dmar.c | 414 ++++++++++++++++++++++++++++++++++--
drivers/iommu/intel-iommu.c | 162 +++++++++++---
drivers/iommu/intel_irq_remapping.c | 167 ++++++++++++++-
include/linux/acpi.h | 10 +
include/linux/dmar.h | 53 ++++-
include/linux/pci-acpi.h | 8 +
8 files changed, 836 insertions(+), 57 deletions(-)

--
1.8.4


2014-01-03 00:07:50

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 05/10] ACPI: Add acpi_run_dsm()

Will need to use that get dmar entries for hotplug

Signed-off-by: Yinghai Lu <[email protected]>
Cc: Len Brown <[email protected]>
Cc: Rafael J. Wysocki <[email protected]>
Cc: [email protected]
---
drivers/acpi/bus.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/acpi.h | 10 +++++++
2 files changed, 87 insertions(+)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index bba9b72..14d18d3 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -283,6 +283,83 @@ out_kfree:
}
EXPORT_SYMBOL(acpi_run_osc);

+static void acpi_print_dsm_error(acpi_handle handle,
+ struct acpi_dsm_context *context, char *error)
+{
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
+
+ if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer)))
+ printk(KERN_DEBUG "%s for idx %x\n", error, context->func_idx);
+ else {
+ printk(KERN_DEBUG "%s:%s for idx %x\n",
+ (char *)buffer.pointer, error, context->func_idx);
+ kfree(buffer.pointer);
+ }
+}
+
+acpi_status acpi_run_dsm(acpi_handle handle, struct acpi_dsm_context *context)
+{
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[4];
+ union acpi_object *out_obj;
+ u8 uuid[16];
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ if (!context)
+ return AE_ERROR;
+ if (ACPI_FAILURE(acpi_str_to_uuid(context->uuid_str, uuid)))
+ return AE_ERROR;
+ context->ret.length = ACPI_ALLOCATE_BUFFER;
+ context->ret.pointer = NULL;
+
+ /* Setting up input parameters */
+ input.count = 4;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = 16;
+ in_params[0].buffer.pointer = uuid;
+ in_params[1].type = ACPI_TYPE_INTEGER;
+ in_params[1].integer.value = context->rev;
+ in_params[2].type = ACPI_TYPE_INTEGER;
+ in_params[2].integer.value = context->func_idx;
+ in_params[3].type = ACPI_TYPE_PACKAGE;
+ in_params[3].package.count = context->func_argc;
+ in_params[3].package.elements = context->func_argv;
+
+ status = acpi_evaluate_object(handle, "_DSM", &input, &output);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ if (!output.length)
+ return AE_NULL_OBJECT;
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ acpi_print_dsm_error(handle, context,
+ "_DSM evaluation returned wrong type");
+ status = AE_TYPE;
+ goto out_kfree;
+ }
+
+ context->ret.length = out_obj->buffer.length;
+ context->ret.pointer = kmalloc(context->ret.length, GFP_KERNEL);
+ if (!context->ret.pointer) {
+ status = AE_NO_MEMORY;
+ goto out_kfree;
+ }
+ memcpy(context->ret.pointer, out_obj->buffer.pointer,
+ context->ret.length);
+ status = AE_OK;
+
+out_kfree:
+ kfree(output.pointer);
+ if (status != AE_OK)
+ context->ret.pointer = NULL;
+ return status;
+}
+EXPORT_SYMBOL(acpi_run_dsm);
+
bool osc_sb_apei_support_acked;
static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
static void acpi_bus_osc_support(void)
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 762980a..02daa9c 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -323,6 +323,16 @@ void __init acpi_nvs_nosave(void);
void __init acpi_nvs_nosave_s3(void);
#endif /* CONFIG_PM_SLEEP */

+struct acpi_dsm_context {
+ char *uuid_str; /* uuid string */
+ int rev;
+ int func_idx; /* function index */
+ int func_argc;
+ union acpi_object *func_argv;
+ struct acpi_buffer ret; /* free by caller if success */
+};
+acpi_status acpi_run_dsm(acpi_handle handle, struct acpi_dsm_context *context);
+
struct acpi_osc_context {
char *uuid_str; /* UUID string */
int rev;
--
1.8.4

2014-01-03 00:08:24

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 01/10] IOMMU: Update dmar units devices list during hotplug

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 have right iommu for the new dev and will not get DMAR
error anymore.

-v2: add pci_dev_put/pci_dev_get to make refcount consistent.
-v3: fix one left over CONFIG_DMAR
-v4: pass pci_dev *dev in save_dev_dmaru()/get_dev_dmaru() according to Bjorn.
-v5: fix case only have intr-remap enabled.
-v6: skip handling of vf adding/removing.

Signed-off-by: Yinghai Lu <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: Vinod Koul <[email protected]>
Cc: Dan Williams <[email protected]>
Cc: Donald Dutile <[email protected]>
Cc: [email protected]
---
drivers/iommu/dmar.c | 138 ++++++++++++++++++++++++++++++++++++
drivers/iommu/intel-iommu.c | 32 +++++----
drivers/iommu/intel_irq_remapping.c | 33 ++++++++-
include/linux/dmar.h | 10 +++
4 files changed, 200 insertions(+), 13 deletions(-)

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index f4eaa50..a14867c 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -450,6 +450,144 @@ fail:
return ret;
}

+struct dev_dmaru {
+ struct list_head list;
+ void *dmaru;
+ int index;
+ int segment;
+ unsigned char bus;
+ unsigned int devfn;
+};
+
+static int save_dev_dmaru(struct pci_dev *dev, void *dmaru,
+ int index, struct list_head *lh)
+{
+ struct dev_dmaru *m;
+
+ m = kzalloc(sizeof(*m), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->segment = pci_domain_nr(dev->bus);
+ m->bus = dev->bus->number;
+ m->devfn = dev->devfn;
+ m->dmaru = dmaru;
+ m->index = index;
+
+ list_add(&m->list, lh);
+
+ return 0;
+}
+static void *get_dev_dmaru(struct pci_dev *dev, int *index,
+ struct list_head *lh)
+{
+ int segment = pci_domain_nr(dev->bus);
+ unsigned char bus = dev->bus->number;
+ unsigned int devfn = dev->devfn;
+ 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);
+void remove_dev_from_drhd(struct pci_dev *dev)
+{
+ struct dmar_drhd_unit *drhd = NULL;
+ int segment = pci_domain_nr(dev->bus);
+ int i;
+
+ if (dev->is_virtfn)
+ return;
+
+ 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(dev, drhd, i,
+ &saved_dev_drhd_list);
+ /* always remove it */
+ pci_dev_put(dev);
+ drhd->devices[i] = NULL;
+ return;
+ }
+ }
+ }
+}
+void restore_dev_to_drhd(struct pci_dev *dev)
+{
+ struct dmar_drhd_unit *drhd = NULL;
+ int i;
+
+ if (dev->is_virtfn)
+ return;
+
+ /* find the stored drhd */
+ drhd = get_dev_dmaru(dev, &i, &saved_dev_drhd_list);
+ /* restore that into drhd */
+ if (drhd)
+ drhd->devices[i] = pci_dev_get(dev);
+}
+
+#ifdef CONFIG_INTEL_IOMMU
+static LIST_HEAD(saved_dev_atsr_list);
+void remove_dev_from_atsr(struct pci_dev *dev)
+{
+ struct dmar_atsr_unit *atsr = NULL;
+ int segment = pci_domain_nr(dev->bus);
+ int i;
+
+ if (dev->is_virtfn)
+ return;
+
+ 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(dev, atsr, i,
+ &saved_dev_atsr_list);
+ /* always remove it */
+ pci_dev_put(dev);
+ atsr->devices[i] = NULL;
+ return;
+ }
+ }
+ }
+}
+void restore_dev_to_atsr(struct pci_dev *dev)
+{
+ struct dmar_atsr_unit *atsr = NULL;
+ int i;
+
+ if (dev->is_virtfn)
+ return;
+
+ /* find the stored atsr */
+ atsr = get_dev_dmaru(dev, &i, &saved_dev_atsr_list);
+ /* restore that into atsr */
+ if (atsr)
+ atsr->devices[i] = pci_dev_get(dev);
+}
+#endif /* CONFIG_INTEL_IOMMU */

int __init dmar_table_init(void)
{
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 43b9bfe..a3eb689 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3528,7 +3528,7 @@ rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
return ret;
}

-static LIST_HEAD(dmar_atsr_units);
+LIST_HEAD(dmar_atsr_units);

int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
{
@@ -3542,6 +3542,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);

@@ -3573,16 +3574,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;

@@ -3642,20 +3640,30 @@ 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))
- return 0;
+ switch (action) {
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ if (iommu_no_mapping(dev) || iommu_pass_through)
+ break;

- domain = find_domain(pdev);
- if (!domain)
- return 0;
+ domain = find_domain(pdev);
+ if (!domain)
+ break;

- 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);
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ 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/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 07ab6e8..8471f40 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -807,12 +807,43 @@ int __init parse_ioapics_under_ir(void)
return 1;
}

+static int device_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ switch (action) {
+ case BUS_NOTIFY_DEL_DEVICE:
+ remove_dev_from_drhd(pdev);
+ break;
+ case BUS_NOTIFY_ADD_DEVICE:
+ restore_dev_to_drhd(pdev);
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block device_nb = {
+ .notifier_call = device_notifier,
+};
+
int __init ir_dev_scope_init(void)
{
+ int ret;
+
if (!irq_remapping_enabled)
return 0;

- return dmar_dev_scope_init();
+ ret = dmar_dev_scope_init();
+ if (ret < 0)
+ return ret;
+
+ if (intel_iommu_enabled != 1)
+ bus_register_notifier(&pci_bus_type, &device_nb);
+
+ return ret;
}
rootfs_initcall(ir_dev_scope_init);

diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index b029d1a..0a3dd71 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -131,6 +131,11 @@ extern int dmar_set_interrupt(struct intel_iommu *iommu);
extern irqreturn_t dmar_fault(int irq, void *dev_id);
extern int arch_setup_dmar_msi(unsigned int irq);

+void remove_dev_from_drhd(struct pci_dev *dev);
+void restore_dev_to_drhd(struct pci_dev *dev);
+void remove_dev_from_atsr(struct pci_dev *dev);
+void restore_dev_to_atsr(struct pci_dev *dev);
+
#ifdef CONFIG_INTEL_IOMMU
extern int iommu_detected, no_iommu;
extern struct list_head dmar_rmrr_units;
@@ -146,14 +151,19 @@ struct dmar_rmrr_unit {
#define for_each_rmrr_units(rmrr) \
list_for_each_entry(rmrr, &dmar_rmrr_units, list)

+extern struct list_head dmar_atsr_units;
struct dmar_atsr_unit {
struct list_head list; /* list of ATSR units */
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.8.4

2014-01-03 00:08:22

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 07/10] IOMMU: Add init_dmar_one()

Will need that for hot added intel iommu

Signed-off-by: Yinghai Lu <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: Donald Dutile <[email protected]>
Cc: [email protected]
---
drivers/iommu/dmar.c | 4 +-
drivers/iommu/intel-iommu.c | 122 ++++++++++++++++++++++++++++++++++++++++----
2 files changed, 114 insertions(+), 12 deletions(-)

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 20e5727..78e75ad 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -64,7 +64,7 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
list_add(&drhd->list, &dmar_drhd_units);
}

-static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
+static int dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
struct pci_dev **dev, u16 segment)
{
struct pci_bus *bus;
@@ -115,7 +115,7 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
return 0;
}

-int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
+int dmar_parse_dev_scope(void *start, void *end, int *cnt,
struct pci_dev ***devices, u16 segment)
{
struct acpi_dmar_device_scope *scope;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index c60d4ed..e52ce1c 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2462,19 +2462,12 @@ static int __init init_dmars(void)
* initialize and program root entry to not present
* endfor
*/
- for_each_drhd_unit(drhd) {
/*
* lock not needed as this is only incremented in the single
* threaded kernel __init code path all other access are read
* only
*/
- if (g_num_of_iommus < IOMMU_UNITS_SUPPORTED) {
- g_num_of_iommus++;
- continue;
- }
- printk_once(KERN_ERR "intel-iommu: exceeded %d IOMMUs\n",
- IOMMU_UNITS_SUPPORTED);
- }
+ g_num_of_iommus = IOMMU_UNITS_SUPPORTED;

g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *),
GFP_KERNEL);
@@ -2675,6 +2668,108 @@ error:
return ret;
}

+int init_dmar_one(struct dmar_drhd_unit *drhd)
+{
+ struct intel_iommu *iommu;
+ int ret;
+
+ /*
+ * for each drhd
+ * allocate root
+ * initialize and program root entry to not present
+ * endfor
+ */
+
+ if (drhd->ignored)
+ return 0;
+
+ iommu = drhd->iommu;
+ g_iommus[iommu->seq_id] = iommu;
+
+ ret = iommu_init_domains(iommu);
+ if (ret)
+ goto error;
+
+ /*
+ * TBD:
+ * we could share the same root & context tables
+ * among all IOMMU's. Need to Split it later.
+ */
+ ret = iommu_alloc_root_entry(iommu);
+ if (ret) {
+ pr_err("IOMMU: allocate root entry failed\n");
+ goto error;
+ }
+
+ /*
+ * Start from the sane iommu hardware state.
+ */
+ /*
+ * If the queued invalidation is already initialized by us
+ * (for example, while enabling interrupt-remapping) then
+ * we got the things already rolling from a sane state.
+ */
+ if (!iommu->qi) {
+ /*
+ * Clear any previous faults.
+ */
+ dmar_fault(-1, iommu);
+ /*
+ * Disable queued invalidation if supported and already enabled
+ * before OS handover.
+ */
+ dmar_disable_qi(iommu);
+ }
+
+ if (dmar_enable_qi(iommu)) {
+ /*
+ * Queued Invalidate not enabled, use Register Based
+ * Invalidate
+ */
+ iommu->flush.flush_context = __iommu_flush_context;
+ iommu->flush.flush_iotlb = __iommu_flush_iotlb;
+ pr_info("IOMMU %d 0x%Lx: using Register based invalidation\n",
+ iommu->seq_id, (unsigned long long)drhd->reg_base_addr);
+ } else {
+ iommu->flush.flush_context = qi_flush_context;
+ iommu->flush.flush_iotlb = qi_flush_iotlb;
+ pr_info("IOMMU %d 0x%Lx: using Queued invalidation\n",
+ iommu->seq_id, (unsigned long long)drhd->reg_base_addr);
+ }
+
+ /*
+ * for each drhd
+ * enable fault log
+ * global invalidate context cache
+ * global invalidate iotlb
+ * enable translation
+ */
+ iommu_flush_write_buffer(iommu);
+
+ ret = dmar_set_interrupt(iommu);
+ if (ret)
+ goto error;
+
+ iommu_set_root_entry(iommu);
+
+ iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
+ iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
+
+ ret = iommu_enable_translation(iommu);
+ if (ret)
+ goto error;
+
+ iommu_disable_protect_mem_regions(iommu);
+
+ return 0;
+error:
+ free_dmar_iommu(iommu);
+ free_iommu(iommu);
+ drhd->iommu = NULL;
+ return ret;
+}
+
+
/* This takes a number of _MM_ pages, not VTD pages */
static struct iova *intel_alloc_iova(struct device *dev,
struct dmar_domain *domain,
@@ -3530,7 +3625,8 @@ rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)

LIST_HEAD(dmar_atsr_units);

-int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
+int __dmar_parse_one_atsr(struct acpi_dmar_header *hdr,
+ struct dmar_atsr_unit **patsru)
{
struct acpi_dmar_atsr *atsr;
struct dmar_atsr_unit *atsru;
@@ -3545,11 +3641,17 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
atsru->segment = atsr->segment;

list_add(&atsru->list, &dmar_atsr_units);
+ if (patsru)
+ *patsru = atsru;

return 0;
}
+int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
+{
+ return __dmar_parse_one_atsr(hdr, NULL);
+}

-static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
+int atsr_parse_dev(struct dmar_atsr_unit *atsru)
{
int rc;
struct acpi_dmar_atsr *atsr;
--
1.8.4

2014-01-03 00:08:21

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 03/10] IOMMU: Don't clean handler data before free_irq()

need that in __free_irq.

Signed-off-by: Yinghai Lu <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: [email protected]
---
drivers/iommu/intel-iommu.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 865a65c..9751ee7 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1315,7 +1315,6 @@ void free_dmar_iommu(struct intel_iommu *iommu)
iommu_disable_translation(iommu);

if (iommu->irq) {
- irq_set_handler_data(iommu->irq, NULL);
/* This will mask the irq */
free_irq(iommu->irq, iommu);
destroy_irq(iommu->irq);
--
1.8.4

2014-01-03 00:08:19

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 02/10] IOMMU: Fix tboot force iommu logic

Should check dmar_disabled just after tboot_force_iommu.

Otherwise when tboot is not used, and intel_iommu=off, and nointrmap
still get dmar_table_init() called. that will cause some get_device
calling, and it will have some device refcount leaking.

Signed-off-by: Yinghai Lu <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: [email protected]
---
drivers/iommu/intel-iommu.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index a3eb689..865a65c 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3681,6 +3681,9 @@ int __init intel_iommu_init(void)
/* VT-d is required for a TXT/tboot launch, so enforce that */
force_on = tboot_force_iommu();

+ if (no_iommu || dmar_disabled)
+ return -ENODEV;
+
if (dmar_table_init()) {
if (force_on)
panic("tboot: Failed to initialize DMAR table\n");
@@ -3707,9 +3710,6 @@ int __init intel_iommu_init(void)
return -ENODEV;
}

- if (no_iommu || dmar_disabled)
- return -ENODEV;
-
if (iommu_init_mempool()) {
if (force_on)
panic("tboot: Failed to initialize iommu memory\n");
--
1.8.4

2014-01-03 00:08:16

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 06/10] IOMMU: Separate free_dmar_iommu from free_iommu

We will need separate two calling later for iommu_remove.

Signed-off-by: Yinghai Lu <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: Donald Dutile <[email protected]>
---
drivers/iommu/dmar.c | 2 --
drivers/iommu/intel-iommu.c | 1 +
2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index f5e0718..20e5727 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -876,8 +876,6 @@ void free_iommu(struct intel_iommu *iommu)
if (!iommu)
return;

- free_dmar_iommu(iommu);
-
if (iommu->reg)
unmap_iommu(iommu);

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 9751ee7..c60d4ed 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2668,6 +2668,7 @@ error:
if (drhd->ignored)
continue;
iommu = drhd->iommu;
+ free_dmar_iommu(iommu);
free_iommu(iommu);
}
kfree(g_iommus);
--
1.8.4

2014-01-03 00:07:59

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 10/10] IOMMU: Add intel iommu irq-remapping and dmar hotplug support

during hostbridge hotadd/hotremove, we call acpi_pci_iommu_add/remove in
acpi_pci_root_add/remove.

For add, need to after ioapic add.

Run dsm in acpi root device to get iommu entries, and init dmar/irq_remapping

Signed-off-by: Yinghai Lu <[email protected]>
Cc: Len Brown <[email protected]>
Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Bjorn Helgaas <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: Donald Dutile <[email protected]>
Cc: [email protected]
Cc: [email protected]
---
drivers/acpi/pci_root.c | 2 +
drivers/iommu/dmar.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci-acpi.h | 8 ++
3 files changed, 211 insertions(+)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index e666be3..dd82d8b 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -600,6 +600,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
}

acpi_pci_ioapic_add(root);
+ acpi_pci_iommu_add(root);

pci_bus_add_devices(root->bus);
return 1;
@@ -615,6 +616,7 @@ static void acpi_pci_root_remove(struct acpi_device *device)

pci_stop_root_bus(root->bus);

+ acpi_pci_iommu_remove(root);
acpi_pci_ioapic_remove(root);

device_set_run_wake(root->bus->bridge, false);
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 31d7942..c74f112 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -38,6 +38,7 @@
#include <linux/tboot.h>
#include <linux/dmi.h>
#include <linux/slab.h>
+#include <acpi/acpi.h>
#include <asm/irq_remapping.h>
#include <asm/iommu_table.h>

@@ -1548,3 +1549,203 @@ int __init dmar_ir_support(void)
return dmar->flags & 0x1;
}
IOMMU_INIT_POST(detect_intel_iommu);
+
+static u8 dmar_uuid_str[] = "D8C1A3A6-BE9B-4C9B-91BF-C3CB81FC5DAF";
+
+static int acpi_dmar_dsm_support(acpi_handle handle)
+{
+ int support = 0;
+ struct acpi_dsm_context context = {
+ .uuid_str = dmar_uuid_str,
+ .rev = 1,
+ .func_idx = 0,
+ };
+
+ if (ACPI_SUCCESS(acpi_run_dsm(handle, &context))) {
+ u32 *ret = context.ret.pointer;
+ support = ret[0];
+ kfree(context.ret.pointer);
+ }
+
+ if (support & 1)
+ return support & 0x0e;
+
+ return 0;
+}
+
+static void *acpi_dmar_dsm_run(acpi_handle handle, int idx)
+{
+ struct acpi_dsm_context context = {
+ .uuid_str = dmar_uuid_str,
+ .rev = 1,
+ .func_idx = idx,
+ };
+
+ if (ACPI_SUCCESS(acpi_run_dsm(handle, &context)))
+ return context.ret.pointer;
+
+ return NULL;
+}
+
+static void handle_iommu_add(acpi_handle handle, void **d, void **a)
+{
+ int support;
+ struct acpi_dmar_header *header;
+ struct dmar_drhd_unit *dmaru = NULL;
+ struct dmar_atsr_unit *atsru = NULL;
+
+ *d = NULL;
+ *a = NULL;
+
+ support = acpi_dmar_dsm_support(handle);
+
+ if (!support)
+ return;
+
+ /* DRHD */
+ if (support & (1<<1)) {
+ header = acpi_dmar_dsm_run(handle, 1);
+ dmar_table_print_dmar_entry(header);
+ __dmar_parse_one_drhd(header, &dmaru);
+ }
+
+ if (!dmaru)
+ return;
+
+ /* ATSR */
+ if (support & (1<<2)) {
+ header = acpi_dmar_dsm_run(handle, 2);
+ dmar_table_print_dmar_entry(header);
+ __dmar_parse_one_atsr(header, &atsru);
+ }
+
+ /* RHSA */
+ if (support & (1<<3)) {
+ header = acpi_dmar_dsm_run(handle, 3);
+ dmar_table_print_dmar_entry(header);
+ dmar_parse_one_rhsa(header);
+ kfree(header);
+ }
+
+ /*
+ * only need to init intr_remap and dmar for hot-add ones
+ * after enable_IR() or pci_iommu_init
+ */
+ /*
+ * TODO: handle parsing failure for pre-installed hotplug one
+ * Could make every parse_one duplicate the entry table?
+ */
+ if (irq_remapping_enabled == 1)
+ intel_enable_irq_remapping_one(dmaru);
+ if (irq_remapping_enabled == 1 || intel_iommu_enabled == 1)
+ dmar_parse_dev(dmaru);
+ if (intel_iommu_enabled == 1) {
+ init_dmar_one(dmaru);
+ if (atsru) {
+ header = atsru->hdr;
+ if (atsr_parse_dev(atsru)) {
+ kfree(header);
+ atsru = NULL;
+ }
+ *a = atsru;
+ }
+ }
+ *d = dmaru;
+}
+
+static void handle_iommu_remove(void *drhd, void *atsr)
+{
+ struct dmar_drhd_unit *dmaru = drhd;
+ struct dmar_atsr_unit *atsru = atsr;
+
+ if (!dmaru)
+ return;
+
+ if (irq_remapping_enabled == 1)
+ disable_irq_remapping_one(dmaru);
+ if (intel_iommu_enabled == 1) {
+ free_dmar_iommu(dmaru->iommu);
+ if (atsru) {
+ kfree(atsru->devices);
+ remove_atsru_from_saved_dev_atsru_list(atsru);
+ }
+ }
+ if (irq_remapping_enabled == 1 || intel_iommu_enabled == 1) {
+ kfree(dmaru->devices);
+ remove_dmaru_from_saved_dev_drhd_list(dmaru);
+ }
+ if (atsru) {
+ kfree(atsru->hdr);
+ list_del(&atsru->list);
+ kfree(atsru);
+ }
+ free_iommu(dmaru->iommu);
+ kfree(dmaru->hdr);
+ list_del(&dmaru->list);
+ kfree(dmaru);
+}
+
+struct acpi_pci_iommu {
+ acpi_handle root_handle;
+ void *dmaru;
+ void *atsru;
+ struct list_head list;
+};
+
+static LIST_HEAD(iommu_list);
+static DEFINE_MUTEX(iommu_list_lock);
+
+static acpi_status register_iommu(acpi_handle handle, u32 lvl,
+ void *context, void **rv)
+{
+ acpi_handle root_handle = context;
+ void *dmaru, *atsru;
+ struct acpi_pci_iommu *iommu;
+
+ handle_iommu_add(handle, &dmaru, &atsru);
+ if (!dmaru)
+ return AE_OK;
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu) {
+ pr_err("%s: cannot allocate memory\n", __func__);
+ handle_iommu_remove(dmaru, atsru);
+ return AE_OK;
+ }
+ iommu->root_handle = root_handle;
+ iommu->dmaru = dmaru;
+ iommu->atsru = atsru;
+
+ mutex_lock(&iommu_list_lock);
+ list_add(&iommu->list, &iommu_list);
+ mutex_unlock(&iommu_list_lock);
+
+ return AE_OK;
+}
+
+void acpi_pci_iommu_add(struct acpi_pci_root *root)
+{
+ acpi_status status;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
+ (u32)1, register_iommu, NULL,
+ root->device->handle,
+ NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("%s: register_iommu failure - %d", __func__, status);
+}
+
+void acpi_pci_iommu_remove(struct acpi_pci_root *root)
+{
+ struct acpi_pci_iommu *iommu, *tmp;
+
+ mutex_lock(&iommu_list_lock);
+ list_for_each_entry_safe(iommu, tmp, &iommu_list, list) {
+ if (root->device->handle != iommu->root_handle)
+ continue;
+ list_del(&iommu->list);
+ handle_iommu_remove(iommu->dmaru, iommu->atsru);
+ kfree(iommu);
+ }
+ mutex_unlock(&iommu_list_lock);
+}
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index d2a976a..f00a89f 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -86,4 +86,12 @@ extern bool aer_acpi_firmware_first(void);
static inline bool aer_acpi_firmware_first(void) { return false; }
#endif

+#ifdef CONFIG_DMAR_TABLE
+void acpi_pci_iommu_add(struct acpi_pci_root *root);
+void acpi_pci_iommu_remove(struct acpi_pci_root *root);
+#else
+static inline void acpi_pci_iommu_add(struct acpi_pci_root *root) { }
+static inline void acpi_pci_iommu_remove(struct acpi_pci_root *root) { }
+#endif
+
#endif /* _PCI_ACPI_H_ */
--
1.8.4

2014-01-03 00:07:56

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 09/10] IOMMU: Add dmar_parse_one_drhd()

To parse new hot-added iommu dmar entry table

Signed-off-by: Yinghai Lu <[email protected]>
Cc: Vinod Koul <[email protected]>
Cc: Dan Williams <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: Donald Dutile <[email protected]>
---
drivers/iommu/dmar.c | 51 +++++++++++++++++++++++++++++++++++++++++----------
include/linux/dmar.h | 47 ++++++++++++++++++++++++++++++++++++++---------
2 files changed, 79 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 78e75ad..31d7942 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -167,8 +167,9 @@ int dmar_parse_dev_scope(void *start, void *end, int *cnt,
* structure which uniquely represent one DMA remapping hardware unit
* present in the platform
*/
-static int __init
-dmar_parse_one_drhd(struct acpi_dmar_header *header)
+static int
+__dmar_parse_one_drhd(struct acpi_dmar_header *header,
+ struct dmar_drhd_unit **pdmaru)
{
struct acpi_dmar_hardware_unit *drhd;
struct dmar_drhd_unit *dmaru;
@@ -190,10 +191,18 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
return ret;
}
dmar_register_drhd_unit(dmaru);
+ if (pdmaru)
+ *pdmaru = dmaru;
return 0;
}

-static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
+static int __init
+dmar_parse_one_drhd(struct acpi_dmar_header *header)
+{
+ return __dmar_parse_one_drhd(header, NULL);
+}
+
+static int dmar_parse_dev(struct dmar_drhd_unit *dmaru)
{
struct acpi_dmar_hardware_unit *drhd;
int ret = 0;
@@ -214,10 +223,10 @@ static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
return ret;
}

-#ifdef CONFIG_ACPI_NUMA
-static int __init
+static int
dmar_parse_one_rhsa(struct acpi_dmar_header *header)
{
+#ifdef CONFIG_ACPI_NUMA
struct acpi_dmar_rhsa *rhsa;
struct dmar_drhd_unit *drhd;

@@ -241,12 +250,11 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header)
dmi_get_system_info(DMI_BIOS_VERSION),
dmi_get_system_info(DMI_PRODUCT_VERSION));

+#endif
return 0;
}
-#endif

-static void __init
-dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
+static void dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
{
struct acpi_dmar_hardware_unit *drhd;
struct acpi_dmar_reserved_memory *rmrr;
@@ -358,9 +366,7 @@ parse_dmar_table(void)
ret = dmar_parse_one_atsr(entry_header);
break;
case ACPI_DMAR_HARDWARE_AFFINITY:
-#ifdef CONFIG_ACPI_NUMA
ret = dmar_parse_one_rhsa(entry_header);
-#endif
break;
default:
pr_warn("Unknown DMAR structure type %d\n",
@@ -545,6 +551,23 @@ void restore_dev_to_drhd(struct pci_dev *dev)
drhd->devices[i] = pci_dev_get(dev);
}

+static void remove_dmaru_from_saved_list(void *drhd,
+ struct list_head *lh)
+{
+ struct dev_dmaru *m, *n;
+
+ list_for_each_entry_safe(m, n, lh, list) {
+ if (m->dmaru == drhd) {
+ list_del(&m->list);
+ kfree(m);
+ }
+ }
+}
+static void remove_dmaru_from_saved_dev_drhd_list(void *drhd)
+{
+ remove_dmaru_from_saved_list(drhd, &saved_dev_drhd_list);
+}
+
#ifdef CONFIG_INTEL_IOMMU
static LIST_HEAD(saved_dev_atsr_list);
void remove_dev_from_atsr(struct pci_dev *dev)
@@ -587,6 +610,14 @@ void restore_dev_to_atsr(struct pci_dev *dev)
if (atsr)
atsr->devices[i] = pci_dev_get(dev);
}
+static void remove_atsru_from_saved_dev_atsru_list(void *atsr)
+{
+ remove_dmaru_from_saved_list(atsr, &saved_dev_atsr_list);
+}
+#else
+static void remove_atsru_from_saved_dev_atsru_list(struct dmar_atsr_unit *atsr)
+{
+}
#endif /* CONFIG_INTEL_IOMMU */

int __init dmar_table_init(void)
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index 0a3dd71..81e8678 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -33,6 +33,7 @@ struct acpi_dmar_header;
#define DMAR_X2APIC_OPT_OUT 0x2

struct intel_iommu;
+struct dmar_drhd_unit;
#ifdef CONFIG_DMAR_TABLE
extern struct acpi_table_header *dmar_tbl;
struct dmar_drhd_unit {
@@ -136,6 +137,15 @@ void restore_dev_to_drhd(struct pci_dev *dev);
void remove_dev_from_atsr(struct pci_dev *dev);
void restore_dev_to_atsr(struct pci_dev *dev);

+struct dmar_atsr_unit {
+ struct list_head list; /* list of ATSR units */
+ 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 */
+};
+
#ifdef CONFIG_INTEL_IOMMU
extern int iommu_detected, no_iommu;
extern struct list_head dmar_rmrr_units;
@@ -152,24 +162,19 @@ struct dmar_rmrr_unit {
list_for_each_entry(rmrr, &dmar_rmrr_units, list)

extern struct list_head dmar_atsr_units;
-struct dmar_atsr_unit {
- struct list_head list; /* list of ATSR units */
- 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);
+int __dmar_parse_one_atsr(struct acpi_dmar_header *header,
+ struct dmar_atsr_unit **patsru);
+int atsr_parse_dev(struct dmar_atsr_unit *atsru);
extern int dmar_parse_dev_scope(void *start, void *end, int *cnt,
struct pci_dev ***devices, u16 segment);
extern int intel_iommu_init(void);
+int init_dmar_one(struct dmar_drhd_unit *drhd);
#else /* !CONFIG_INTEL_IOMMU: */
static inline int intel_iommu_init(void) { return -ENODEV; }
static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header)
@@ -180,10 +185,34 @@ static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
{
return 0;
}
+static inline int __dmar_parse_one_atsr(struct acpi_dmar_header *header,
+ struct dmar_atsr_unit **patsru)
+{
+ return 0;
+}
+static inline int atsr_parse_dev(struct dmar_atsr_unit *atsru)
+{
+ return 0;
+}
static inline int dmar_parse_rmrr_atsr_dev(void)
{
return 0;
}
+static inline int init_dmar_one(struct dmar_drhd_unit *drhd)
+{
+ return 0;
+}
#endif /* CONFIG_INTEL_IOMMU */

+#ifdef CONFIG_IRQ_REMAP
+void disable_irq_remapping_one(struct dmar_drhd_unit *drhd);
+int intel_enable_irq_remapping_one(struct dmar_drhd_unit *drhd);
+#else
+static inline void disable_irq_remapping_one(struct dmar_drhd_unit *drhd) { }
+static inline int intel_enable_irq_remapping_one(struct dmar_drhd_unit *drhd)
+{
+ return 0;
+}
+#endif
+
#endif /* __DMAR_H__ */
--
1.8.4

2014-01-03 00:12:45

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 08/10] IOMMU: Add intel_enable_irq_remapping_one()

Will need that for hot-added iommu interrupt remapping suppor.

Signed-off-by: Yinghai <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/iommu/intel_irq_remapping.c | 134 ++++++++++++++++++++++++++++++++++--
1 file changed, 127 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 8471f40..41d03d7 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -218,7 +218,7 @@ static struct intel_iommu *map_ioapic_to_ir(int apic)
{
int i;

- for (i = 0; i < MAX_IO_APICS; i++)
+ for (i = 0; i < ir_ioapic_num; i++)
if (ir_ioapic[i].id == apic)
return ir_ioapic[i].iommu;
return NULL;
@@ -324,7 +324,7 @@ static int set_ioapic_sid(struct irte *irte, int apic)
if (!irte)
return -1;

- for (i = 0; i < MAX_IO_APICS; i++) {
+ for (i = 0; i < ir_ioapic_num; i++) {
if (ir_ioapic[i].id == apic) {
sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
break;
@@ -704,6 +704,7 @@ static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
struct acpi_dmar_pci_path *path;
u8 bus;
int count;
+ int i;

bus = scope->bus;
path = (struct acpi_dmar_pci_path *)(scope + 1);
@@ -720,11 +721,16 @@ static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
path++;
}

- ir_ioapic[ir_ioapic_num].bus = bus;
- ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->device, path->function);
- ir_ioapic[ir_ioapic_num].iommu = iommu;
- ir_ioapic[ir_ioapic_num].id = scope->enumeration_id;
- ir_ioapic_num++;
+ for (i = 0; i < ir_ioapic_num; i++)
+ if (!ir_ioapic[i].iommu)
+ break;
+
+ ir_ioapic[i].bus = bus;
+ ir_ioapic[i].devfn = PCI_DEVFN(path->device, path->function);
+ ir_ioapic[i].iommu = iommu;
+ ir_ioapic[i].id = scope->enumeration_id;
+ if (i == ir_ioapic_num)
+ ir_ioapic_num++;
}

static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
@@ -1139,6 +1145,120 @@ static int intel_setup_hpet_msi(unsigned int irq, unsigned int id)
return 0;
}

+void disable_irq_remapping_one(struct dmar_drhd_unit *drhd)
+{
+ struct intel_iommu *iommu = drhd->iommu;
+ int i;
+
+ /*
+ * Disable Interrupt-remapping for the DRHD's now.
+ */
+ if (!ecap_ir_support(iommu->ecap))
+ return;
+
+ iommu_disable_irq_remapping(iommu);
+
+ /* remove it from ir_ioapic */
+ for (i = 0; i < ir_ioapic_num; i++)
+ if (ir_ioapic[i].iommu == iommu) {
+ ir_ioapic[i].iommu = NULL;
+ ir_ioapic[i].id = 0xff;
+ }
+}
+
+static int parse_ioapics_under_ir_one(struct dmar_drhd_unit *drhd)
+{
+ int ir_supported = 0;
+
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (ecap_ir_support(iommu->ecap)) {
+ if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
+ return -1;
+
+ ir_supported = 1;
+ }
+
+ return ir_supported;
+}
+
+int intel_enable_irq_remapping_one(struct dmar_drhd_unit *drhd)
+{
+ int setup = 0;
+ int eim = 1;
+ int ret;
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (parse_ioapics_under_ir_one(drhd) != 1) {
+ pr_info("Not enable interrupt remapping\n");
+ return -1;
+ }
+
+ /*
+ * If the queued invalidation is already initialized,
+ * shouldn't disable it.
+ */
+ if (!iommu->qi) {
+ /*
+ * Clear previous faults.
+ */
+ dmar_fault(-1, iommu);
+
+ /*
+ * Disable intr remapping and queued invalidation, if already
+ * enabled prior to OS handover.
+ */
+ iommu_disable_irq_remapping(iommu);
+
+ dmar_disable_qi(iommu);
+ }
+
+ /*
+ * check for the Interrupt-remapping support
+ */
+
+ if (ecap_ir_support(iommu->ecap))
+ if (eim && !ecap_eim_support(iommu->ecap)) {
+ pr_info("DRHD %Lx: EIM not supported by DRHD, ecap %Lx\n",
+ drhd->reg_base_addr, iommu->ecap);
+ return -1;
+ }
+
+ /*
+ * Enable queued invalidation for the DRHD's.
+ */
+ ret = dmar_enable_qi(iommu);
+
+ if (ret) {
+ pr_err("DRHD %Lx: failed to enable queued, invalidation, ecap %Lx, ret %d\n",
+ drhd->reg_base_addr, iommu->ecap, ret);
+ return -1;
+ }
+
+ /*
+ * Setup Interrupt-remapping for the DRHD's now.
+ */
+ if (ecap_ir_support(iommu->ecap)) {
+ if (intel_setup_irq_remapping(iommu, eim))
+ goto error;
+
+ setup = 1;
+ }
+
+ if (!setup)
+ goto error;
+
+ pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");
+
+ return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
+
+error:
+ /*
+ * handle error condition gracefully here!
+ */
+ return -1;
+}
+
struct irq_remap_ops intel_irq_remap_ops = {
.supported = intel_irq_remapping_supported,
.prepare = dmar_table_init,
--
1.8.4

2014-01-03 00:13:20

by Yinghai Lu

[permalink] [raw]
Subject: [PATCH v2 04/10] IOMMU: iommu_unique_seq_id()

So for hot-remove/hot-add will reuse seq_id.

Signed-off-by: Yinghai Lu <[email protected]>
Cc: Joerg Roedel <[email protected]>
Cc: Donald Dutile <[email protected]>
---
drivers/iommu/dmar.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index a14867c..f5e0718 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -785,11 +785,22 @@ out:
return err;
}

+static DECLARE_BITMAP(iommu_allocated, 1024);
+
+static int iommu_unique_seq_id(void)
+{
+ int id;
+
+ id = find_first_zero_bit(iommu_allocated, 1024);
+ __set_bit(id, iommu_allocated);
+
+ return id;
+}
+
int alloc_iommu(struct dmar_drhd_unit *drhd)
{
struct intel_iommu *iommu;
u32 ver, sts;
- static int iommu_allocated = 0;
int agaw = 0;
int msagaw = 0;
int err;
@@ -803,7 +814,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
if (!iommu)
return -ENOMEM;

- iommu->seq_id = iommu_allocated++;
+ iommu->seq_id = iommu_unique_seq_id();
sprintf (iommu->name, "dmar%d", iommu->seq_id);

err = map_iommu(iommu, drhd->reg_base_addr);
@@ -855,6 +866,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
err_unmap:
unmap_iommu(iommu);
error:
+ __clear_bit(iommu->seq_id, iommu_allocated);
kfree(iommu);
return err;
}
@@ -869,6 +881,8 @@ void free_iommu(struct intel_iommu *iommu)
if (iommu->reg)
unmap_iommu(iommu);

+ __clear_bit(iommu->seq_id, iommu_allocated);
+
kfree(iommu);
}

--
1.8.4

2014-01-06 02:25:40

by Yijing Wang

[permalink] [raw]
Subject: Re: [PATCH v2 10/10] IOMMU: Add intel iommu irq-remapping and dmar hotplug support

Hi Yinghai,
There is a problem, consider that static DMAR table contains the hotplug CPU DMAR info(include drhd and atsr).
So, if we enable irq remapping, all DMAR drhd unit and atsr unit will be parsed during boot path. When IOH hub object in
DSDT was enumerated, we found device that support _DSM, by _DSM, we get the hotplug DMAR info(which has been initialized during irq remapping boot path).
We cannot identify the DMAR info in _DSM whether has been initialized.

For DRHD, I think we can use reg_base_addr to identify whether has been registered.
But for ATSR, maybe it's a Thorny issue.

Thanks!
Yijing.

On 2014/1/3 8:08, Yinghai Lu wrote:
> during hostbridge hotadd/hotremove, we call acpi_pci_iommu_add/remove in
> acpi_pci_root_add/remove.
>
> For add, need to after ioapic add.
>
> Run dsm in acpi root device to get iommu entries, and init dmar/irq_remapping
>
> Signed-off-by: Yinghai Lu <[email protected]>
> Cc: Len Brown <[email protected]>
> Cc: "Rafael J. Wysocki" <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>
> Cc: Joerg Roedel <[email protected]>
> Cc: Donald Dutile <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> ---
> drivers/acpi/pci_root.c | 2 +
> drivers/iommu/dmar.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci-acpi.h | 8 ++
> 3 files changed, 211 insertions(+)
>
> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
> index e666be3..dd82d8b 100644
> --- a/drivers/acpi/pci_root.c
> +++ b/drivers/acpi/pci_root.c
> @@ -600,6 +600,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
> }
>
> acpi_pci_ioapic_add(root);
> + acpi_pci_iommu_add(root);
>
> pci_bus_add_devices(root->bus);
> return 1;
> @@ -615,6 +616,7 @@ static void acpi_pci_root_remove(struct acpi_device *device)
>
> pci_stop_root_bus(root->bus);
>
> + acpi_pci_iommu_remove(root);
> acpi_pci_ioapic_remove(root);
>
> device_set_run_wake(root->bus->bridge, false);
> diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
> index 31d7942..c74f112 100644
> --- a/drivers/iommu/dmar.c
> +++ b/drivers/iommu/dmar.c
> @@ -38,6 +38,7 @@
> #include <linux/tboot.h>
> #include <linux/dmi.h>
> #include <linux/slab.h>
> +#include <acpi/acpi.h>
> #include <asm/irq_remapping.h>
> #include <asm/iommu_table.h>
>
> @@ -1548,3 +1549,203 @@ int __init dmar_ir_support(void)
> return dmar->flags & 0x1;
> }
> IOMMU_INIT_POST(detect_intel_iommu);
> +
> +static u8 dmar_uuid_str[] = "D8C1A3A6-BE9B-4C9B-91BF-C3CB81FC5DAF";
> +
> +static int acpi_dmar_dsm_support(acpi_handle handle)
> +{
> + int support = 0;
> + struct acpi_dsm_context context = {
> + .uuid_str = dmar_uuid_str,
> + .rev = 1,
> + .func_idx = 0,
> + };
> +
> + if (ACPI_SUCCESS(acpi_run_dsm(handle, &context))) {
> + u32 *ret = context.ret.pointer;
> + support = ret[0];
> + kfree(context.ret.pointer);
> + }
> +
> + if (support & 1)
> + return support & 0x0e;
> +
> + return 0;
> +}
> +
> +static void *acpi_dmar_dsm_run(acpi_handle handle, int idx)
> +{
> + struct acpi_dsm_context context = {
> + .uuid_str = dmar_uuid_str,
> + .rev = 1,
> + .func_idx = idx,
> + };
> +
> + if (ACPI_SUCCESS(acpi_run_dsm(handle, &context)))
> + return context.ret.pointer;
> +
> + return NULL;
> +}
> +
> +static void handle_iommu_add(acpi_handle handle, void **d, void **a)
> +{
> + int support;
> + struct acpi_dmar_header *header;
> + struct dmar_drhd_unit *dmaru = NULL;
> + struct dmar_atsr_unit *atsru = NULL;
> +
> + *d = NULL;
> + *a = NULL;
> +
> + support = acpi_dmar_dsm_support(handle);
> +
> + if (!support)
> + return;
> +
> + /* DRHD */
> + if (support & (1<<1)) {
> + header = acpi_dmar_dsm_run(handle, 1);
> + dmar_table_print_dmar_entry(header);
> + __dmar_parse_one_drhd(header, &dmaru);
> + }
> +
> + if (!dmaru)
> + return;
> +
> + /* ATSR */
> + if (support & (1<<2)) {
> + header = acpi_dmar_dsm_run(handle, 2);
> + dmar_table_print_dmar_entry(header);
> + __dmar_parse_one_atsr(header, &atsru);
> + }
> +
> + /* RHSA */
> + if (support & (1<<3)) {
> + header = acpi_dmar_dsm_run(handle, 3);
> + dmar_table_print_dmar_entry(header);
> + dmar_parse_one_rhsa(header);
> + kfree(header);
> + }
> +
> + /*
> + * only need to init intr_remap and dmar for hot-add ones
> + * after enable_IR() or pci_iommu_init
> + */
> + /*
> + * TODO: handle parsing failure for pre-installed hotplug one
> + * Could make every parse_one duplicate the entry table?
> + */
> + if (irq_remapping_enabled == 1)
> + intel_enable_irq_remapping_one(dmaru);
> + if (irq_remapping_enabled == 1 || intel_iommu_enabled == 1)
> + dmar_parse_dev(dmaru);
> + if (intel_iommu_enabled == 1) {
> + init_dmar_one(dmaru);
> + if (atsru) {
> + header = atsru->hdr;
> + if (atsr_parse_dev(atsru)) {
> + kfree(header);
> + atsru = NULL;
> + }
> + *a = atsru;
> + }
> + }
> + *d = dmaru;
> +}
> +
> +static void handle_iommu_remove(void *drhd, void *atsr)
> +{
> + struct dmar_drhd_unit *dmaru = drhd;
> + struct dmar_atsr_unit *atsru = atsr;
> +
> + if (!dmaru)
> + return;
> +
> + if (irq_remapping_enabled == 1)
> + disable_irq_remapping_one(dmaru);
> + if (intel_iommu_enabled == 1) {
> + free_dmar_iommu(dmaru->iommu);
> + if (atsru) {
> + kfree(atsru->devices);
> + remove_atsru_from_saved_dev_atsru_list(atsru);
> + }
> + }
> + if (irq_remapping_enabled == 1 || intel_iommu_enabled == 1) {
> + kfree(dmaru->devices);
> + remove_dmaru_from_saved_dev_drhd_list(dmaru);
> + }
> + if (atsru) {
> + kfree(atsru->hdr);
> + list_del(&atsru->list);
> + kfree(atsru);
> + }
> + free_iommu(dmaru->iommu);
> + kfree(dmaru->hdr);
> + list_del(&dmaru->list);
> + kfree(dmaru);
> +}
> +
> +struct acpi_pci_iommu {
> + acpi_handle root_handle;
> + void *dmaru;
> + void *atsru;
> + struct list_head list;
> +};
> +
> +static LIST_HEAD(iommu_list);
> +static DEFINE_MUTEX(iommu_list_lock);
> +
> +static acpi_status register_iommu(acpi_handle handle, u32 lvl,
> + void *context, void **rv)
> +{
> + acpi_handle root_handle = context;
> + void *dmaru, *atsru;
> + struct acpi_pci_iommu *iommu;
> +
> + handle_iommu_add(handle, &dmaru, &atsru);
> + if (!dmaru)
> + return AE_OK;
> +
> + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
> + if (!iommu) {
> + pr_err("%s: cannot allocate memory\n", __func__);
> + handle_iommu_remove(dmaru, atsru);
> + return AE_OK;
> + }
> + iommu->root_handle = root_handle;
> + iommu->dmaru = dmaru;
> + iommu->atsru = atsru;
> +
> + mutex_lock(&iommu_list_lock);
> + list_add(&iommu->list, &iommu_list);
> + mutex_unlock(&iommu_list_lock);
> +
> + return AE_OK;
> +}
> +
> +void acpi_pci_iommu_add(struct acpi_pci_root *root)
> +{
> + acpi_status status;
> +
> + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
> + (u32)1, register_iommu, NULL,
> + root->device->handle,
> + NULL);
> + if (ACPI_FAILURE(status))
> + pr_err("%s: register_iommu failure - %d", __func__, status);
> +}
> +
> +void acpi_pci_iommu_remove(struct acpi_pci_root *root)
> +{
> + struct acpi_pci_iommu *iommu, *tmp;
> +
> + mutex_lock(&iommu_list_lock);
> + list_for_each_entry_safe(iommu, tmp, &iommu_list, list) {
> + if (root->device->handle != iommu->root_handle)
> + continue;
> + list_del(&iommu->list);
> + handle_iommu_remove(iommu->dmaru, iommu->atsru);
> + kfree(iommu);
> + }
> + mutex_unlock(&iommu_list_lock);
> +}
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index d2a976a..f00a89f 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -86,4 +86,12 @@ extern bool aer_acpi_firmware_first(void);
> static inline bool aer_acpi_firmware_first(void) { return false; }
> #endif
>
> +#ifdef CONFIG_DMAR_TABLE
> +void acpi_pci_iommu_add(struct acpi_pci_root *root);
> +void acpi_pci_iommu_remove(struct acpi_pci_root *root);
> +#else
> +static inline void acpi_pci_iommu_add(struct acpi_pci_root *root) { }
> +static inline void acpi_pci_iommu_remove(struct acpi_pci_root *root) { }
> +#endif
> +
> #endif /* _PCI_ACPI_H_ */
>


--
Thanks!
Yijing

2014-01-06 03:15:04

by Yijing Wang

[permalink] [raw]
Subject: Re: [PATCH v2 10/10] IOMMU: Add intel iommu irq-remapping and dmar hotplug support

Hi Yinghai,
Maybe we should add some guards to protect from initializing DMAR when we disable Intel IOMMU
like append intel_iommu=off to boot command.

Thanks!
Yijing.

On 2014/1/3 8:08, Yinghai Lu wrote:
> during hostbridge hotadd/hotremove, we call acpi_pci_iommu_add/remove in
> acpi_pci_root_add/remove.
>
> For add, need to after ioapic add.
>
> Run dsm in acpi root device to get iommu entries, and init dmar/irq_remapping
>
> Signed-off-by: Yinghai Lu <[email protected]>
> Cc: Len Brown <[email protected]>
> Cc: "Rafael J. Wysocki" <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>
> Cc: Joerg Roedel <[email protected]>
> Cc: Donald Dutile <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> ---
> drivers/acpi/pci_root.c | 2 +
> drivers/iommu/dmar.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci-acpi.h | 8 ++
> 3 files changed, 211 insertions(+)
>
> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
> index e666be3..dd82d8b 100644
> --- a/drivers/acpi/pci_root.c
> +++ b/drivers/acpi/pci_root.c
> @@ -600,6 +600,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
> }
>
> acpi_pci_ioapic_add(root);
> + acpi_pci_iommu_add(root);
>
> pci_bus_add_devices(root->bus);
> return 1;
> @@ -615,6 +616,7 @@ static void acpi_pci_root_remove(struct acpi_device *device)
>
> pci_stop_root_bus(root->bus);
>
> + acpi_pci_iommu_remove(root);
> acpi_pci_ioapic_remove(root);
>
> device_set_run_wake(root->bus->bridge, false);
> diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
> index 31d7942..c74f112 100644
> --- a/drivers/iommu/dmar.c
> +++ b/drivers/iommu/dmar.c
> @@ -38,6 +38,7 @@
> #include <linux/tboot.h>
> #include <linux/dmi.h>
> #include <linux/slab.h>
> +#include <acpi/acpi.h>
> #include <asm/irq_remapping.h>
> #include <asm/iommu_table.h>
>
> @@ -1548,3 +1549,203 @@ int __init dmar_ir_support(void)
> return dmar->flags & 0x1;
> }
> IOMMU_INIT_POST(detect_intel_iommu);
> +
> +static u8 dmar_uuid_str[] = "D8C1A3A6-BE9B-4C9B-91BF-C3CB81FC5DAF";
> +
> +static int acpi_dmar_dsm_support(acpi_handle handle)
> +{
> + int support = 0;
> + struct acpi_dsm_context context = {
> + .uuid_str = dmar_uuid_str,
> + .rev = 1,
> + .func_idx = 0,
> + };
> +
> + if (ACPI_SUCCESS(acpi_run_dsm(handle, &context))) {
> + u32 *ret = context.ret.pointer;
> + support = ret[0];
> + kfree(context.ret.pointer);
> + }
> +
> + if (support & 1)
> + return support & 0x0e;
> +
> + return 0;
> +}
> +
> +static void *acpi_dmar_dsm_run(acpi_handle handle, int idx)
> +{
> + struct acpi_dsm_context context = {
> + .uuid_str = dmar_uuid_str,
> + .rev = 1,
> + .func_idx = idx,
> + };
> +
> + if (ACPI_SUCCESS(acpi_run_dsm(handle, &context)))
> + return context.ret.pointer;
> +
> + return NULL;
> +}
> +
> +static void handle_iommu_add(acpi_handle handle, void **d, void **a)
> +{
> + int support;
> + struct acpi_dmar_header *header;
> + struct dmar_drhd_unit *dmaru = NULL;
> + struct dmar_atsr_unit *atsru = NULL;
> +
> + *d = NULL;
> + *a = NULL;
> +
> + support = acpi_dmar_dsm_support(handle);
> +
> + if (!support)
> + return;
> +
> + /* DRHD */
> + if (support & (1<<1)) {
> + header = acpi_dmar_dsm_run(handle, 1);
> + dmar_table_print_dmar_entry(header);
> + __dmar_parse_one_drhd(header, &dmaru);
> + }
> +
> + if (!dmaru)
> + return;
> +
> + /* ATSR */
> + if (support & (1<<2)) {
> + header = acpi_dmar_dsm_run(handle, 2);
> + dmar_table_print_dmar_entry(header);
> + __dmar_parse_one_atsr(header, &atsru);
> + }
> +
> + /* RHSA */
> + if (support & (1<<3)) {
> + header = acpi_dmar_dsm_run(handle, 3);
> + dmar_table_print_dmar_entry(header);
> + dmar_parse_one_rhsa(header);
> + kfree(header);
> + }
> +
> + /*
> + * only need to init intr_remap and dmar for hot-add ones
> + * after enable_IR() or pci_iommu_init
> + */
> + /*
> + * TODO: handle parsing failure for pre-installed hotplug one
> + * Could make every parse_one duplicate the entry table?
> + */
> + if (irq_remapping_enabled == 1)
> + intel_enable_irq_remapping_one(dmaru);
> + if (irq_remapping_enabled == 1 || intel_iommu_enabled == 1)
> + dmar_parse_dev(dmaru);
> + if (intel_iommu_enabled == 1) {
> + init_dmar_one(dmaru);
> + if (atsru) {
> + header = atsru->hdr;
> + if (atsr_parse_dev(atsru)) {
> + kfree(header);
> + atsru = NULL;
> + }
> + *a = atsru;
> + }
> + }
> + *d = dmaru;
> +}
> +
> +static void handle_iommu_remove(void *drhd, void *atsr)
> +{
> + struct dmar_drhd_unit *dmaru = drhd;
> + struct dmar_atsr_unit *atsru = atsr;
> +
> + if (!dmaru)
> + return;
> +
> + if (irq_remapping_enabled == 1)
> + disable_irq_remapping_one(dmaru);
> + if (intel_iommu_enabled == 1) {
> + free_dmar_iommu(dmaru->iommu);
> + if (atsru) {
> + kfree(atsru->devices);
> + remove_atsru_from_saved_dev_atsru_list(atsru);
> + }
> + }
> + if (irq_remapping_enabled == 1 || intel_iommu_enabled == 1) {
> + kfree(dmaru->devices);
> + remove_dmaru_from_saved_dev_drhd_list(dmaru);
> + }
> + if (atsru) {
> + kfree(atsru->hdr);
> + list_del(&atsru->list);
> + kfree(atsru);
> + }
> + free_iommu(dmaru->iommu);
> + kfree(dmaru->hdr);
> + list_del(&dmaru->list);
> + kfree(dmaru);
> +}
> +
> +struct acpi_pci_iommu {
> + acpi_handle root_handle;
> + void *dmaru;
> + void *atsru;
> + struct list_head list;
> +};
> +
> +static LIST_HEAD(iommu_list);
> +static DEFINE_MUTEX(iommu_list_lock);
> +
> +static acpi_status register_iommu(acpi_handle handle, u32 lvl,
> + void *context, void **rv)
> +{
> + acpi_handle root_handle = context;
> + void *dmaru, *atsru;
> + struct acpi_pci_iommu *iommu;
> +
> + handle_iommu_add(handle, &dmaru, &atsru);
> + if (!dmaru)
> + return AE_OK;
> +
> + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
> + if (!iommu) {
> + pr_err("%s: cannot allocate memory\n", __func__);
> + handle_iommu_remove(dmaru, atsru);
> + return AE_OK;
> + }
> + iommu->root_handle = root_handle;
> + iommu->dmaru = dmaru;
> + iommu->atsru = atsru;
> +
> + mutex_lock(&iommu_list_lock);
> + list_add(&iommu->list, &iommu_list);
> + mutex_unlock(&iommu_list_lock);
> +
> + return AE_OK;
> +}
> +
> +void acpi_pci_iommu_add(struct acpi_pci_root *root)
> +{
> + acpi_status status;
> +
> + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
> + (u32)1, register_iommu, NULL,
> + root->device->handle,
> + NULL);
> + if (ACPI_FAILURE(status))
> + pr_err("%s: register_iommu failure - %d", __func__, status);
> +}
> +
> +void acpi_pci_iommu_remove(struct acpi_pci_root *root)
> +{
> + struct acpi_pci_iommu *iommu, *tmp;
> +
> + mutex_lock(&iommu_list_lock);
> + list_for_each_entry_safe(iommu, tmp, &iommu_list, list) {
> + if (root->device->handle != iommu->root_handle)
> + continue;
> + list_del(&iommu->list);
> + handle_iommu_remove(iommu->dmaru, iommu->atsru);
> + kfree(iommu);
> + }
> + mutex_unlock(&iommu_list_lock);
> +}
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index d2a976a..f00a89f 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -86,4 +86,12 @@ extern bool aer_acpi_firmware_first(void);
> static inline bool aer_acpi_firmware_first(void) { return false; }
> #endif
>
> +#ifdef CONFIG_DMAR_TABLE
> +void acpi_pci_iommu_add(struct acpi_pci_root *root);
> +void acpi_pci_iommu_remove(struct acpi_pci_root *root);
> +#else
> +static inline void acpi_pci_iommu_add(struct acpi_pci_root *root) { }
> +static inline void acpi_pci_iommu_remove(struct acpi_pci_root *root) { }
> +#endif
> +
> #endif /* _PCI_ACPI_H_ */
>


--
Thanks!
Yijing