Re: [PATCH] pci, dmar: Update dmar units devices list during hotplug

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 05/24/2011 10:42 AM, Alex Williamson wrote:
> On Tue, 2011-05-24 at 11:58 +0100, David Woodhouse wrote:
>> On Thu, 2011-05-19 at 16:15 -0600, Alex Williamson wrote:
>>> I think I'd vote for saving some kind of representation of the bus
>>> hierarchy, we probably don't need to list every possible individual
>>> device.  Leaving a broken pointer around to be matched up and restored
>>> later just seems like a continuation of an idea that was bad to begin
>>> with.  Thanks, 
>>
>> I agree. We should just process the original ATSR information in
>> dmar_find_matched_drhd_unit(), rather than comparing with a list of
>> possibly stale pointers.
>>
>> I don't quite understand why the list of PCI devices was *ever* done
>> like that.
> 
> Yinghai,
> 
> I thought I might be running into something similar so spent some time
> taking a different slant coding up the bug you found.  Turns out I
> should have tested your patch first because I wasn't hitting that bug at
> all.  The patch below is a work-in-progress that I think fixes the bug
> by providing a quick means of re-parsing the scope as needed to match
> current struct pci_devs.  It needs testing and cleanup, but feel free to
> run with it (or ignore).  Just figured its better to post than waste the
> code if you end up doing something similar.  Thanks,
> 
> Alex
> 

it does not apply to current linus tree cleanly.

