Commit 28541d41 authored by Alex Williamson's avatar Alex Williamson

vfio/pci: Add infrastructure for additional device specific regions

Add support for additional regions with indexes started after the
already defined fixed regions.  Device specific code can register
these regions with the new vfio_pci_register_dev_region() function.
The ops structure per region currently only includes read/write
access and a release function, allowing automatic cleanup when the
device is closed.  mmap support is only missing here because it's
not needed by the first user queued for this support.
Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent c7bb4cb4
...@@ -175,7 +175,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) ...@@ -175,7 +175,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
static void vfio_pci_disable(struct vfio_pci_device *vdev) static void vfio_pci_disable(struct vfio_pci_device *vdev)
{ {
struct pci_dev *pdev = vdev->pdev; struct pci_dev *pdev = vdev->pdev;
int bar; int i, bar;
/* Stop the device from further DMA */ /* Stop the device from further DMA */
pci_clear_master(pdev); pci_clear_master(pdev);
...@@ -186,6 +186,13 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) ...@@ -186,6 +186,13 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
vdev->virq_disabled = false; vdev->virq_disabled = false;
for (i = 0; i < vdev->num_regions; i++)
vdev->region[i].ops->release(vdev, &vdev->region[i]);
vdev->num_regions = 0;
kfree(vdev->region);
vdev->region = NULL; /* don't krealloc a freed pointer */
vfio_config_free(vdev); vfio_config_free(vdev);
for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) { for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) {
...@@ -463,6 +470,51 @@ static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev, ...@@ -463,6 +470,51 @@ static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev,
return 0; return 0;
} }
static int region_type_cap(struct vfio_pci_device *vdev,
struct vfio_info_cap *caps,
unsigned int type, unsigned int subtype)
{
struct vfio_info_cap_header *header;
struct vfio_region_info_cap_type *cap;
header = vfio_info_cap_add(caps, sizeof(*cap),
VFIO_REGION_INFO_CAP_TYPE, 1);
if (IS_ERR(header))
return PTR_ERR(header);
cap = container_of(header, struct vfio_region_info_cap_type, header);
cap->type = type;
cap->subtype = subtype;
return 0;
}
int vfio_pci_register_dev_region(struct vfio_pci_device *vdev,
unsigned int type, unsigned int subtype,
const struct vfio_pci_regops *ops,
size_t size, u32 flags, void *data)
{
struct vfio_pci_region *region;
region = krealloc(vdev->region,
(vdev->num_regions + 1) * sizeof(*region),
GFP_KERNEL);
if (!region)
return -ENOMEM;
vdev->region = region;
vdev->region[vdev->num_regions].type = type;
vdev->region[vdev->num_regions].subtype = subtype;
vdev->region[vdev->num_regions].ops = ops;
vdev->region[vdev->num_regions].size = size;
vdev->region[vdev->num_regions].flags = flags;
vdev->region[vdev->num_regions].data = data;
vdev->num_regions++;
return 0;
}
static long vfio_pci_ioctl(void *device_data, static long vfio_pci_ioctl(void *device_data,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
...@@ -485,7 +537,7 @@ static long vfio_pci_ioctl(void *device_data, ...@@ -485,7 +537,7 @@ static long vfio_pci_ioctl(void *device_data,
if (vdev->reset_works) if (vdev->reset_works)
info.flags |= VFIO_DEVICE_FLAGS_RESET; info.flags |= VFIO_DEVICE_FLAGS_RESET;
info.num_regions = VFIO_PCI_NUM_REGIONS; info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
info.num_irqs = VFIO_PCI_NUM_IRQS; info.num_irqs = VFIO_PCI_NUM_IRQS;
return copy_to_user((void __user *)arg, &info, minsz); return copy_to_user((void __user *)arg, &info, minsz);
...@@ -494,7 +546,7 @@ static long vfio_pci_ioctl(void *device_data, ...@@ -494,7 +546,7 @@ static long vfio_pci_ioctl(void *device_data,
struct pci_dev *pdev = vdev->pdev; struct pci_dev *pdev = vdev->pdev;
struct vfio_region_info info; struct vfio_region_info info;
struct vfio_info_cap caps = { .buf = NULL, .size = 0 }; struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
int ret; int i, ret;
minsz = offsetofend(struct vfio_region_info, offset); minsz = offsetofend(struct vfio_region_info, offset);
...@@ -568,7 +620,21 @@ static long vfio_pci_ioctl(void *device_data, ...@@ -568,7 +620,21 @@ static long vfio_pci_ioctl(void *device_data,
break; break;
default: default:
return -EINVAL; if (info.index >=
VFIO_PCI_NUM_REGIONS + vdev->num_regions)
return -EINVAL;
i = info.index - VFIO_PCI_NUM_REGIONS;
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = vdev->region[i].size;
info.flags = vdev->region[i].flags;
ret = region_type_cap(vdev, &caps,
vdev->region[i].type,
vdev->region[i].subtype);
if (ret)
return ret;
} }
if (caps.size) { if (caps.size) {
...@@ -866,7 +932,7 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, ...@@ -866,7 +932,7 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
struct vfio_pci_device *vdev = device_data; struct vfio_pci_device *vdev = device_data;
if (index >= VFIO_PCI_NUM_REGIONS) if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
return -EINVAL; return -EINVAL;
switch (index) { switch (index) {
...@@ -883,6 +949,10 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, ...@@ -883,6 +949,10 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
case VFIO_PCI_VGA_REGION_INDEX: case VFIO_PCI_VGA_REGION_INDEX:
return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite); return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite);
default:
index -= VFIO_PCI_NUM_REGIONS;
return vdev->region[index].ops->rw(vdev, buf,
count, ppos, iswrite);
} }
return -EINVAL; return -EINVAL;
...@@ -1065,6 +1135,7 @@ static void vfio_pci_remove(struct pci_dev *pdev) ...@@ -1065,6 +1135,7 @@ static void vfio_pci_remove(struct pci_dev *pdev)
return; return;
vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev); vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev);
kfree(vdev->region);
kfree(vdev); kfree(vdev);
if (vfio_pci_is_vga(pdev)) { if (vfio_pci_is_vga(pdev)) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/irqbypass.h> #include <linux/irqbypass.h>
#include <linux/types.h>
#ifndef VFIO_PCI_PRIVATE_H #ifndef VFIO_PCI_PRIVATE_H
#define VFIO_PCI_PRIVATE_H #define VFIO_PCI_PRIVATE_H
...@@ -33,6 +34,25 @@ struct vfio_pci_irq_ctx { ...@@ -33,6 +34,25 @@ struct vfio_pci_irq_ctx {
struct irq_bypass_producer producer; struct irq_bypass_producer producer;
}; };
struct vfio_pci_device;
struct vfio_pci_region;
struct vfio_pci_regops {
size_t (*rw)(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
void (*release)(struct vfio_pci_device *vdev,
struct vfio_pci_region *region);
};
struct vfio_pci_region {
u32 type;
u32 subtype;
const struct vfio_pci_regops *ops;
void *data;
size_t size;
u32 flags;
};
struct vfio_pci_device { struct vfio_pci_device {
struct pci_dev *pdev; struct pci_dev *pdev;
void __iomem *barmap[PCI_STD_RESOURCE_END + 1]; void __iomem *barmap[PCI_STD_RESOURCE_END + 1];
...@@ -45,6 +65,8 @@ struct vfio_pci_device { ...@@ -45,6 +65,8 @@ struct vfio_pci_device {
struct vfio_pci_irq_ctx *ctx; struct vfio_pci_irq_ctx *ctx;
int num_ctx; int num_ctx;
int irq_type; int irq_type;
int num_regions;
struct vfio_pci_region *region;
u8 msi_qmax; u8 msi_qmax;
u8 msix_bar; u8 msix_bar;
u16 msix_size; u16 msix_size;
...@@ -91,4 +113,9 @@ extern void vfio_pci_uninit_perm_bits(void); ...@@ -91,4 +113,9 @@ extern void vfio_pci_uninit_perm_bits(void);
extern int vfio_config_init(struct vfio_pci_device *vdev); extern int vfio_config_init(struct vfio_pci_device *vdev);
extern void vfio_config_free(struct vfio_pci_device *vdev); extern void vfio_config_free(struct vfio_pci_device *vdev);
extern int vfio_pci_register_dev_region(struct vfio_pci_device *vdev,
unsigned int type, unsigned int subtype,
const struct vfio_pci_regops *ops,
size_t size, u32 flags, void *data);
#endif /* VFIO_PCI_PRIVATE_H */ #endif /* VFIO_PCI_PRIVATE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment