diff mbox

[Part3,V5,1/8] iommu/vt-d: Introduce helper function dmar_walk_resources()

Message ID 1410487848-6027-2-git-send-email-jiang.liu@linux.intel.com
State Not Applicable
Headers show

Commit Message

Jiang Liu Sept. 12, 2014, 2:10 a.m. UTC
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@linux.intel.com>
---
 drivers/iommu/dmar.c        |  209 +++++++++++++++++++++++--------------------
 drivers/iommu/intel-iommu.c |    4 +-
 include/linux/dmar.h        |   19 ++--
 3 files changed, 122 insertions(+), 110 deletions(-)

Comments

Yijing Wang Sept. 12, 2014, 9:16 a.m. UTC | #1
On 2014/9/12 10:10, 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@linux.intel.com>

Hi Gerry. some comments below.


> ---
>  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 60ab474bfff3..afd46eb9a5de 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;

Why do we need a switch to control print ?

> +};
> +
>  
> +static int dmar_walk_resources(struct acpi_dmar_header *start, size_t len,
> +			       struct dmar_res_callback *cb)

The name dmar_walk_resources easily make people think this is related with I/O or memory resources.
Do you have a better idea of this ?  What about dmar_walk_remapping_entry() or dmar_walk_remapping_structure() ?

> +{
> +	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");

What about use pr_warn() instead of pr_debug(), pr_debug() default is off.

> +			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);

Same as above.

> +		} 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_resources((struct acpi_dmar_header *)(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;
>  }
>  
> @@ -762,76 +791,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;
>
Jiang Liu Sept. 16, 2014, 7:13 a.m. UTC | #2
On 2014/9/12 17:16, Yijing Wang wrote:
> On 2014/9/12 10:10, 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@linux.intel.com>
> 
> Hi Gerry. some comments below.
Hi Yijing,
	Thanks for review. Please refer inline comments below.

> 
> 
>> ---
>>  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 60ab474bfff3..afd46eb9a5de 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;
> 
> Why do we need a switch to control print ?
We will walk DMAR entries several times during hotplug and only
want to print once.

> 
>> +};
>> +
>>  
>> +static int dmar_walk_resources(struct acpi_dmar_header *start, size_t len,
>> +			       struct dmar_res_callback *cb)
> 
> The name dmar_walk_resources easily make people think this is related with I/O or memory resources.
> Do you have a better idea of this ?  What about dmar_walk_remapping_entry() or dmar_walk_remapping_structure() ?
Good suggestion, I like dmar_walk_remapping_entries().
> 
>> +{
>> +	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");
> 
> What about use pr_warn() instead of pr_debug(), pr_debug() default is off.
It seems a common practice for BIOS engineer to mark the last entry with
zero length. So it will be annoying if we generate this debug message
on product kernel.

> 
>> +			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);
> 
> Same as above.
This is typically caused by new DMAR specification. It will also be
annoying too if  we also generate this debug message on an newer
hardware platform with older linux kernel.

> 
>> +		} 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_resources((struct acpi_dmar_header *)(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;
>>  }
>>  
>> @@ -762,76 +791,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;
>>
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Yijing Wang Sept. 16, 2014, 8:08 a.m. UTC | #3
>>>  #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;
>>
>> Why do we need a switch to control print ?
> We will walk DMAR entries several times during hotplug and only
> want to print once.

Fine, thanks for your explanation.

> 
>>
>>> +};
>>> +
>>>  
>>> +static int dmar_walk_resources(struct acpi_dmar_header *start, size_t len,
>>> +			       struct dmar_res_callback *cb)
>>
>> The name dmar_walk_resources easily make people think this is related with I/O or memory resources.
>> Do you have a better idea of this ?  What about dmar_walk_remapping_entry() or dmar_walk_remapping_structure() ?
> Good suggestion, I like dmar_walk_remapping_entries().
>>
>>> +{
>>> +	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");
>>
>> What about use pr_warn() instead of pr_debug(), pr_debug() default is off.
> It seems a common practice for BIOS engineer to mark the last entry with
> zero length. So it will be annoying if we generate this debug message
> on product kernel.

OK.

> 
>>
>>> +			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);
>>
>> Same as above.
> This is typically caused by new DMAR specification. It will also be
> annoying too if  we also generate this debug message on an newer
> hardware platform with older linux kernel.

OK.

> 
>>
>>> +		} 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",

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 60ab474bfff3..afd46eb9a5de 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_resources(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_resources((struct acpi_dmar_header *)(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;
 }
 
@@ -762,76 +791,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;