aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/iov.c
diff options
context:
space:
mode:
authorYu Zhao <yu.zhao@intel.com>2009-03-19 23:25:16 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-03-20 13:48:28 -0400
commit74bb1bcc7dbbc9ddef773bf3395d7ff92aaaad2e (patch)
tree38dd25aed251b00a4b34612320beb64f4a058814 /drivers/pci/iov.c
parentdd7cc44d0bcec5e9c42fe52e88dc254ae62eac8d (diff)
PCI: handle SR-IOV Virtual Function Migration
Add or remove a Virtual Function after receiving a Migrate In or Out Request. Reviewed-by: Matthew Wilcox <willy@linux.intel.com> Signed-off-by: Yu Zhao <yu.zhao@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r--drivers/pci/iov.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index d0ff8ad8f7ba..7227efc760db 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -179,6 +179,97 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
179 pci_dev_put(dev); 179 pci_dev_put(dev);
180} 180}
181 181
182static int sriov_migration(struct pci_dev *dev)
183{
184 u16 status;
185 struct pci_sriov *iov = dev->sriov;
186
187 if (!iov->nr_virtfn)
188 return 0;
189
190 if (!(iov->cap & PCI_SRIOV_CAP_VFM))
191 return 0;
192
193 pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status);
194 if (!(status & PCI_SRIOV_STATUS_VFM))
195 return 0;
196
197 schedule_work(&iov->mtask);
198
199 return 1;
200}
201
202static void sriov_migration_task(struct work_struct *work)
203{
204 int i;
205 u8 state;
206 u16 status;
207 struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask);
208
209 for (i = iov->initial; i < iov->nr_virtfn; i++) {
210 state = readb(iov->mstate + i);
211 if (state == PCI_SRIOV_VFM_MI) {
212 writeb(PCI_SRIOV_VFM_AV, iov->mstate + i);
213 state = readb(iov->mstate + i);
214 if (state == PCI_SRIOV_VFM_AV)
215 virtfn_add(iov->self, i, 1);
216 } else if (state == PCI_SRIOV_VFM_MO) {
217 virtfn_remove(iov->self, i, 1);
218 writeb(PCI_SRIOV_VFM_UA, iov->mstate + i);
219 state = readb(iov->mstate + i);
220 if (state == PCI_SRIOV_VFM_AV)
221 virtfn_add(iov->self, i, 0);
222 }
223 }
224
225 pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status);
226 status &= ~PCI_SRIOV_STATUS_VFM;
227 pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status);
228}
229
230static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn)
231{
232 int bir;
233 u32 table;
234 resource_size_t pa;
235 struct pci_sriov *iov = dev->sriov;
236
237 if (nr_virtfn <= iov->initial)
238 return 0;
239
240 pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table);
241 bir = PCI_SRIOV_VFM_BIR(table);
242 if (bir > PCI_STD_RESOURCE_END)
243 return -EIO;
244
245 table = PCI_SRIOV_VFM_OFFSET(table);
246 if (table + nr_virtfn > pci_resource_len(dev, bir))
247 return -EIO;
248
249 pa = pci_resource_start(dev, bir) + table;
250 iov->mstate = ioremap(pa, nr_virtfn);
251 if (!iov->mstate)
252 return -ENOMEM;
253
254 INIT_WORK(&iov->mtask, sriov_migration_task);
255
256 iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR;
257 pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
258
259 return 0;
260}
261
262static void sriov_disable_migration(struct pci_dev *dev)
263{
264 struct pci_sriov *iov = dev->sriov;
265
266 iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR);
267 pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
268
269 cancel_work_sync(&iov->mtask);
270 iounmap(iov->mstate);
271}
272
182static int sriov_enable(struct pci_dev *dev, int nr_virtfn) 273static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
183{ 274{
184 int rc; 275 int rc;
@@ -261,6 +352,12 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
261 goto failed; 352 goto failed;
262 } 353 }
263 354
355 if (iov->cap & PCI_SRIOV_CAP_VFM) {
356 rc = sriov_enable_migration(dev, nr_virtfn);
357 if (rc)
358 goto failed;
359 }
360
264 kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 361 kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
265 iov->nr_virtfn = nr_virtfn; 362 iov->nr_virtfn = nr_virtfn;
266 363
@@ -290,6 +387,9 @@ static void sriov_disable(struct pci_dev *dev)
290 if (!iov->nr_virtfn) 387 if (!iov->nr_virtfn)
291 return; 388 return;
292 389
390 if (iov->cap & PCI_SRIOV_CAP_VFM)
391 sriov_disable_migration(dev);
392
293 for (i = 0; i < iov->nr_virtfn; i++) 393 for (i = 0; i < iov->nr_virtfn; i++)
294 virtfn_remove(dev, i, 0); 394 virtfn_remove(dev, i, 0);
295 395
@@ -559,3 +659,22 @@ void pci_disable_sriov(struct pci_dev *dev)
559 sriov_disable(dev); 659 sriov_disable(dev);
560} 660}
561EXPORT_SYMBOL_GPL(pci_disable_sriov); 661EXPORT_SYMBOL_GPL(pci_disable_sriov);
662
663/**
664 * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration
665 * @dev: the PCI device
666 *
667 * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not.
668 *
669 * Physical Function driver is responsible to register IRQ handler using
670 * VF Migration Interrupt Message Number, and call this function when the
671 * interrupt is generated by the hardware.
672 */
673irqreturn_t pci_sriov_migration(struct pci_dev *dev)
674{
675 if (!dev->is_physfn)
676 return IRQ_NONE;
677
678 return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE;
679}
680EXPORT_SYMBOL_GPL(pci_sriov_migration);