summaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2016-02-22 18:02:36 -0500
committerAlex Williamson <alex.williamson@redhat.com>2016-02-22 18:10:09 -0500
commit188ad9d6cbbce4a1d322ac208914a1dea34b30b6 (patch)
tree001329c7874b9438e1408bb8d1cf3c4e66da8756 /drivers/vfio
parentff63eb638d63b95e489f976428f1df01391e15e4 (diff)
vfio/pci: Include sparse mmap capability for MSI-X table regions
vfio-pci has never allowed the user to directly mmap the MSI-X vector table, but we've always relied on implicit knowledge of the user that they cannot do this. Now that we have capability chains that we can expose in the region info ioctl and a sparse mmap capability that represents the sub-areas within the region that can be mmap'd, we can make the mmap constraints more explicit. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/pci/vfio_pci.c73
1 files changed, 72 insertions, 1 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 2760a7ba3f30..4682207b1ac8 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -421,6 +421,48 @@ static int vfio_pci_for_each_slot_or_bus(struct pci_dev *pdev,
421 return walk.ret; 421 return walk.ret;
422} 422}
423 423
424static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev,
425 struct vfio_info_cap *caps)
426{
427 struct vfio_info_cap_header *header;
428 struct vfio_region_info_cap_sparse_mmap *sparse;
429 size_t end, size;
430 int nr_areas = 2, i = 0;
431
432 end = pci_resource_len(vdev->pdev, vdev->msix_bar);
433
434 /* If MSI-X table is aligned to the start or end, only one area */
435 if (((vdev->msix_offset & PAGE_MASK) == 0) ||
436 (PAGE_ALIGN(vdev->msix_offset + vdev->msix_size) >= end))
437 nr_areas = 1;
438
439 size = sizeof(*sparse) + (nr_areas * sizeof(*sparse->areas));
440
441 header = vfio_info_cap_add(caps, size,
442 VFIO_REGION_INFO_CAP_SPARSE_MMAP, 1);
443 if (IS_ERR(header))
444 return PTR_ERR(header);
445
446 sparse = container_of(header,
447 struct vfio_region_info_cap_sparse_mmap, header);
448 sparse->nr_areas = nr_areas;
449
450 if (vdev->msix_offset & PAGE_MASK) {
451 sparse->areas[i].offset = 0;
452 sparse->areas[i].size = vdev->msix_offset & PAGE_MASK;
453 i++;
454 }
455
456 if (PAGE_ALIGN(vdev->msix_offset + vdev->msix_size) < end) {
457 sparse->areas[i].offset = PAGE_ALIGN(vdev->msix_offset +
458 vdev->msix_size);
459 sparse->areas[i].size = end - sparse->areas[i].offset;
460 i++;
461 }
462
463 return 0;
464}
465
424static long vfio_pci_ioctl(void *device_data, 466static long vfio_pci_ioctl(void *device_data,
425 unsigned int cmd, unsigned long arg) 467 unsigned int cmd, unsigned long arg)
426{ 468{
@@ -451,6 +493,8 @@ static long vfio_pci_ioctl(void *device_data,
451 } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { 493 } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
452 struct pci_dev *pdev = vdev->pdev; 494 struct pci_dev *pdev = vdev->pdev;
453 struct vfio_region_info info; 495 struct vfio_region_info info;
496 struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
497 int ret;
454 498
455 minsz = offsetofend(struct vfio_region_info, offset); 499 minsz = offsetofend(struct vfio_region_info, offset);
456 500
@@ -479,8 +523,15 @@ static long vfio_pci_ioctl(void *device_data,
479 VFIO_REGION_INFO_FLAG_WRITE; 523 VFIO_REGION_INFO_FLAG_WRITE;
480 if (IS_ENABLED(CONFIG_VFIO_PCI_MMAP) && 524 if (IS_ENABLED(CONFIG_VFIO_PCI_MMAP) &&
481 pci_resource_flags(pdev, info.index) & 525 pci_resource_flags(pdev, info.index) &
482 IORESOURCE_MEM && info.size >= PAGE_SIZE) 526 IORESOURCE_MEM && info.size >= PAGE_SIZE) {
483 info.flags |= VFIO_REGION_INFO_FLAG_MMAP; 527 info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
528 if (info.index == vdev->msix_bar) {
529 ret = msix_sparse_mmap_cap(vdev, &caps);
530 if (ret)
531 return ret;
532 }
533 }
534
484 break; 535 break;
485 case VFIO_PCI_ROM_REGION_INDEX: 536 case VFIO_PCI_ROM_REGION_INDEX:
486 { 537 {
@@ -520,6 +571,26 @@ static long vfio_pci_ioctl(void *device_data,
520 return -EINVAL; 571 return -EINVAL;
521 } 572 }
522 573
574 if (caps.size) {
575 info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
576 if (info.argsz < sizeof(info) + caps.size) {
577 info.argsz = sizeof(info) + caps.size;
578 info.cap_offset = 0;
579 } else {
580 vfio_info_cap_shift(&caps, sizeof(info));
581 ret = copy_to_user((void __user *)arg +
582 sizeof(info), caps.buf,
583 caps.size);
584 if (ret) {
585 kfree(caps.buf);
586 return ret;
587 }
588 info.cap_offset = sizeof(info);
589 }
590
591 kfree(caps.buf);
592 }
593
523 return copy_to_user((void __user *)arg, &info, minsz); 594 return copy_to_user((void __user *)arg, &info, minsz);
524 595
525 } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { 596 } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {