From: Keith Busch <kbusch@xxxxxxxxxx> Write combining can be provide performance improvement for places that can safely use this capability. Previous discussions on the topic suggest a vfio user needs to explicitly request such a mapping, and it sounds like a new vfio specific ioctl to request this is one way recommended way to do that. This patch implements a new ioctl to achieve that so a user can request write combining on prefetchable memory. A new ioctl seems a bit much for just this purpose, so the implementation here provides a "flags" field with only the write combine option defined. The rest of the bits are reserved for future use. Link: https://lore.kernel.org/all/20171009025000.39435-1-aik@xxxxxxxxx/ Link: https://lore.kernel.org/lkml/ZLFBnACjoTbDmKuU@xxxxxxxxxx/ Signed-off-by: Keith Busch <kbusch@xxxxxxxxxx> --- drivers/vfio/pci/vfio_pci_core.c | 39 +++++++++++++++++++++++++++++++- include/linux/vfio_pci_core.h | 1 + include/uapi/linux/vfio.h | 17 ++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index ba0ce0075b2fb..c275c95eafe32 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1042,12 +1042,18 @@ static int vfio_pci_ioctl_get_region_info(struct vfio_pci_core_device *vdev, info.flags = VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE; if (vdev->bar_mmap_supported[info.index]) { + struct resource *res; + info.flags |= VFIO_REGION_INFO_FLAG_MMAP; if (info.index == vdev->msix_bar) { ret = msix_mmappable_cap(vdev, &caps); if (ret) return ret; } + + res = &vdev->pdev->resource[index]; + if (res->flags & IORESOURCE_PREFETCH) + info.flags |= VFIO_REGION_INFO_FLAG_PREFETCH; } break; @@ -1223,6 +1229,32 @@ static int vfio_pci_ioctl_set_irqs(struct vfio_pci_core_device *vdev, return ret; } +static int vfio_pci_ioctl_set_region_flags(struct vfio_pci_core_device *vdev, + struct vfio_region_flags __user *arg) +{ + struct vfio_region_flags region_flags; + struct resource *res; + u32 index; + + if (copy_from_user(®ion_flags, arg, sizeof(region_flags))) + return -EFAULT; + + index = region_flags.index; + if (index >= PCI_STD_NUM_BARS) + return -EINVAL; + + if (region_flags.flags & VFIO_REGION_FLAG_WRITE_COMBINE) { + res = &vdev->pdev->resource[index]; + if (!(res->flags & IORESOURCE_MEM) || + !(res->flags & IORESOURCE_PREFETCH)) + return -EINVAL; + vdev->bar_write_combine[index] = true; + } else + vdev->bar_write_combine[index] = false; + + return 0; +} + static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev, void __user *arg) { @@ -1484,6 +1516,8 @@ long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, return vfio_pci_ioctl_reset(vdev, uarg); case VFIO_DEVICE_SET_IRQS: return vfio_pci_ioctl_set_irqs(vdev, uarg); + case VFIO_DEVICE_SET_REGION_FLAGS: + return vfio_pci_ioctl_set_region_flags(vdev, uarg); default: return -ENOTTY; } @@ -1756,7 +1790,10 @@ int vfio_pci_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma } vma->vm_private_data = vdev; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (vdev->bar_write_combine[index]) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); /* diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index fbb472dd99b36..0e0122ce4196a 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -54,6 +54,7 @@ struct vfio_pci_core_device { struct pci_dev *pdev; void __iomem *barmap[PCI_STD_NUM_BARS]; bool bar_mmap_supported[PCI_STD_NUM_BARS]; + bool bar_write_combine[PCI_STD_NUM_BARS]; u8 *pci_config_map; u8 *vconfig; struct perm_bits *msi_perm; diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 2b68e6cdf1902..5537b20b23541 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -275,6 +275,7 @@ struct vfio_region_info { #define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */ #define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */ #define VFIO_REGION_INFO_FLAG_CAPS (1 << 3) /* Info supports caps */ +#define VFIO_REGION_INFO_FLAG_PREFETCH (1 << 4) /* Region is prefetchable */ __u32 index; /* Region index */ __u32 cap_offset; /* Offset within info struct of first cap */ __aligned_u64 size; /* Region size (bytes) */ @@ -1821,6 +1822,22 @@ struct vfio_iommu_spapr_tce_remove { }; #define VFIO_IOMMU_SPAPR_TCE_REMOVE _IO(VFIO_TYPE, VFIO_BASE + 20) +/** + * VFIO_DEVICE_SET_REGION_FLAGS - _IOW(VFIO_TYPE, VFIO_BASE + 21, struct vfio_region_flags) + * + * Set mapping options for the region + * + * Flags supported: + * - VFIO_REGION_FLAG_WRITE_COMBINE: use write-combine when requested to map + * this region. Supported only if the region is prefetchable. + */ +struct vfio_region_flags { + __u32 index; /* Region index */ + __u32 flags; /* Region flags */ +#define VFIO_REGION_FLAG_WRITE_COMBINE (1 << 0) +}; +#define VFIO_DEVICE_SET_REGION_FLAGS _IO(VFIO_TYPE, VFIO_BASE + 21) + /* ***************************************************************** */ #endif /* _UAPIVFIO_H */ -- 2.43.5