diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 54ec841e3e18cd53d21f7da88ca6f697cb8d68f2..4fe91d7410529e93ed1595c4e8de7d2e08eaf9d5 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -17,6 +17,8 @@ #define VIRTFN_ID_LEN 17 /* "virtfn%u\0" for 2^32 - 1 */ +static DEFINE_MUTEX(pci_sriov_numvfs_lock); + int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id) { if (!dev->is_physfn) @@ -358,6 +360,16 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) return rc; } +int pci_iov_add_virtfn_locked(struct pci_dev *dev, int id) +{ + int rc; + + mutex_lock(&pci_sriov_numvfs_lock); + rc = pci_iov_add_virtfn(dev, id); + mutex_unlock(&pci_sriov_numvfs_lock); + return rc; +} + void pci_iov_remove_virtfn(struct pci_dev *dev, int id) { char buf[VIRTFN_ID_LEN]; @@ -390,6 +402,13 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id) } } +void pci_iov_remove_virtfn_locked(struct pci_dev *dev, int id) +{ + mutex_lock(&pci_sriov_numvfs_lock); + pci_iov_remove_virtfn(dev, id); + mutex_unlock(&pci_sriov_numvfs_lock); +} + static ssize_t sriov_totalvfs_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -591,14 +610,21 @@ static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) return 0; for (i = 0; i < num_vfs; i++) { - rc = pci_iov_add_virtfn(dev, i); + if (dev->bus->number != pci_iov_virtfn_bus(dev, i)) + rc = pci_iov_add_virtfn_locked(dev, i); + else + rc = pci_iov_add_virtfn(dev, i); if (rc) goto failed; } return 0; failed: - while (i--) - pci_iov_remove_virtfn(dev, i); + while (i--) { + if (dev->bus->number != pci_iov_virtfn_bus(dev, i)) + pci_iov_remove_virtfn_locked(dev, i); + else + pci_iov_remove_virtfn(dev, i); + } return rc; } @@ -718,8 +744,12 @@ static void sriov_del_vfs(struct pci_dev *dev) struct pci_sriov *iov = dev->sriov; int i; - for (i = 0; i < iov->num_VFs; i++) - pci_iov_remove_virtfn(dev, i); + for (i = 0; i < iov->num_VFs; i++) { + if (dev->bus->number != pci_iov_virtfn_bus(dev, i)) + pci_iov_remove_virtfn_locked(dev, i); + else + pci_iov_remove_virtfn(dev, i); + } } static void sriov_disable(struct pci_dev *dev) diff --git a/include/linux/pci.h b/include/linux/pci.h index 9b1b2265da1f7e707c3b0c77c5c6cded6028725f..96fe2624b8cee7d753053889de4e101e497c89c8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2376,7 +2376,9 @@ void pci_disable_sriov(struct pci_dev *dev); int pci_iov_sysfs_link(struct pci_dev *dev, struct pci_dev *virtfn, int id); int pci_iov_add_virtfn(struct pci_dev *dev, int id); +int pci_iov_add_virtfn_locked(struct pci_dev *dev, int id); void pci_iov_remove_virtfn(struct pci_dev *dev, int id); +void pci_iov_remove_virtfn_locked(struct pci_dev *dev, int id); int pci_num_vf(struct pci_dev *dev); int pci_vfs_assigned(struct pci_dev *dev); int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); @@ -2422,8 +2424,14 @@ static inline int pci_iov_add_virtfn(struct pci_dev *dev, int id) { return -ENOSYS; } +static inline int pci_iov_add_virtfn_locked(struct pci_dev *dev, int id) +{ + return -ENOSYS; +} static inline void pci_iov_remove_virtfn(struct pci_dev *dev, int id) { } +static inline void pci_iov_remove_virtfn_locked(struct pci_dev *dev, + int id) { } static inline void pci_disable_sriov(struct pci_dev *dev) { } static inline int pci_num_vf(struct pci_dev *dev) { return 0; } static inline int pci_vfs_assigned(struct pci_dev *dev)