forked from mirrors/linux
		
	PCI/IOV: Add pci_iov_get_pf_drvdata() to allow VF reaching the drvdata of a PF
There are some cases where a SR-IOV VF driver will need to reach into and interact with the PF driver. This requires accessing the drvdata of the PF. Provide a function pci_iov_get_pf_drvdata() to return this PF drvdata in a safe way. Normally accessing a drvdata of a foreign struct device would be done using the device_lock() to protect against device driver probe()/remove() races. However, due to the design of pci_enable_sriov() this will result in a ABBA deadlock on the device_lock as the PF's device_lock is held during PF sriov_configure() while calling pci_enable_sriov() which in turn holds the VF's device_lock while calling VF probe(), and similarly for remove. This means the VF driver can never obtain the PF's device_lock. Instead use the implicit locking created by pci_enable/disable_sriov(). A VF driver can access its PF drvdata only while its own driver is attached, and the PF driver can control access to its own drvdata based on when it calls pci_enable/disable_sriov(). To use this API the PF driver will setup the PF drvdata in the probe() function. pci_enable_sriov() is only called from sriov_configure() which cannot happen until probe() completes, ensuring no VF races with drvdata setup. For removal, the PF driver must call pci_disable_sriov() in its remove function before destroying any of the drvdata. This ensures that all VF drivers are unbound before returning, fencing concurrent access to the drvdata. The introduction of a new function to do this access makes clear the special locking scheme and the documents the requirements on the PF/VF drivers using this. Link: https://lore.kernel.org/all/20220224142024.147653-5-yishaih@nvidia.com Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Signed-off-by: Yishai Hadas <yishaih@nvidia.com> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
This commit is contained in:
		
							parent
							
								
									143a41d762
								
							
						
					
					
						commit
						a7e9f240c0
					
				
					 2 changed files with 36 additions and 0 deletions
				
			
		| 
						 | 
					@ -47,6 +47,35 @@ int pci_iov_vf_id(struct pci_dev *dev)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(pci_iov_vf_id);
 | 
					EXPORT_SYMBOL_GPL(pci_iov_vf_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * pci_iov_get_pf_drvdata - Return the drvdata of a PF
 | 
				
			||||||
 | 
					 * @dev - VF pci_dev
 | 
				
			||||||
 | 
					 * @pf_driver - Device driver required to own the PF
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This must be called from a context that ensures that a VF driver is attached.
 | 
				
			||||||
 | 
					 * The value returned is invalid once the VF driver completes its remove()
 | 
				
			||||||
 | 
					 * callback.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Locking is achieved by the driver core. A VF driver cannot be probed until
 | 
				
			||||||
 | 
					 * pci_enable_sriov() is called and pci_disable_sriov() does not return until
 | 
				
			||||||
 | 
					 * all VF drivers have completed their remove().
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The PF driver must call pci_disable_sriov() before it begins to destroy the
 | 
				
			||||||
 | 
					 * drvdata.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void *pci_iov_get_pf_drvdata(struct pci_dev *dev, struct pci_driver *pf_driver)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pci_dev *pf_dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dev->is_virtfn)
 | 
				
			||||||
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
						pf_dev = dev->physfn;
 | 
				
			||||||
 | 
						if (pf_dev->driver != pf_driver)
 | 
				
			||||||
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
						return pci_get_drvdata(pf_dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(pci_iov_get_pf_drvdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may
 | 
					 * Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may
 | 
				
			||||||
 * change when NumVFs changes.
 | 
					 * change when NumVFs changes.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2167,6 +2167,7 @@ void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar);
 | 
				
			||||||
int pci_iov_virtfn_bus(struct pci_dev *dev, int id);
 | 
					int pci_iov_virtfn_bus(struct pci_dev *dev, int id);
 | 
				
			||||||
int pci_iov_virtfn_devfn(struct pci_dev *dev, int id);
 | 
					int pci_iov_virtfn_devfn(struct pci_dev *dev, int id);
 | 
				
			||||||
int pci_iov_vf_id(struct pci_dev *dev);
 | 
					int pci_iov_vf_id(struct pci_dev *dev);
 | 
				
			||||||
 | 
					void *pci_iov_get_pf_drvdata(struct pci_dev *dev, struct pci_driver *pf_driver);
 | 
				
			||||||
int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
 | 
					int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
 | 
				
			||||||
void pci_disable_sriov(struct pci_dev *dev);
 | 
					void pci_disable_sriov(struct pci_dev *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2200,6 +2201,12 @@ static inline int pci_iov_vf_id(struct pci_dev *dev)
 | 
				
			||||||
	return -ENOSYS;
 | 
						return -ENOSYS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void *pci_iov_get_pf_drvdata(struct pci_dev *dev,
 | 
				
			||||||
 | 
										   struct pci_driver *pf_driver)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
 | 
					static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
 | 
				
			||||||
{ return -ENODEV; }
 | 
					{ return -ENODEV; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue