Re: [Patch Part3 V6 1/8] iommu/vt-d: Introduce helper function dmar_walk_resources()

From: Yijing Wang
Date: Fri Sep 19 2014 - 02:50:00 EST


On 2014/9/19 13:18, Jiang Liu wrote:
> Introduce helper function dmar_walk_resources to walk resource entries
> in DMAR table and ACPI buffer object returned by ACPI _DSM method
> for IOMMU hot-plug.
>
> Signed-off-by: Jiang Liu <jiang.liu@xxxxxxxxxxxxxxx>

Reviewed-by: Yijing Wang <wangyijing@xxxxxxxxxx>

> ---
> drivers/iommu/dmar.c | 209 +++++++++++++++++++++++--------------------
> drivers/iommu/intel-iommu.c | 4 +-
> include/linux/dmar.h | 19 ++--
> 3 files changed, 122 insertions(+), 110 deletions(-)
>
> diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
> index 06d268abe951..a05cf3634efe 100644
> --- a/drivers/iommu/dmar.c
> +++ b/drivers/iommu/dmar.c
> @@ -44,6 +44,14 @@
>
> #include "irq_remapping.h"
>
> +typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *);
> +struct dmar_res_callback {
> + dmar_res_handler_t cb[ACPI_DMAR_TYPE_RESERVED];
> + void *arg[ACPI_DMAR_TYPE_RESERVED];
> + bool ignore_unhandled;
> + bool print_entry;
> +};
> +
> /*
> * Assumptions:
> * 1) The hotplug framework guarentees that DMAR unit will be hot-added
> @@ -333,7 +341,7 @@ static struct notifier_block dmar_pci_bus_nb = {
> * present in the platform
> */
> static int __init
> -dmar_parse_one_drhd(struct acpi_dmar_header *header)
> +dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg)
> {
> struct acpi_dmar_hardware_unit *drhd;
> struct dmar_drhd_unit *dmaru;
> @@ -364,6 +372,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
> return ret;
> }
> dmar_register_drhd_unit(dmaru);
> +
> + if (arg)
> + (*(int *)arg)++;
> +
> return 0;
> }
>
> @@ -376,7 +388,8 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
> kfree(dmaru);
> }
>
> -static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
> +static int __init dmar_parse_one_andd(struct acpi_dmar_header *header,
> + void *arg)
> {
> struct acpi_dmar_andd *andd = (void *)header;
>
> @@ -398,7 +411,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
>
> #ifdef CONFIG_ACPI_NUMA
> static int __init
> -dmar_parse_one_rhsa(struct acpi_dmar_header *header)
> +dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg)
> {
> struct acpi_dmar_rhsa *rhsa;
> struct dmar_drhd_unit *drhd;
> @@ -425,6 +438,8 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header)
>
> return 0;
> }
> +#else
> +#define dmar_parse_one_rhsa dmar_res_noop
> #endif
>
> static void __init
> @@ -486,6 +501,52 @@ static int __init dmar_table_detect(void)
> return (ACPI_SUCCESS(status) ? 1 : 0);
> }
>
> +static int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
> + size_t len, struct dmar_res_callback *cb)
> +{
> + int ret = 0;
> + struct acpi_dmar_header *iter, *next;
> + struct acpi_dmar_header *end = ((void *)start) + len;
> +
> + for (iter = start; iter < end && ret == 0; iter = next) {
> + next = (void *)iter + iter->length;
> + if (iter->length == 0) {
> + /* Avoid looping forever on bad ACPI tables */
> + pr_debug(FW_BUG "Invalid 0-length structure\n");
> + break;
> + } else if (next > end) {
> + /* Avoid passing table end */
> + pr_warn(FW_BUG "record passes table end\n");
> + ret = -EINVAL;
> + break;
> + }
> +
> + if (cb->print_entry)
> + dmar_table_print_dmar_entry(iter);
> +
> + if (iter->type >= ACPI_DMAR_TYPE_RESERVED) {
> + /* continue for forward compatibility */
> + pr_debug("Unknown DMAR structure type %d\n",
> + iter->type);
> + } else if (cb->cb[iter->type]) {
> + ret = cb->cb[iter->type](iter, cb->arg[iter->type]);
> + } else if (!cb->ignore_unhandled) {
> + pr_warn("No handler for DMAR structure type %d\n",
> + iter->type);
> + ret = -EINVAL;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar,
> + struct dmar_res_callback *cb)
> +{
> + return dmar_walk_remapping_entries((void *)(dmar + 1),
> + dmar->header.length - sizeof(*dmar), cb);
> +}
> +
> /**
> * parse_dmar_table - parses the DMA reporting table
> */
> @@ -493,9 +554,18 @@ static int __init
> parse_dmar_table(void)
> {
> struct acpi_table_dmar *dmar;
> - struct acpi_dmar_header *entry_header;
> int ret = 0;
> int drhd_count = 0;
> + struct dmar_res_callback cb = {
> + .print_entry = true,
> + .ignore_unhandled = true,
> + .arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count,
> + .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd,
> + .cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr,
> + .cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr,
> + .cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa,
> + .cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd,
> + };
>
> /*
> * Do it again, earlier dmar_tbl mapping could be mapped with
> @@ -519,51 +589,10 @@ parse_dmar_table(void)
> }
>
> pr_info("Host address width %d\n", dmar->width + 1);
> -
> - entry_header = (struct acpi_dmar_header *)(dmar + 1);
> - while (((unsigned long)entry_header) <
> - (((unsigned long)dmar) + dmar_tbl->length)) {
> - /* Avoid looping forever on bad ACPI tables */
> - if (entry_header->length == 0) {
> - pr_warn("Invalid 0-length structure\n");
> - ret = -EINVAL;
> - break;
> - }
> -
> - dmar_table_print_dmar_entry(entry_header);
> -
> - switch (entry_header->type) {
> - case ACPI_DMAR_TYPE_HARDWARE_UNIT:
> - drhd_count++;
> - ret = dmar_parse_one_drhd(entry_header);
> - break;
> - case ACPI_DMAR_TYPE_RESERVED_MEMORY:
> - ret = dmar_parse_one_rmrr(entry_header);
> - break;
> - case ACPI_DMAR_TYPE_ROOT_ATS:
> - ret = dmar_parse_one_atsr(entry_header);
> - break;
> - case ACPI_DMAR_TYPE_HARDWARE_AFFINITY:
> -#ifdef CONFIG_ACPI_NUMA
> - ret = dmar_parse_one_rhsa(entry_header);
> -#endif
> - break;
> - case ACPI_DMAR_TYPE_NAMESPACE:
> - ret = dmar_parse_one_andd(entry_header);
> - break;
> - default:
> - pr_warn("Unknown DMAR structure type %d\n",
> - entry_header->type);
> - ret = 0; /* for forward compatibility */
> - break;
> - }
> - if (ret)
> - break;
> -
> - entry_header = ((void *)entry_header + entry_header->length);
> - }
> - if (drhd_count == 0)
> + ret = dmar_walk_dmar_table(dmar, &cb);
> + if (ret == 0 && drhd_count == 0)
> pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
> +
> return ret;
> }
>
> @@ -761,76 +790,60 @@ static void warn_invalid_dmar(u64 addr, const char *message)
> dmi_get_system_info(DMI_PRODUCT_VERSION));
> }
>
> -static int __init check_zero_address(void)
> +static int __ref
> +dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
> {
> - struct acpi_table_dmar *dmar;
> - struct acpi_dmar_header *entry_header;
> struct acpi_dmar_hardware_unit *drhd;
> + void __iomem *addr;
> + u64 cap, ecap;
>
> - dmar = (struct acpi_table_dmar *)dmar_tbl;
> - entry_header = (struct acpi_dmar_header *)(dmar + 1);
> -
> - while (((unsigned long)entry_header) <
> - (((unsigned long)dmar) + dmar_tbl->length)) {
> - /* Avoid looping forever on bad ACPI tables */
> - if (entry_header->length == 0) {
> - pr_warn("Invalid 0-length structure\n");
> - return 0;
> - }
> -
> - if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) {
> - void __iomem *addr;
> - u64 cap, ecap;
> -
> - drhd = (void *)entry_header;
> - if (!drhd->address) {
> - warn_invalid_dmar(0, "");
> - goto failed;
> - }
> + drhd = (void *)entry;
> + if (!drhd->address) {
> + warn_invalid_dmar(0, "");
> + return -EINVAL;
> + }
>
> - addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
> - if (!addr ) {
> - printk("IOMMU: can't validate: %llx\n", drhd->address);
> - goto failed;
> - }
> - cap = dmar_readq(addr + DMAR_CAP_REG);
> - ecap = dmar_readq(addr + DMAR_ECAP_REG);
> - early_iounmap(addr, VTD_PAGE_SIZE);
> - if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
> - warn_invalid_dmar(drhd->address,
> - " returns all ones");
> - goto failed;
> - }
> - }
> + addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
> + if (!addr) {
> + pr_warn("IOMMU: can't validate: %llx\n", drhd->address);
> + return -EINVAL;
> + }
> + cap = dmar_readq(addr + DMAR_CAP_REG);
> + ecap = dmar_readq(addr + DMAR_ECAP_REG);
> + early_iounmap(addr, VTD_PAGE_SIZE);
>
> - entry_header = ((void *)entry_header + entry_header->length);
> + if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
> + warn_invalid_dmar(drhd->address, " returns all ones");
> + return -EINVAL;
> }
> - return 1;
>
> -failed:
> return 0;
> }
>
> int __init detect_intel_iommu(void)
> {
> int ret;
> + struct dmar_res_callback validate_drhd_cb = {
> + .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
> + .ignore_unhandled = true,
> + };
>
> down_write(&dmar_global_lock);
> ret = dmar_table_detect();
> if (ret)
> - ret = check_zero_address();
> - {
> - if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
> - iommu_detected = 1;
> - /* Make sure ACS will be enabled */
> - pci_request_acs();
> - }
> + ret = !dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
> + &validate_drhd_cb);
> + if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
> + iommu_detected = 1;
> + /* Make sure ACS will be enabled */
> + pci_request_acs();
> + }
>
> #ifdef CONFIG_X86
> - if (ret)
> - x86_init.iommu.iommu_init = intel_iommu_init;
> + if (ret)
> + x86_init.iommu.iommu_init = intel_iommu_init;
> #endif
> - }
> +
> early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size);
> dmar_tbl = NULL;
> up_write(&dmar_global_lock);
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 5619f264862d..4af2206e41bc 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -3682,7 +3682,7 @@ static inline void init_iommu_pm_ops(void) {}
> #endif /* CONFIG_PM */
>
>
> -int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
> +int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg)
> {
> struct acpi_dmar_reserved_memory *rmrr;
> struct dmar_rmrr_unit *rmrru;
> @@ -3708,7 +3708,7 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
> return 0;
> }
>
> -int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
> +int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr, void *arg)
> {
> struct acpi_dmar_atsr *atsr;
> struct dmar_atsr_unit *atsru;
> diff --git a/include/linux/dmar.h b/include/linux/dmar.h
> index 1deece46a0ca..fac8ca34f9a8 100644
> --- a/include/linux/dmar.h
> +++ b/include/linux/dmar.h
> @@ -115,22 +115,21 @@ extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info,
> extern int detect_intel_iommu(void);
> extern int enable_drhd_fault_handling(void);
>
> +static inline int dmar_res_noop(struct acpi_dmar_header *hdr, void *arg)
> +{
> + return 0;
> +}
> +
> #ifdef CONFIG_INTEL_IOMMU
> extern int iommu_detected, no_iommu;
> extern int intel_iommu_init(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_parse_one_rmrr(struct acpi_dmar_header *header, void *arg);
> +extern int dmar_parse_one_atsr(struct acpi_dmar_header *header, void *arg);
> extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
> #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)
> -{
> - return 0;
> -}
> -static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
> -{
> - return 0;
> -}
> +#define dmar_parse_one_rmrr dmar_res_noop
> +#define dmar_parse_one_atsr dmar_res_noop
> static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
> {
> return 0;
>


--
Thanks!
Yijing

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/