> 
> Not for commit
> 
> Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
> ---
> 
> diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
> index 9cbeb0b..1d7bc58 100644
> --- a/drivers/pci/dmar.c
> +++ b/drivers/pci/dmar.c
> @@ -59,8 +59,8 @@ 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,
> -					   struct pci_dev **dev, u16 segment)
> +struct pci_dev *dmar_get_scope_dev(struct acpi_dmar_device_scope *scope,
> +				   u16 segment)
>  {
>  	struct pci_bus *bus;
>  	struct pci_dev *pdev = NULL;
> @@ -72,7 +72,7 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
>  	count = (scope->length - sizeof(struct acpi_dmar_device_scope))
>  		/ sizeof(struct acpi_dmar_pci_path);
>  
> -	while (count) {
> +	for (; count; path++, count--, bus = pdev->subordinate) {
>  		if (pdev)
>  			pci_dev_put(pdev);
>  		/*
> @@ -80,82 +80,103 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
>  		 * ignore it
>  		 */
>  		if (!bus) {
> -			printk(KERN_WARNING
> -			PREFIX "Device scope bus [%d] not found\n",
> -			scope->bus);
> -			break;
> +			printk(KERN_WARNING PREFIX
> +			       "Device scope bus [%d] not found\n", scope->bus);
> +			return NULL;
>  		}
>  		pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn));
>  		if (!pdev) {
>  			printk(KERN_WARNING PREFIX
> -			"Device scope device [%04x:%02x:%02x.%02x] not found\n",
> -				segment, bus->number, path->dev, path->fn);
> -			break;
> +			       "Device scope device [%04x:%02x:%02x.%02x] not found\n",
> +			       segment, bus->number, path->dev, path->fn);
> +			return NULL;
>  		}
> -		path ++;
> -		count --;
> -		bus = pdev->subordinate;
>  	}
> -	if (!pdev) {
> -		printk(KERN_WARNING PREFIX
> -		"Device scope device [%04x:%02x:%02x.%02x] not found\n",
> -		segment, scope->bus, path->dev, path->fn);
> -		*dev = NULL;
> +
> +	return pdev;
> +}
> +
> +static int dmar_match_scope_one(struct acpi_dmar_device_scope *scope,
> +				struct pci_dev *dev, u16 segment)
> +{
> +	struct pci_dev *pdev;
> +	int ret = 0;
> +
> +	if (segment != pci_domain_nr(dev->bus))
>  		return 0;
> +
> +	pdev = dmar_get_scope_dev(scope, segment);
> +	if (!pdev)
> +		return 0;
> +
> +	if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT) {
> +		if (dev == pdev)
> +			ret = 1;
> +	} else {
> +		while (dev) {
> +			if (dev == pdev) {
> +				ret = 1;
> +				break;
> +			}
> +			dev = dev->bus->self;
> +		}
>  	}
> -	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);
> -		printk(KERN_WARNING PREFIX
> -			"Device scope type does not match for %s\n",
> -			 pci_name(pdev));
> -		return -EINVAL;
> +
> +	pci_dev_put(pdev);
> +
> +	return ret;
> +}
> +
> +int dmar_match_scope(struct acpi_dmar_device_scope **scopes, int cnt,
> +		     struct pci_dev *dev, u16 segment)
> +{
> +	int i;
> +
> +	for (i = 0; i < cnt; i++) {
> +		if (dmar_match_scope_one(scopes[i], dev, segment))
> +			return 1;
>  	}
> -	*dev = pdev;
>  	return 0;
>  }
>  
>  static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
> -				       struct pci_dev ***devices, u16 segment)
> +				       struct acpi_dmar_device_scope ***scopes)
>  {
>  	struct acpi_dmar_device_scope *scope;
> -	void * tmp = start;
> -	int index;
> -	int ret;
> +	void *tmp = start;
> +	int index = 0;
>  
>  	*cnt = 0;
> +
>  	while (start < end) {
>  		scope = start;
> +
>  		if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
>  		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
>  			(*cnt)++;
>  		else
>  			printk(KERN_WARNING PREFIX
> -				"Unsupported device scope\n");
> +			       "Unsupported device scope\n");
> +
>  		start += scope->length;
>  	}
> +
>  	if (*cnt == 0)
>  		return 0;
>  
> -	*devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
> -	if (!*devices)
> +	*scopes = kcalloc(*cnt, sizeof(struct acpi_dmar_device_scope *),
> +                          GFP_KERNEL);
> +	if (!*scopes)
>  		return -ENOMEM;
>  
>  	start = tmp;
> -	index = 0;
>  	while (start < end) {
>  		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) {
> -				kfree(*devices);
> -				return ret;
> -			}
> -			index ++;
> -		}
> +		    scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
> +			(*scopes)[index++] = scope;
> +
>  		start += scope->length;
>  	}
>  
> @@ -204,9 +225,8 @@ static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
>  		return 0;
>  
>  	ret = dmar_parse_dev_scope((void *)(drhd + 1),
> -				((void *)drhd) + drhd->header.length,
> -				&dmaru->devices_cnt, &dmaru->devices,
> -				drhd->segment);
> +				   ((void *)drhd) + drhd->header.length,
> +				   &dmaru->scopes_cnt, &dmaru->scopes);
>  	if (ret) {
>  		list_del(&dmaru->list);
>  		kfree(dmaru);
> @@ -250,10 +270,10 @@ rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
>  
>  	rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
>  	ret = dmar_parse_dev_scope((void *)(rmrr + 1),
> -		((void *)rmrr) + rmrr->header.length,
> -		&rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
> +				   ((void *)rmrr) + rmrr->header.length,
> +				   &rmrru->scopes_cnt, &rmrru->scopes);
>  
> -	if (ret || (rmrru->devices_cnt == 0)) {
> +	if (ret || (rmrru->scopes_cnt == 0)) {
>  		list_del(&rmrru->list);
>  		kfree(rmrru);
>  	}
> @@ -290,10 +310,9 @@ static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
>  
>  	atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
>  	rc = dmar_parse_dev_scope((void *)(atsr + 1),
> -				(void *)atsr + atsr->header.length,
> -				&atsru->devices_cnt, &atsru->devices,
> -				atsr->segment);
> -	if (rc || !atsru->devices_cnt) {
> +				  (void *)atsr + atsr->header.length,
> +				  &atsru->scopes_cnt, &atsru->scopes);
> +	if (rc || !atsru->scopes_cnt) {
>  		list_del(&atsru->list);
>  		kfree(atsru);
>  	}
> @@ -307,6 +326,7 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
>  	struct pci_bus *bus;
>  	struct acpi_dmar_atsr *atsr;
>  	struct dmar_atsr_unit *atsru;
> +	struct pci_dev *pdev;
>  
>  	list_for_each_entry(atsru, &dmar_atsr_units, list) {
>  		atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
> @@ -325,10 +345,18 @@ found:
>  			return 0;
>  
>  		if (bridge->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
> -			for (i = 0; i < atsru->devices_cnt; i++)
> -				if (atsru->devices[i] == bridge)
> +			for (i = 0; i < atsru->scopes_cnt; i++) {
> +				pdev = dmar_get_scope_dev(atsru->scopes[i],
> +							  atsr->segment);
> +				if (!pdev)
> +					continue;
> +
> +				if (pdev == bridge) {
> +					pci_dev_put(pdev);
>  					return 1;
> -			break;
> +				}
> +				pci_dev_put(pdev);
> +			}
>  		}
>  	}
>  
> @@ -475,23 +503,6 @@ parse_dmar_table(void)
>  	return ret;
>  }
>  
> -int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
> -			  struct pci_dev *dev)
> -{
> -	int index;
> -
> -	while (dev) {
> -		for (index = 0; index < cnt; index++)
> -			if (dev == devices[index])
> -				return 1;
> -
> -		/* Check our parent */
> -		dev = dev->bus->self;
> -	}
> -
> -	return 0;
> -}
> -
>  struct dmar_drhd_unit *
>  dmar_find_matched_drhd_unit(struct pci_dev *dev)
>  {
> @@ -504,11 +515,11 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
>  				    header);
>  
>  		if (dmaru->include_all &&
> -		    drhd->segment == pci_domain_nr(dev->bus))
> +		    dmaru->segment == pci_domain_nr(dev->bus))
>  			return dmaru;
>  
> -		if (dmar_pci_device_match(dmaru->devices,
> -					  dmaru->devices_cnt, dev))
> +		if (dmar_match_scope(dmaru->scopes, dmaru->scopes_cnt,
> +				     dev, dmaru->segment))
>  			return dmaru;
>  	}
>  
> diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
> index e7ff5b8..7d0b095 100644
> --- a/drivers/pci/intel-iommu.c
> +++ b/drivers/pci/intel-iommu.c
> @@ -541,32 +541,34 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
>  
>  static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
>  {
> -	struct dmar_drhd_unit *drhd = NULL;
> -	int i;
> +	struct dmar_drhd_unit *dmaru = NULL;
> +	struct pci_dev *pdev;
> +	struct intel_iommu *found = NULL;
>  
> -	for_each_drhd_unit(drhd) {
> -		if (drhd->ignored)
> +	pdev = pci_get_domain_bus_and_slot(segment, bus, devfn);
> +
> +	for_each_drhd_unit(dmaru) {
> +		if (dmaru->ignored)
>  			continue;
> -		if (segment != drhd->segment)
> +		if (segment != dmaru->segment)
>  			continue;
>  
> -		for (i = 0; i < drhd->devices_cnt; i++) {
> -			if (drhd->devices[i] &&
> -			    drhd->devices[i]->bus->number == bus &&
> -			    drhd->devices[i]->devfn == devfn)
> -				return drhd->iommu;
> -			if (drhd->devices[i] &&
> -			    drhd->devices[i]->subordinate &&
> -			    drhd->devices[i]->subordinate->number <= bus &&
> -			    drhd->devices[i]->subordinate->subordinate >= bus)
> -				return drhd->iommu;
> +		if (dmaru->include_all) {
> +			found = dmaru->iommu;
> +			break;
> +		}
> +
> +		if (dmar_match_scope(dmaru->scopes, dmaru->scopes_cnt,
> +				     pdev, dmaru->segment)) {
> +			found = dmaru->iommu;
> +			break;
>  		}
>  
> -		if (drhd->include_all)
> -			return drhd->iommu;
>  	}
>  
> -	return NULL;
> +	pci_dev_put(pdev);
> +
> +	return found;
>  }
>  
>  static void domain_flush_cache(struct dmar_domain *domain,
> @@ -2250,7 +2252,7 @@ int __init init_dmars(void)
>  	struct dmar_rmrr_unit *rmrr;
>  	struct pci_dev *pdev;
>  	struct intel_iommu *iommu;
> -	int i, ret;
> +	int ret;
>  
>  	/*
>  	 * for each drhd
> @@ -2397,18 +2399,22 @@ int __init init_dmars(void)
>  	 */
>  	printk(KERN_INFO "IOMMU: Setting RMRR:\n");
>  	for_each_rmrr_units(rmrr) {
> -		for (i = 0; i < rmrr->devices_cnt; i++) {
> -			pdev = rmrr->devices[i];
> -			/*
> -			 * some BIOS lists non-exist devices in DMAR
> -			 * table.
> -			 */
> +		struct acpi_dmar_reserved_memory *rmrrh;
> +		int i;
> +
> +		rmrrh = container_of(rmrr->hdr,
> +				     struct acpi_dmar_reserved_memory, header);
> +
> +		for (i = 0; i < rmrr->scopes_cnt; i++) {
> +			pdev = dmar_get_scope_dev(rmrr->scopes[i],
> +						  rmrrh->segment);
>  			if (!pdev)
>  				continue;
> -			ret = iommu_prepare_rmrr_dev(rmrr, pdev);
> -			if (ret)
> +
> +			if (iommu_prepare_rmrr_dev(rmrr, pdev))
>  				printk(KERN_ERR
>  				       "IOMMU: mapping reserved region failed\n");
> +			pci_dev_put(pdev);
>  		}
>  	}
>  
> @@ -3078,15 +3084,21 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB, quir
>  static void __init init_no_remapping_devices(void)
>  {
>  	struct dmar_drhd_unit *drhd;
> +	struct pci_dev *pdev;
>  
>  	for_each_drhd_unit(drhd) {
>  		if (!drhd->include_all) {
>  			int i;
> -			for (i = 0; i < drhd->devices_cnt; i++)
> -				if (drhd->devices[i] != NULL)
> +			for (i = 0; i < drhd->scopes_cnt; i++) {
> +				pdev = dmar_get_scope_dev(drhd->scopes[i],
> +							  drhd->segment);
> +				if (pdev) {
> +					pci_dev_put(pdev);
>  					break;
> +				}
> +			}
>  			/* ignore DMAR unit if no pci devices exist */
> -			if (i == drhd->devices_cnt)
> +			if (i == drhd->scopes_cnt)
>  				drhd->ignored = 1;
>  		}
>  	}
> @@ -3099,20 +3111,28 @@ static void __init init_no_remapping_devices(void)
>  		if (drhd->ignored || drhd->include_all)
>  			continue;
>  
> -		for (i = 0; i < drhd->devices_cnt; i++)
> -			if (drhd->devices[i] &&
> -				!IS_GFX_DEVICE(drhd->devices[i]))
> +		for (i = 0; i < drhd->scopes_cnt; i++) {
> +			pdev = dmar_get_scope_dev(drhd->scopes[i],
> +						  drhd->segment);
> +			if (pdev && !IS_GFX_DEVICE(pdev)) {
> +				pci_dev_put(pdev);
>  				break;
> +			}
> +			pci_dev_put(pdev);
> +		}
>  
> -		if (i < drhd->devices_cnt)
> +		if (i < drhd->scopes_cnt)
>  			continue;
>  
>  		/* bypass IOMMU if it is just for gfx devices */
>  		drhd->ignored = 1;
> -		for (i = 0; i < drhd->devices_cnt; i++) {
> -			if (!drhd->devices[i])
> +		for (i = 0; i < drhd->scopes_cnt; i++) {
> +			pdev = dmar_get_scope_dev(drhd->scopes[i],
> +						  drhd->segment);
> +			if (!pdev)
>  				continue;
> -			drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
> +			pdev->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
> +			pci_dev_put(pdev);
>  		}
>  	}
>  }
> diff --git a/include/linux/dmar.h b/include/linux/dmar.h
> index 69a6fba..70b7e3c 100644
> --- a/include/linux/dmar.h
> +++ b/include/linux/dmar.h
> @@ -32,8 +32,8 @@ struct dmar_drhd_unit {
>  	struct list_head list;		/* list of drhd units	*/
>  	struct  acpi_dmar_header *hdr;	/* ACPI header		*/
>  	u64	reg_base_addr;		/* register base address*/
> -	struct	pci_dev **devices; 	/* target device array	*/
> -	int	devices_cnt;		/* target device count	*/
> +	struct	acpi_dmar_device_scope **scopes; /* target scope array	*/
> +	int	scopes_cnt;		/* target scope count	*/
>  	u16	segment;		/* PCI domain		*/
>  	u8	ignored:1; 		/* ignore drhd		*/
>  	u8	include_all:1;
> @@ -55,6 +55,9 @@ extern struct list_head dmar_drhd_units;
>  
>  extern int dmar_table_init(void);
>  extern int dmar_dev_scope_init(void);
> +extern int dmar_match_scope(struct acpi_dmar_device_scope **, int,
> +			    struct pci_dev *, u16);
> +extern struct pci_dev *dmar_get_scope_dev(struct acpi_dmar_device_scope *, u16);
>  
>  /* Intel IOMMU detection */
>  extern void detect_intel_iommu(void);
> @@ -72,6 +75,20 @@ static inline int dmar_table_init(void)
>  {
>  	return -ENODEV;
>  }
> +
> +static inline int dmar_match_scope(struct acpi_dmar_device_scope **scopes,
> +				   int cnt, struct pci_dev *dev, u16 segment)
> +{
> +	return 0;
> +}
> +
> +static inline struct pci_dev *dmar_get_scope_dev(
> +					struct acpi_dmar_device_scope *scope,
> +					u16 segment)
> +{
> +	return NULL;
> +}
> +
>  static inline int enable_drhd_fault_handling(void)
>  {
>  	return -1;
> @@ -203,8 +220,8 @@ struct dmar_rmrr_unit {
>  	struct acpi_dmar_header *hdr;	/* ACPI header		*/
>  	u64	base_address;		/* reserved base address*/
>  	u64	end_address;		/* reserved end address */
> -	struct pci_dev **devices;	/* target devices */
> -	int	devices_cnt;		/* target device count */
> +	struct	acpi_dmar_device_scope **scopes; /* target scope array */
> +	int	scopes_cnt;		/* target scope count */
>  };
>  
>  #define for_each_rmrr_units(rmrr) \
> @@ -213,8 +230,8 @@ struct dmar_rmrr_unit {
>  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 */
> +	struct acpi_dmar_device_scope **scopes;	/* target scope array */
> +	int scopes_cnt;		/* target scope count */
>  	u8 include_all:1;		/* include all ports */
>  };
>  
> 

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


[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux