diff options
author | Yu Zhao <yu.zhao@intel.com> | 2009-03-19 23:25:16 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-03-20 13:48:28 -0400 |
commit | 74bb1bcc7dbbc9ddef773bf3395d7ff92aaaad2e (patch) | |
tree | 38dd25aed251b00a4b34612320beb64f4a058814 /drivers/pci | |
parent | dd7cc44d0bcec5e9c42fe52e88dc254ae62eac8d (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')
-rw-r--r-- | drivers/pci/iov.c | 119 | ||||
-rw-r--r-- | drivers/pci/pci.h | 4 |
2 files changed, 123 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 | ||
182 | static 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 | |||
202 | static 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 | |||
230 | static 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 | |||
262 | static 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 | |||
182 | static int sriov_enable(struct pci_dev *dev, int nr_virtfn) | 273 | static 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 | } |
561 | EXPORT_SYMBOL_GPL(pci_disable_sriov); | 661 | EXPORT_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 | */ | ||
673 | irqreturn_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 | } | ||
680 | EXPORT_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 |