vlib: add PCI MSI-X interrupt support (vfio only)

Change-Id: Iae2ddf93d1705354175e3dcae26b66e6f98a5c32
Signed-off-by: Damjan Marion <damarion@cisco.com>
This commit is contained in:
Damjan Marion
2018-03-04 19:35:23 +01:00
parent 2060db832a
commit 599a16bf8a
2 changed files with 197 additions and 6 deletions

View File

@ -66,6 +66,15 @@ typedef struct
size_t size;
} linux_pci_region_t;
typedef struct
{
int fd;
u32 clib_file_index;
union
{
pci_msix_handler_function_t *msix_handler;
};
} linux_pci_irq_t;
typedef enum
{
@ -98,6 +107,7 @@ typedef struct
/* Interrupt handler */
void (*interrupt_handler) (vlib_pci_dev_handle_t h);
linux_pci_irq_t *msix_irqs;
/* private data */
uword private_data;
@ -131,7 +141,7 @@ typedef struct
extern linux_pci_main_t linux_pci_main;
linux_pci_device_t *
static linux_pci_device_t *
linux_pci_get_device (vlib_pci_dev_handle_t h)
{
linux_pci_main_t *lpm = &linux_pci_main;
@ -155,8 +165,8 @@ vlib_pci_set_private_data (vlib_pci_dev_handle_t h, uword private_data)
vlib_pci_addr_t *
vlib_pci_get_addr (vlib_pci_dev_handle_t h)
{
linux_pci_device_t *lpm = linux_pci_get_device (h);
return &lpm->addr;
linux_pci_device_t *d = linux_pci_get_device (h);
return &d->addr;
}
/* Call to allocate/initialize the pci subsystem.
@ -530,6 +540,54 @@ scan_uio_dir (void *arg, u8 * path_name, u8 * file_name)
return 0;
}
static clib_error_t *
vfio_set_irqs (linux_pci_device_t * p, u32 index, u32 start, u32 count,
u32 flags, int *efds)
{
int data_len = efds ? count * sizeof (int) : 0;
u8 buf[sizeof (struct vfio_irq_set) + data_len];
struct vfio_irq_info irq_info = { 0 };
struct vfio_irq_set *irq_set = (struct vfio_irq_set *) buf;
irq_info.argsz = sizeof (struct vfio_irq_info);
irq_info.index = index;
if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0)
return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) "
"'%U'", format_vlib_pci_addr, &p->addr);
if (irq_info.count < start + count)
return clib_error_return_unix (0, "vfio_set_irq: unexistng interrupt on "
"'%U'", format_vlib_pci_addr, &p->addr);
if (efds)
{
flags |= VFIO_IRQ_SET_DATA_EVENTFD;
clib_memcpy (&irq_set->data, efds, data_len);
}
else
flags |= VFIO_IRQ_SET_DATA_NONE;
ASSERT ((flags & (VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_DATA_EVENTFD)) !=
(VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_DATA_EVENTFD));
irq_set->argsz = sizeof (struct vfio_irq_set) + data_len;
irq_set->index = index;
irq_set->start = start;
irq_set->count = count;
irq_set->flags = flags;
if (ioctl (p->fd, VFIO_DEVICE_SET_IRQS, irq_set) < 0)
return clib_error_return_unix (0, "%U:ioctl(VFIO_DEVICE_SET_IRQS) "
"[index = %u, start = %u, count = %u, "
"flags = 0x%x]",
format_vlib_pci_addr, &p->addr,
index, start, count, flags);
return 0;
}
static clib_error_t *
linux_pci_uio_read_ready (clib_file_t * uf)
{
@ -568,6 +626,14 @@ linux_pci_vfio_unmask_intx (linux_pci_device_t * d)
return err;
}
clib_error_t *
vlib_pci_vfio_unmask_msix (vlib_pci_dev_handle_t h, u32 start, u32 count)
{
linux_pci_device_t *p = linux_pci_get_device (h);
return vfio_set_irqs (p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
VFIO_IRQ_SET_ACTION_UNMASK, 0);
}
static clib_error_t *
linux_pci_uio_error_ready (clib_file_t * uf)
{
@ -577,7 +643,25 @@ linux_pci_uio_error_ready (clib_file_t * uf)
}
static clib_error_t *
linux_pci_vfio_read_ready (clib_file_t * uf)
linux_pci_vfio_msix_read_ready (clib_file_t * uf)
{
int __attribute__ ((unused)) rv;
vlib_pci_dev_handle_t h = uf->private_data >> 16;
u16 line = uf->private_data & 0xffff;
linux_pci_device_t *p = linux_pci_get_device (h);
linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, line);
u64 icount;
rv = read (uf->file_descriptor, &icount, sizeof (icount));
if (irq->msix_handler)
irq->msix_handler (h, line);
return /* no error */ 0;
}
static clib_error_t *
linux_pci_vfio_intx_read_ready (clib_file_t * uf)
{
int __attribute__ ((unused)) rv;
vlib_pci_dev_handle_t h = uf->private_data;
@ -662,6 +746,100 @@ error:
return err;
}
clib_error_t *
vlib_pci_register_msix_handler (vlib_pci_dev_handle_t h, u32 start, u32 count,
pci_msix_handler_function_t * msix_handler)
{
clib_error_t *err = 0;
linux_pci_device_t *p = linux_pci_get_device (h);
u32 i;
if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
"support");
/* *INDENT-OFF* */
vec_validate_init_empty (p->msix_irqs, start + count - 1, (linux_pci_irq_t)
{ .fd = -1});
/* *INDENT-ON* */
for (i = start; i < start + count; i++)
{
clib_file_t t = { 0 };
linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
ASSERT (irq->fd == -1);
irq->fd = eventfd (0, EFD_NONBLOCK);
if (irq->fd == -1)
{
err = clib_error_return_unix (0, "eventfd");
goto error;
}
t.read_function = linux_pci_vfio_msix_read_ready;
t.file_descriptor = irq->fd;
t.error_function = linux_pci_vfio_error_ready;
t.private_data = p->handle << 16 | i;
t.description = format (0, "PCI %U MSI-X #%u", format_vlib_pci_addr,
&p->addr, i);
irq->clib_file_index = clib_file_add (&file_main, &t);
irq->msix_handler = msix_handler;
}
return 0;
error:
while (i-- > start)
{
linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
if (irq->fd != -1)
{
clib_file_del_by_index (&file_main, irq->clib_file_index);
close (irq->fd);
irq->fd = -1;
}
}
return err;
}
clib_error_t *
vlib_pci_enable_msix_irq (vlib_pci_dev_handle_t h, u16 start, u16 count)
{
linux_pci_device_t *p = linux_pci_get_device (h);
int fds[count];
int i;
if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
"support");
for (i = start; i < start + count; i++)
{
linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
fds[i] = irq->fd;
}
return vfio_set_irqs (p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
VFIO_IRQ_SET_ACTION_TRIGGER, fds);
}
clib_error_t *
vlib_pci_disable_msix_irq (vlib_pci_dev_handle_t h, u16 start, u16 count)
{
linux_pci_device_t *p = linux_pci_get_device (h);
int i, fds[count];
if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
"support");
for (i = start; i < start + count; i++)
fds[i] = -1;
return vfio_set_irqs (p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
VFIO_IRQ_SET_ACTION_TRIGGER, fds);
}
static linux_pci_vfio_iommu_group_t *
get_vfio_iommu_group (int group)
{
@ -822,7 +1000,7 @@ add_device_vfio (vlib_main_t * vm, linux_pci_device_t * p,
goto error;
}
t.read_function = linux_pci_vfio_read_ready;
t.read_function = linux_pci_vfio_intx_read_ready;
t.file_descriptor = efd;
t.error_function = linux_pci_vfio_error_ready;
t.private_data = p->handle;

View File

@ -117,6 +117,10 @@ typedef struct
u16 vendor_id, device_id;
} pci_device_id_t;
typedef void (pci_intx_handler_function_t) (vlib_pci_dev_handle_t handle);
typedef void (pci_msix_handler_function_t) (vlib_pci_dev_handle_t handle,
u16 line);
typedef struct _pci_device_registration
{
/* Driver init function. */
@ -124,7 +128,7 @@ typedef struct _pci_device_registration
vlib_pci_dev_handle_t handle);
/* Interrupt handler */
void (*interrupt_handler) (vlib_pci_dev_handle_t handle);
pci_intx_handler_function_t *interrupt_handler;
/* List of registrations */
struct _pci_device_registration *next_registration;
@ -252,6 +256,15 @@ clib_error_t *vlib_pci_map_region_fixed (vlib_pci_dev_handle_t h,
u32 resource, u8 * addr,
void **result);
clib_error_t *vlib_pci_register_msix_handler (vlib_pci_dev_handle_t h,
u32 start, u32 count,
pci_msix_handler_function_t *
msix_handler);
clib_error_t *vlib_pci_enable_msix_irq (vlib_pci_dev_handle_t h, u16 start,
u16 count);
clib_error_t *vlib_pci_disable_msix_irq (vlib_pci_dev_handle_t h, u16 start,
u16 count);
unformat_function_t unformat_vlib_pci_addr;
format_function_t format_vlib_pci_addr;
format_function_t format_vlib_pci_link_speed;