Skip to content

Commit

Permalink
iommu/vt-d: Unify the way to process DMAR device scope array
Browse files Browse the repository at this point in the history
Now we have a PCI bus notification based mechanism to update DMAR
device scope array, we could extend the mechanism to support boot
time initialization too, which will help to unify and simplify
the implementation.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: Joerg Roedel <joro@8bytes.org>
  • Loading branch information
Jiang Liu authored and Joerg Roedel committed Mar 4, 2014
1 parent 59ce051 commit 2e45528
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 178 deletions.
163 changes: 43 additions & 120 deletions drivers/iommu/dmar.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ LIST_HEAD(dmar_drhd_units);

struct acpi_table_header * __initdata dmar_tbl;
static acpi_size dmar_tbl_size;
static int dmar_dev_scope_status = 1;

static int alloc_iommu(struct dmar_drhd_unit *drhd);
static void free_iommu(struct intel_iommu *iommu);
Expand All @@ -76,58 +77,6 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
list_add_rcu(&drhd->list, &dmar_drhd_units);
}

static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
struct pci_dev __rcu **dev, u16 segment)
{
struct pci_bus *bus;
struct pci_dev *pdev = NULL;
struct acpi_dmar_pci_path *path;
int count;

bus = pci_find_bus(segment, scope->bus);
path = (struct acpi_dmar_pci_path *)(scope + 1);
count = (scope->length - sizeof(struct acpi_dmar_device_scope))
/ sizeof(struct acpi_dmar_pci_path);

while (count) {
if (pdev)
pci_dev_put(pdev);
/*
* Some BIOSes list non-exist devices in DMAR table, just
* ignore it
*/
if (!bus) {
pr_warn("Device scope bus [%d] not found\n", scope->bus);
break;
}
pdev = pci_get_slot(bus, PCI_DEVFN(path->device, path->function));
if (!pdev) {
/* warning will be printed below */
break;
}
path ++;
count --;
bus = pdev->subordinate;
}
if (!pdev) {
pr_warn("Device scope device [%04x:%02x:%02x.%02x] not found\n",
segment, scope->bus, path->device, path->function);
return 0;
}
if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && \
pdev->subordinate) || (scope->entry_type == \
ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) {
pci_dev_put(pdev);
pr_warn("Device scope type does not match for %s\n",
pci_name(pdev));
return -EINVAL;
}

rcu_assign_pointer(*dev, pdev);

return 0;
}

void *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
{
struct acpi_dmar_device_scope *scope;
Expand All @@ -150,35 +99,6 @@ void *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
return kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
}

int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
struct pci_dev __rcu ***devices, u16 segment)
{
struct acpi_dmar_device_scope *scope;
int index, ret;

*devices = dmar_alloc_dev_scope(start, end, cnt);
if (*cnt == 0)
return 0;
else if (!*devices)
return -ENOMEM;

for (index = 0; start < end; start += scope->length) {
scope = start;
if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) {
ret = dmar_parse_one_dev_scope(scope,
&(*devices)[index], segment);
if (ret) {
dmar_free_dev_scope(devices, cnt);
return ret;
}
index ++;
}
}

return 0;
}

void dmar_free_dev_scope(struct pci_dev __rcu ***devices, int *cnt)
{
int i;
Expand Down Expand Up @@ -220,6 +140,8 @@ dmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event)
if (!info) {
pr_warn("Out of memory when allocating notify_info "
"for %s.\n", pci_name(dev));
if (dmar_dev_scope_status == 0)
dmar_dev_scope_status = -ENOMEM;
return NULL;
}
}
Expand Down Expand Up @@ -349,6 +271,8 @@ static int dmar_pci_bus_add_dev(struct dmar_pci_notify_info *info)
}
if (ret >= 0)
ret = dmar_iommu_notify_scope_dev(info);
if (ret < 0 && dmar_dev_scope_status == 0)
dmar_dev_scope_status = ret;

return ret;
}
Expand Down Expand Up @@ -418,9 +342,21 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
dmaru->reg_base_addr = drhd->address;
dmaru->segment = drhd->segment;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
if (!dmaru->include_all) {
dmaru->devices = dmar_alloc_dev_scope((void *)(drhd + 1),
((void *)drhd) + drhd->header.length,
&dmaru->devices_cnt);
if (dmaru->devices_cnt && dmaru->devices == NULL) {
kfree(dmaru);
return -ENOMEM;
}
}

ret = alloc_iommu(dmaru);
if (ret) {
if (!dmaru->include_all)
dmar_free_dev_scope(&dmaru->devices,
&dmaru->devices_cnt);
kfree(dmaru);
return ret;
}
Expand All @@ -437,21 +373,6 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
kfree(dmaru);
}

static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
{
struct acpi_dmar_hardware_unit *drhd;

drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;

if (dmaru->include_all)
return 0;

return dmar_parse_dev_scope((void *)(drhd + 1),
((void *)drhd) + drhd->header.length,
&dmaru->devices_cnt, &dmaru->devices,
drhd->segment);
}

#ifdef CONFIG_ACPI_NUMA
static int __init
dmar_parse_one_rhsa(struct acpi_dmar_header *header)
Expand Down Expand Up @@ -665,34 +586,35 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)

