aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2013-09-04 13:28:04 -0400
committerAlex Williamson <alex.williamson@redhat.com>2013-09-04 13:28:04 -0400
commit8b27ee60bfd6bbb84d2df28fa706c5c5081066ca (patch)
tree1fab334bc5bfdc157df746cda06ad328cc1a5208
parent3bc4f3993b93dbf1f6402e2034a2e20eb07db807 (diff)
vfio-pci: PCI hot reset interface
The current VFIO_DEVICE_RESET interface only maps to PCI use cases where we can isolate the reset to the individual PCI function. This means the device must support FLR (PCIe or AF), PM reset on D3hot->D0 transition, device specific reset, or be a singleton device on a bus for a secondary bus reset. FLR does not have widespread support, PM reset is not very reliable, and bus topology is dictated by the system and device design. We need to provide a means for a user to induce a bus reset in cases where the existing mechanisms are not available or not reliable. This device specific extension to VFIO provides the user with this ability. Two new ioctls are introduced: - VFIO_DEVICE_PCI_GET_HOT_RESET_INFO - VFIO_DEVICE_PCI_HOT_RESET The first provides the user with information about the extent of devices affected by a hot reset. This is essentially a list of devices and the IOMMU groups they belong to. The user may then initiate a hot reset by calling the second ioctl. We must be careful that the user has ownership of all the affected devices found via the first ioctl, so the second ioctl takes a list of file descriptors for the VFIO groups affected by the reset. Each group must have IOMMU protection established for the ioctl to succeed. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
-rw-r--r--drivers/vfio/pci/vfio_pci.c286
-rw-r--r--include/uapi/linux/vfio.h38
2 files changed, 323 insertions, 1 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index cef6002acbd4..6ab71b9fcf8d 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -13,6 +13,7 @@
13 13
14#include <linux/device.h> 14#include <linux/device.h>
15#include <linux/eventfd.h> 15#include <linux/eventfd.h>
16#include <linux/file.h>
16#include <linux/interrupt.h> 17#include <linux/interrupt.h>
17#include <linux/iommu.h> 18#include <linux/iommu.h>
18#include <linux/module.h> 19#include <linux/module.h>
@@ -227,6 +228,110 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
227 return 0; 228 return 0;
228} 229}
229 230
231static int vfio_pci_count_devs(struct pci_dev *pdev, void *data)
232{
233 (*(int *)data)++;
234 return 0;
235}
236
237struct vfio_pci_fill_info {
238 int max;
239 int cur;
240 struct vfio_pci_dependent_device *devices;
241};
242
243static int vfio_pci_fill_devs(struct pci_dev *pdev, void *data)
244{
245 struct vfio_pci_fill_info *fill = data;
246 struct iommu_group *iommu_group;
247
248 if (fill->cur == fill->max)
249 return -EAGAIN; /* Something changed, try again */
250
251 iommu_group = iommu_group_get(&pdev->dev);
252 if (!iommu_group)
253 return -EPERM; /* Cannot reset non-isolated devices */
254
255 fill->devices[fill->cur].group_id = iommu_group_id(iommu_group);
256 fill->devices[fill->cur].segment = pci_domain_nr(pdev->bus);
257 fill->devices[fill->cur].bus = pdev->bus->number;
258 fill->devices[fill->cur].devfn = pdev->devfn;
259 fill->cur++;
260 iommu_group_put(iommu_group);
261 return 0;
262}
263
264struct vfio_pci_group_entry {
265 struct vfio_group *group;
266 int id;
267};
268
269struct vfio_pci_group_info {
270 int count;
271 struct vfio_pci_group_entry *groups;
272};
273
274static int vfio_pci_validate_devs(struct pci_dev *pdev, void *data)
275{
276 struct vfio_pci_group_info *info = data;
277 struct iommu_group *group;
278 int id, i;
279
280 group = iommu_group_get(&pdev->dev);
281 if (!group)
282 return -EPERM;
283
284 id = iommu_group_id(group);
285
286 for (i = 0; i < info->count; i++)
287 if (info->groups[i].id == id)
288 break;
289
290 iommu_group_put(group);
291
292 return (i == info->count) ? -EINVAL : 0;
293}
294
295static bool vfio_pci_dev_below_slot(struct pci_dev *pdev, struct pci_slot *slot)
296{
297 for (; pdev; pdev = pdev->bus->self)
298 if (pdev->bus == slot->bus)
299 return (pdev->slot == slot);
300 return false;
301}
302
303struct vfio_pci_walk_info {
304 int (*fn)(struct pci_dev *, void *data);
305 void *data;
306 struct pci_dev *pdev;
307 bool slot;
308 int ret;
309};
310
311static int vfio_pci_walk_wrapper(struct pci_dev *pdev, void *data)
312{
313 struct vfio_pci_walk_info *walk = data;
314
315 if (!walk->slot || vfio_pci_dev_below_slot(pdev, walk->pdev->slot))
316 walk->ret = walk->fn(pdev, walk->data);
317
318 return walk->ret;
319}
320
321static int vfio_pci_for_each_slot_or_bus(struct pci_dev *pdev,
322 int (*fn)(struct pci_dev *,
323 void *data), void *data,
324 bool slot)
325{
326 struct vfio_pci_walk_info walk = {
327 .fn = fn, .data = data, .pdev = pdev, .slot = slot, .ret = 0,
328 };
329
330 pci_walk_bus(pdev->bus, vfio_pci_walk_wrapper, &walk);
331
332 return walk.ret;
333}
334
230static long vfio_pci_ioctl(void *device_data, 335static long vfio_pci_ioctl(void *device_data,
231 unsigned int cmd, unsigned long arg) 336 unsigned int cmd, unsigned long arg)
232{ 337{
@@ -407,10 +512,189 @@ static long vfio_pci_ioctl(void *device_data,
407 512
408 return ret; 513 return ret;
409 514
410 } else if (cmd == VFIO_DEVICE_RESET) 515 } else if (cmd == VFIO_DEVICE_RESET) {
411 return vdev->reset_works ? 516 return vdev->reset_works ?
412 pci_reset_function(vdev->pdev) : -EINVAL; 517 pci_reset_function(vdev->pdev) : -EINVAL;
413 518
519 } else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
520 struct vfio_pci_hot_reset_info hdr;
521 struct vfio_pci_fill_info fill = { 0 };
522 struct vfio_pci_dependent_device *devices = NULL;
523 bool slot = false;
524 int ret = 0;
525
526 minsz = offsetofend(struct vfio_pci_hot_reset_info, count);
527
528 if (copy_from_user(&hdr, (void __user *)arg, minsz))
529 return -EFAULT;
530
531 if (hdr.argsz < minsz)
532 return -EINVAL;
533
534 hdr.flags = 0;
535
536 /* Can we do a slot or bus reset or neither? */
537 if (!pci_probe_reset_slot(vdev->pdev->slot))
538 slot = true;
539 else if (pci_probe_reset_bus(vdev->pdev->bus))
540 return -ENODEV;
541
542 /* How many devices are affected? */
543 ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
544 vfio_pci_count_devs,
545 &fill.max, slot);
546 if (ret)
547 return ret;
548
549 WARN_ON(!fill.max); /* Should always be at least one */
550
551 /*
552 * If there's enough space, fill it now, otherwise return
553 * -ENOSPC and the number of devices affected.
554 */
555 if (hdr.argsz < sizeof(hdr) + (fill.max * sizeof(*devices))) {
556 ret = -ENOSPC;
557 hdr.count = fill.max;
558 goto reset_info_exit;
559 }
560
561 devices = kcalloc(fill.max, sizeof(*devices), GFP_KERNEL);
562 if (!devices)
563 return -ENOMEM;
564
565 fill.devices = devices;
566
567 ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
568 vfio_pci_fill_devs,
569 &fill, slot);
570
571 /*
572 * If a device was removed between counting and filling,
573 * we may come up short of fill.max. If a device was
574 * added, we'll have a return of -EAGAIN above.
575 */
576 if (!ret)
577 hdr.count = fill.cur;
578
579reset_info_exit:
580 if (copy_to_user((void __user *)arg, &hdr, minsz))
581 ret = -EFAULT;
582
583 if (!ret) {
584 if (copy_to_user((void __user *)(arg + minsz), devices,
585 hdr.count * sizeof(*devices)))
586 ret = -EFAULT;
587 }
588