aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/iov.c119
-rw-r--r--drivers/pci/pci.h4
-rw-r--r--include/linux/pci.h6
3 files changed, 129 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);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0f1c7d103509..22dcfdb75d91 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1,6 +1,8 @@
1#ifndef DRIVERS_PCI_H 1#ifndef DRIVERS_PCI_H
2#define DRIVERS_PCI_H 2#define DRIVERS_PCI_H
3 3
4#include <linux/workqueue.h>
5
4#define PCI_CFG_SPACE_SIZE 256 6#define PCI_CFG_SPACE_SIZE 256
5#define PCI_CFG_SPACE_EXP_SIZE 4096 7#define PCI_CFG_SPACE_EXP_SIZE 4096
6 8
@@ -218,6 +220,8 @@ struct pci_sriov {
218 struct pci_dev *dev; /* lowest numbered PF */ 220 struct pci_dev *dev; /* lowest numbered PF */
219 struct pci_dev *self; /* this PF */ 221 struct pci_dev *self; /* this PF */
220 struct mutex lock; /* lock for VF bus */ 222 struct mutex lock; /* lock for VF bus */
223 struct work_struct mtask; /* VF Migration task */
224 u8 __iomem *mstate; /* VF Migration State Array */
221}; 225};
222 226
223#ifdef CONFIG_PCI_IOV 227#ifdef CONFIG_PCI_IOV
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c2e491e04063..1216843412da 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -52,6 +52,7 @@
52#include <asm/atomic.h> 52#include <asm/atomic.h>
53#include <linux/device.h> 53#include <linux/device.h>
54#include <linux/io.h> 54#include <linux/io.h>
55#include <linux/irqreturn.h>
55 56
56/* Include the ID list */ 57/* Include the ID list */
57#include <linux/pci_ids.h> 58#include <linux/pci_ids.h>
@@ -1219,6 +1220,7 @@ void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar);
1219#ifdef CONFIG_PCI_IOV 1220#ifdef CONFIG_PCI_IOV
1220extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); 1221extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
1221extern void pci_disable_sriov(struct pci_dev *dev); 1222extern void pci_disable_sriov(struct pci_dev *dev);
1223extern irqreturn_t pci_sriov_migration(struct pci_dev *dev);
1222#else 1224#else
1223static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 1225static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
1224{ 1226{
@@ -1227,6 +1229,10 @@ static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
1227static inline void pci_disable_sriov(struct pci_dev *dev) 1229static inline void pci_disable_sriov(struct pci_dev *dev)
1228{ 1230{
1229} 1231}
1232static inline irqreturn_t pci_sriov_migration(struct pci_dev *dev)
1233{
1234 return IRQ_NONE;
1235}
1230#endif 1236#endif
1231 1237
1232#endif /* __KERNEL__ */ 1238#endif /* __KERNEL__ */