Commit 0e242612 authored by Jiang Liu's avatar Jiang Liu Committed by Joerg Roedel

iommu/vt-d: Use RCU to protect global resources in interrupt context

Global DMA and interrupt remapping resources may be accessed in
interrupt context, so use RCU instead of rwsem to protect them
in such cases.
Signed-off-by: default avatarJiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: default avatarJoerg Roedel <joro@8bytes.org>
parent 3a5670e8
...@@ -71,13 +71,13 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) ...@@ -71,13 +71,13 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
* the very end. * the very end.
*/ */
if (drhd->include_all) if (drhd->include_all)
list_add_tail(&drhd->list, &dmar_drhd_units); list_add_tail_rcu(&drhd->list, &dmar_drhd_units);
else else
list_add(&drhd->list, &dmar_drhd_units); list_add_rcu(&drhd->list, &dmar_drhd_units);
} }
static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
struct pci_dev **dev, u16 segment) struct pci_dev __rcu **dev, u16 segment)
{ {
struct pci_bus *bus; struct pci_bus *bus;
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
...@@ -122,7 +122,9 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, ...@@ -122,7 +122,9 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
pci_name(pdev)); pci_name(pdev));
return -EINVAL; return -EINVAL;
} }
*dev = pdev;
rcu_assign_pointer(*dev, pdev);
return 0; return 0;
} }
...@@ -149,7 +151,7 @@ void *dmar_alloc_dev_scope(void *start, void *end, int *cnt) ...@@ -149,7 +151,7 @@ void *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
} }
int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
struct pci_dev ***devices, u16 segment) struct pci_dev __rcu ***devices, u16 segment)
{ {
struct acpi_dmar_device_scope *scope; struct acpi_dmar_device_scope *scope;
int index, ret; int index, ret;
...@@ -177,7 +179,7 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, ...@@ -177,7 +179,7 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
return 0; return 0;
} }
void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt) void dmar_free_dev_scope(struct pci_dev __rcu ***devices, int *cnt)
{ {
int i; int i;
struct pci_dev *tmp_dev; struct pci_dev *tmp_dev;
...@@ -186,9 +188,10 @@ void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt) ...@@ -186,9 +188,10 @@ void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt)
for_each_active_dev_scope(*devices, *cnt, i, tmp_dev) for_each_active_dev_scope(*devices, *cnt, i, tmp_dev)
pci_dev_put(tmp_dev); pci_dev_put(tmp_dev);
kfree(*devices); kfree(*devices);
}
*devices = NULL; *devices = NULL;
*cnt = 0; *cnt = 0;
}
} }
/** /**
...@@ -410,7 +413,7 @@ parse_dmar_table(void) ...@@ -410,7 +413,7 @@ parse_dmar_table(void)
return ret; return ret;
} }
static int dmar_pci_device_match(struct pci_dev *devices[], int cnt, static int dmar_pci_device_match(struct pci_dev __rcu *devices[], int cnt,
struct pci_dev *dev) struct pci_dev *dev)
{ {
int index; int index;
...@@ -431,11 +434,12 @@ static int dmar_pci_device_match(struct pci_dev *devices[], int cnt, ...@@ -431,11 +434,12 @@ static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
struct dmar_drhd_unit * struct dmar_drhd_unit *
dmar_find_matched_drhd_unit(struct pci_dev *dev) dmar_find_matched_drhd_unit(struct pci_dev *dev)
{ {
struct dmar_drhd_unit *dmaru = NULL; struct dmar_drhd_unit *dmaru;
struct acpi_dmar_hardware_unit *drhd; struct acpi_dmar_hardware_unit *drhd;
dev = pci_physfn(dev); dev = pci_physfn(dev);
rcu_read_lock();
for_each_drhd_unit(dmaru) { for_each_drhd_unit(dmaru) {
drhd = container_of(dmaru->hdr, drhd = container_of(dmaru->hdr,
struct acpi_dmar_hardware_unit, struct acpi_dmar_hardware_unit,
...@@ -443,14 +447,17 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) ...@@ -443,14 +447,17 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
if (dmaru->include_all && if (dmaru->include_all &&
drhd->segment == pci_domain_nr(dev->bus)) drhd->segment == pci_domain_nr(dev->bus))
return dmaru; goto out;
if (dmar_pci_device_match(dmaru->devices, if (dmar_pci_device_match(dmaru->devices,
dmaru->devices_cnt, dev)) dmaru->devices_cnt, dev))
return dmaru; goto out;
} }
dmaru = NULL;
out:
rcu_read_unlock();
return NULL; return dmaru;
} }
int __init dmar_dev_scope_init(void) int __init dmar_dev_scope_init(void)
......
...@@ -385,14 +385,14 @@ struct dmar_rmrr_unit { ...@@ -385,14 +385,14 @@ struct dmar_rmrr_unit {
struct acpi_dmar_header *hdr; /* ACPI header */ struct acpi_dmar_header *hdr; /* ACPI header */
u64 base_address; /* reserved base address*/ u64 base_address; /* reserved base address*/
u64 end_address; /* reserved end address */ u64 end_address; /* reserved end address */
struct pci_dev **devices; /* target devices */ struct pci_dev __rcu **devices; /* target devices */
int devices_cnt; /* target device count */ int devices_cnt; /* target device count */
}; };
struct dmar_atsr_unit { struct dmar_atsr_unit {
struct list_head list; /* list of ATSR units */ struct list_head list; /* list of ATSR units */
struct acpi_dmar_header *hdr; /* ACPI header */ struct acpi_dmar_header *hdr; /* ACPI header */
struct pci_dev **devices; /* target devices */ struct pci_dev __rcu **devices; /* target devices */
int devices_cnt; /* target device count */ int devices_cnt; /* target device count */
u8 include_all:1; /* include all ports */ u8 include_all:1; /* include all ports */
}; };
...@@ -634,12 +634,15 @@ static void domain_update_iommu_superpage(struct dmar_domain *domain) ...@@ -634,12 +634,15 @@ static void domain_update_iommu_superpage(struct dmar_domain *domain)
} }
/* set iommu_superpage to the smallest common denominator */ /* set iommu_superpage to the smallest common denominator */
rcu_read_lock();
for_each_active_iommu(iommu, drhd) { for_each_active_iommu(iommu, drhd) {
mask &= cap_super_page_val(iommu->cap); mask &= cap_super_page_val(iommu->cap);
if (!mask) { if (!mask) {
break; break;
} }
} }
rcu_read_unlock();
domain->iommu_superpage = fls(mask); domain->iommu_superpage = fls(mask);
} }
...@@ -658,6 +661,7 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn) ...@@ -658,6 +661,7 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
struct pci_dev *dev; struct pci_dev *dev;
int i; int i;
rcu_read_lock();
for_each_active_iommu(iommu, drhd) { for_each_active_iommu(iommu, drhd) {
if (segment != drhd->segment) if (segment != drhd->segment)
continue; continue;
...@@ -677,6 +681,7 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn) ...@@ -677,6 +681,7 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
} }
iommu = NULL; iommu = NULL;
out: out:
rcu_read_unlock();
return iommu; return iommu;
} }
...@@ -1535,10 +1540,12 @@ static void domain_exit(struct dmar_domain *domain) ...@@ -1535,10 +1540,12 @@ static void domain_exit(struct dmar_domain *domain)
dma_pte_free_pagetable(domain, 0, DOMAIN_MAX_PFN(domain->gaw)); dma_pte_free_pagetable(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
/* clear attached or cached domains */ /* clear attached or cached domains */
rcu_read_lock();
for_each_active_iommu(iommu, drhd) for_each_active_iommu(iommu, drhd)
if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE || if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
test_bit(iommu->seq_id, domain->iommu_bmp)) test_bit(iommu->seq_id, domain->iommu_bmp))
iommu_detach_domain(domain, iommu); iommu_detach_domain(domain, iommu);
rcu_read_unlock();
free_domain_mem(domain); free_domain_mem(domain);
} }
...@@ -2338,6 +2345,7 @@ static bool device_has_rmrr(struct pci_dev *dev) ...@@ -2338,6 +2345,7 @@ static bool device_has_rmrr(struct pci_dev *dev)
struct pci_dev *tmp; struct pci_dev *tmp;
int i; int i;
rcu_read_lock();
for_each_rmrr_units(rmrr) { for_each_rmrr_units(rmrr) {
/* /*
* Return TRUE if this RMRR contains the device that * Return TRUE if this RMRR contains the device that
...@@ -2346,9 +2354,11 @@ static bool device_has_rmrr(struct pci_dev *dev) ...@@ -2346,9 +2354,11 @@ static bool device_has_rmrr(struct pci_dev *dev)
for_each_active_dev_scope(rmrr->devices, for_each_active_dev_scope(rmrr->devices,
rmrr->devices_cnt, i, tmp) rmrr->devices_cnt, i, tmp)
if (tmp == dev) { if (tmp == dev) {
rcu_read_unlock();
return true; return true;
} }
} }
rcu_read_unlock();
return false; return false;
} }
...@@ -3512,7 +3522,7 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr) ...@@ -3512,7 +3522,7 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
atsru->hdr = hdr; atsru->hdr = hdr;
atsru->include_all = atsr->flags & 0x1; atsru->include_all = atsr->flags & 0x1;
list_add(&atsru->list, &dmar_atsr_units); list_add_rcu(&atsru->list, &dmar_atsr_units);
return 0; return 0;
} }
...@@ -3574,6 +3584,7 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev) ...@@ -3574,6 +3584,7 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
if (!bridge) if (!bridge)
return 0; return 0;
rcu_read_lock();
list_for_each_entry_rcu(atsru, &dmar_atsr_units, list) { list_for_each_entry_rcu(atsru, &dmar_atsr_units, list) {
atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header); atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
if (atsr->segment != pci_domain_nr(dev->bus)) if (atsr->segment != pci_domain_nr(dev->bus))
...@@ -3588,6 +3599,7 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev) ...@@ -3588,6 +3599,7 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
} }
ret = 0; ret = 0;
out: out:
rcu_read_unlock();
return ret; return ret;
} }
...@@ -3604,7 +3616,7 @@ int __init dmar_parse_rmrr_atsr_dev(void) ...@@ -3604,7 +3616,7 @@ int __init dmar_parse_rmrr_atsr_dev(void)
return ret; return ret;
} }
list_for_each_entry(atsr, &dmar_atsr_units, list) { list_for_each_entry_rcu(atsr, &dmar_atsr_units, list) {
ret = atsr_parse_dev(atsr); ret = atsr_parse_dev(atsr);
if (ret) if (ret)
return ret; return ret;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/rcupdate.h>
struct acpi_dmar_header; struct acpi_dmar_header;
...@@ -41,7 +42,7 @@ struct dmar_drhd_unit { ...@@ -41,7 +42,7 @@ struct dmar_drhd_unit {
struct list_head list; /* list of drhd units */ struct list_head list; /* list of drhd units */
struct acpi_dmar_header *hdr; /* ACPI header */ struct acpi_dmar_header *hdr; /* ACPI header */
u64 reg_base_addr; /* register base address*/ u64 reg_base_addr; /* register base address*/
struct pci_dev **devices; /* target device array */ struct pci_dev __rcu **devices;/* target device array */
int devices_cnt; /* target device count */ int devices_cnt; /* target device count */
u16 segment; /* PCI domain */ u16 segment; /* PCI domain */
u8 ignored:1; /* ignore drhd */ u8 ignored:1; /* ignore drhd */
...@@ -53,22 +54,31 @@ extern struct rw_semaphore dmar_global_lock; ...@@ -53,22 +54,31 @@ extern struct rw_semaphore dmar_global_lock;
extern struct list_head dmar_drhd_units; extern struct list_head dmar_drhd_units;
#define for_each_drhd_unit(drhd) \ #define for_each_drhd_unit(drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) list_for_each_entry_rcu(drhd, &dmar_drhd_units, list)
#define for_each_active_drhd_unit(drhd) \ #define for_each_active_drhd_unit(drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) \ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \
if (drhd->ignored) {} else if (drhd->ignored) {} else
#define for_each_active_iommu(i, drhd) \ #define for_each_active_iommu(i, drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) \ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \
if (i=drhd->iommu, drhd->ignored) {} else if (i=drhd->iommu, drhd->ignored) {} else
#define for_each_iommu(i, drhd) \ #define for_each_iommu(i, drhd) \
list_for_each_entry(drhd, &dmar_drhd_units, list) \ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \
if (i=drhd->iommu, 0) {} else if (i=drhd->iommu, 0) {} else
static inline bool dmar_rcu_check(void)
{
return rwsem_is_locked(&dmar_global_lock) ||
system_state == SYSTEM_BOOTING;
}
#define dmar_rcu_dereference(p) rcu_dereference_check((p), dmar_rcu_check())
#define for_each_dev_scope(a, c, p, d) \ #define for_each_dev_scope(a, c, p, d) \
for ((p) = 0; ((d) = (p) < (c) ? (a)[(p)] : NULL, (p) < (c)); (p)++) for ((p) = 0; ((d) = (p) < (c) ? dmar_rcu_dereference((a)[(p)]) : \
NULL, (p) < (c)); (p)++)
#define for_each_active_dev_scope(a, c, p, d) \ #define for_each_active_dev_scope(a, c, p, d) \
for_each_dev_scope((a), (c), (p), (d)) if (!(d)) { continue; } else for_each_dev_scope((a), (c), (p), (d)) if (!(d)) { continue; } else
...@@ -78,6 +88,7 @@ extern int dmar_dev_scope_init(void); ...@@ -78,6 +88,7 @@ extern int dmar_dev_scope_init(void);
extern int dmar_parse_dev_scope(void *start, void *end, int *cnt, extern int dmar_parse_dev_scope(void *start, void *end, int *cnt,
struct pci_dev ***devices, u16 segment); struct pci_dev ***devices, u16 segment);
extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt); extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt);
extern void dmar_free_dev_scope(struct pci_dev __rcu ***devices, int *cnt);
extern void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt); extern void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt);
/* Intel IOMMU detection */ /* Intel IOMMU detection */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment