diff options
author | Jiang Liu <liuj97@gmail.com> | 2013-05-25 09:48:37 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-06-14 19:39:40 -0400 |
commit | dc087f2f6a2925e81831f3016b9cbb6e470e7423 (patch) | |
tree | 28d1e60368d9e13b4716f5ecceb1af36565dea07 /drivers/pci/iov.c | |
parent | d35329d9f17f05277f2718eb54402dea3e833d19 (diff) |
PCI: Simplify IOV implementation and fix reference count races
Trivial changes to IOV:
1) use new PCI interfaces to simplify IOV implementation
2) fix some reference count related race windows
[bhelgaas: fix virtfn_add() add bus/alloc dev error paths]
Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Donald Dutile <ddutile@redhat.com>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Ram Pai <linuxram@us.ibm.com>
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r-- | drivers/pci/iov.c | 59 |
1 files changed, 24 insertions, 35 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 8f1e1174b71f..5fffca995a91 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c | |||
@@ -51,24 +51,16 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) | |||
51 | return child; | 51 | return child; |
52 | } | 52 | } |
53 | 53 | ||
54 | static void virtfn_remove_bus(struct pci_bus *bus, int busnr) | 54 | static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) |
55 | { | 55 | { |
56 | struct pci_bus *child; | 56 | if (physbus != virtbus && list_empty(&virtbus->devices)) |
57 | 57 | pci_remove_bus(virtbus); | |
58 | if (bus->number == busnr) | ||
59 | return; | ||
60 | |||
61 | child = pci_find_bus(pci_domain_nr(bus), busnr); | ||
62 | BUG_ON(!child); | ||
63 | |||
64 | if (list_empty(&child->devices)) | ||
65 | pci_remove_bus(child); | ||
66 | } | 58 | } |
67 | 59 | ||
68 | static int virtfn_add(struct pci_dev *dev, int id, int reset) | 60 | static int virtfn_add(struct pci_dev *dev, int id, int reset) |
69 | { | 61 | { |
70 | int i; | 62 | int i; |
71 | int rc; | 63 | int rc = -ENOMEM; |
72 | u64 size; | 64 | u64 size; |
73 | char buf[VIRTFN_ID_LEN]; | 65 | char buf[VIRTFN_ID_LEN]; |
74 | struct pci_dev *virtfn; | 66 | struct pci_dev *virtfn; |
@@ -76,18 +68,15 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) | |||
76 | struct pci_sriov *iov = dev->sriov; | 68 | struct pci_sriov *iov = dev->sriov; |
77 | struct pci_bus *bus; | 69 | struct pci_bus *bus; |
78 | 70 | ||
79 | virtfn = pci_alloc_dev(NULL); | ||
80 | if (!virtfn) | ||
81 | return -ENOMEM; | ||
82 | |||
83 | mutex_lock(&iov->dev->sriov->lock); | 71 | mutex_lock(&iov->dev->sriov->lock); |
84 | bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); | 72 | bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); |
85 | if (!bus) { | 73 | if (!bus) |
86 | kfree(virtfn); | 74 | goto failed; |
87 | mutex_unlock(&iov->dev->sriov->lock); | 75 | |
88 | return -ENOMEM; | 76 | virtfn = pci_alloc_dev(bus); |
89 | } | 77 | if (!virtfn) |
90 | virtfn->bus = pci_bus_get(bus); | 78 | goto failed0; |
79 | |||
91 | virtfn->devfn = virtfn_devfn(dev, id); | 80 | virtfn->devfn = virtfn_devfn(dev, id); |
92 | virtfn->vendor = dev->vendor; | 81 | virtfn->vendor = dev->vendor; |
93 | pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); | 82 | pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); |
@@ -136,7 +125,9 @@ failed1: | |||
136 | pci_dev_put(dev); | 125 | pci_dev_put(dev); |
137 | mutex_lock(&iov->dev->sriov->lock); | 126 | mutex_lock(&iov->dev->sriov->lock); |
138 | pci_stop_and_remove_bus_device(virtfn); | 127 | pci_stop_and_remove_bus_device(virtfn); |
139 | virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); | 128 | failed0: |
129 | virtfn_remove_bus(dev->bus, bus); | ||
130 | failed: | ||
140 | mutex_unlock(&iov->dev->sriov->lock); | 131 | mutex_unlock(&iov->dev->sriov->lock); |
141 | 132 | ||
142 | return rc; | 133 | return rc; |
@@ -145,20 +136,15 @@ failed1: | |||
145 | static void virtfn_remove(struct pci_dev *dev, int id, int reset) | 136 | static void virtfn_remove(struct pci_dev *dev, int id, int reset) |
146 | { | 137 | { |
147 | char buf[VIRTFN_ID_LEN]; | 138 | char buf[VIRTFN_ID_LEN]; |
148 | struct pci_bus *bus; | ||
149 | struct pci_dev *virtfn; | 139 | struct pci_dev *virtfn; |
150 | struct pci_sriov *iov = dev->sriov; | 140 | struct pci_sriov *iov = dev->sriov; |
151 | 141 | ||
152 | bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id)); | 142 | virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), |
153 | if (!bus) | 143 | virtfn_bus(dev, id), |
154 | return; | 144 | virtfn_devfn(dev, id)); |
155 | |||
156 | virtfn = pci_get_slot(bus, virtfn_devfn(dev, id)); | ||
157 | if (!virtfn) | 145 | if (!virtfn) |
158 | return; | 146 | return; |
159 | 147 | ||
160 | pci_dev_put(virtfn); | ||
161 | |||
162 | if (reset) { | 148 | if (reset) { |
163 | device_release_driver(&virtfn->dev); | 149 | device_release_driver(&virtfn->dev); |
164 | __pci_reset_function(virtfn); | 150 | __pci_reset_function(virtfn); |
@@ -176,9 +162,11 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) | |||
176 | 162 | ||
177 | mutex_lock(&iov->dev->sriov->lock); | 163 | mutex_lock(&iov->dev->sriov->lock); |
178 | pci_stop_and_remove_bus_device(virtfn); | 164 | pci_stop_and_remove_bus_device(virtfn); |
179 | virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); | 165 | virtfn_remove_bus(dev->bus, virtfn->bus); |
180 | mutex_unlock(&iov->dev->sriov->lock); | 166 | mutex_unlock(&iov->dev->sriov->lock); |
181 | 167 | ||
168 | /* balance pci_get_domain_bus_and_slot() */ | ||
169 | pci_dev_put(virtfn); | ||
182 | pci_dev_put(dev); | 170 | pci_dev_put(dev); |
183 | } | 171 | } |
184 | 172 | ||
@@ -335,13 +323,14 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) | |||
335 | if (!pdev) | 323 | if (!pdev) |
336 | return -ENODEV; | 324 | return -ENODEV; |
337 | 325 | ||
338 | pci_dev_put(pdev); | 326 | if (!pdev->is_physfn) { |
339 | 327 | pci_dev_put(pdev); | |
340 | if (!pdev->is_physfn) | ||
341 | return -ENODEV; | 328 | return -ENODEV; |
329 | } | ||
342 | 330 | ||
343 | rc = sysfs_create_link(&dev->dev.kobj, | 331 | rc = sysfs_create_link(&dev->dev.kobj, |
344 | &pdev->dev.kobj, "dep_link"); | 332 | &pdev->dev.kobj, "dep_link"); |
333 | pci_dev_put(pdev); | ||
345 | if (rc) | 334 | if (rc) |
346 | return rc; | 335 | return rc; |
347 | } | 336 | } |