int __init dmar_dev_scope_init(void)
{
static int dmar_dev_scope_initialized;
struct dmar_drhd_unit *drhd;
int ret = -ENODEV;
struct pci_dev *dev = NULL;
struct dmar_pci_notify_info *info;

if (dmar_dev_scope_initialized)
return dmar_dev_scope_initialized;
if (dmar_dev_scope_status != 1)
return dmar_dev_scope_status;

if (list_empty(&dmar_drhd_units))
goto fail;
if (list_empty(&dmar_drhd_units)) {
dmar_dev_scope_status = -ENODEV;
} else {
dmar_dev_scope_status = 0;

for_each_pci_dev(dev) {
if (dev->is_virtfn)
continue;

info = dmar_alloc_pci_notify_info(dev,
BUS_NOTIFY_ADD_DEVICE);
if (!info) {
return dmar_dev_scope_status;
} else {
dmar_pci_bus_add_dev(info);
dmar_free_pci_notify_info(info);
}
}

for_each_drhd_unit(drhd) {
ret = dmar_parse_dev(drhd);
if (ret)
goto fail;
bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
}

ret = dmar_parse_rmrr_atsr_dev();
if (ret)
goto fail;

bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);

dmar_dev_scope_initialized = 1;
return 0;

fail:
dmar_dev_scope_initialized = ret;
return ret;
return dmar_dev_scope_status;
}


Expand Down Expand Up @@ -1617,7 +1539,8 @@ static int __init dmar_free_unused_resources(void)
if (irq_remapping_enabled || intel_iommu_enabled)
return 0;

bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);
if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units))
bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);

down_write(&dmar_global_lock);
list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) {
Expand Down
71 changes: 18 additions & 53 deletions drivers/iommu/intel-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -3473,11 +3473,6 @@ static void __init init_iommu_pm_ops(void)
static inline void init_iommu_pm_ops(void) {}
#endif /* CONFIG_PM */

static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
{
list_add(&rmrr->list, &dmar_rmrr_units);
}


int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
{
Expand All @@ -3492,21 +3487,17 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
rmrr = (struct acpi_dmar_reserved_memory *)header;
rmrru->base_address = rmrr->base_address;
rmrru->end_address = rmrr->end_address;
rmrru->devices = dmar_alloc_dev_scope((void *)(rmrr + 1),
((void *)rmrr) + rmrr->header.length,
&rmrru->devices_cnt);
if (rmrru->devices_cnt && rmrru->devices == NULL) {
kfree(rmrru);
return -ENOMEM;
}

dmar_register_rmrr_unit(rmrru);
return 0;
}
list_add(&rmrru->list, &dmar_rmrr_units);

static int __init
rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
{
struct acpi_dmar_reserved_memory *rmrr;

rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
return dmar_parse_dev_scope((void *)(rmrr + 1),
((void *)rmrr) + rmrr->header.length,
&rmrru->devices_cnt, &rmrru->devices,
rmrr->segment);
return 0;
}

int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
Expand All @@ -3521,26 +3512,21 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)

atsru->hdr = hdr;
atsru->include_all = atsr->flags & 0x1;
if (!atsru->include_all) {
atsru->devices = dmar_alloc_dev_scope((void *)(atsr + 1),
(void *)atsr + atsr->header.length,
&atsru->devices_cnt);
if (atsru->devices_cnt && atsru->devices == NULL) {
kfree(atsru);
return -ENOMEM;
}
}

list_add_rcu(&atsru->list, &dmar_atsr_units);

return 0;
}

static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
{
struct acpi_dmar_atsr *atsr;

if (atsru->include_all)
return 0;

atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
return dmar_parse_dev_scope((void *)(atsr + 1),
(void *)atsr + atsr->header.length,
&atsru->devices_cnt, &atsru->devices,
atsr->segment);
}

static void intel_iommu_free_atsr(struct dmar_atsr_unit *atsru)
{
dmar_free_dev_scope(&atsru->devices, &atsru->devices_cnt);
Expand Down Expand Up @@ -3604,27 +3590,6 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
return ret;
}

int __init dmar_parse_rmrr_atsr_dev(void)
{
struct dmar_rmrr_unit *rmrr;
struct dmar_atsr_unit *atsr;
int ret;

list_for_each_entry(rmrr, &dmar_rmrr_units, list) {
ret = rmrr_parse_dev(rmrr);
if (ret)
return ret;
}

list_for_each_entry_rcu(atsr, &dmar_atsr_units, list) {
ret = atsr_parse_dev(atsr);
if (ret)
return ret;
}

return 0;
}

int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
{
int ret = 0;
Expand Down
5 changes: 0 additions & 5 deletions include/linux/dmar.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ extern int arch_setup_dmar_msi(unsigned int irq);

#ifdef CONFIG_INTEL_IOMMU
extern int iommu_detected, no_iommu;
extern 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);
extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
Expand All @@ -188,10 +187,6 @@ static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
{
return 0;
}
static inline int dmar_parse_rmrr_atsr_dev(void)
{
return 0;
}
static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
{
return 0;
Expand Down

0 comments on commit 2e45528

Please sign in to comment.