Skip to content

Commit

Permalink
ivshmem: use kvm irqfd for msi notifications
Browse files Browse the repository at this point in the history
Use irqfd for improving context switch when notifying the guest.
If the host doesn't support kvm irqfd, regular msi notifications are
still supported.

Note: the ivshmem implementation doesn't allow switching between MSI and
IO interrupts, this patch doesn't either.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
elmarco committed Oct 26, 2015
1 parent 0f57350 commit 660c97e
Showing 1 changed file with 174 additions and 6 deletions.
180 changes: 174 additions & 6 deletions hw/misc/ivshmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "sysemu/kvm.h"
#include "migration/migration.h"
Expand Down Expand Up @@ -68,6 +69,7 @@ typedef struct Peer {

typedef struct MSIVector {
PCIDevice *pdev;
int virq;
} MSIVector;

typedef struct IVShmemState {
Expand Down Expand Up @@ -293,13 +295,73 @@ static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
msix_notify(pdev, vector);
}

static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector,
MSIMessage msg)
{
IVShmemState *s = IVSHMEM(dev);
EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
MSIVector *v = &s->msi_vectors[vector];
int ret;

IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector);

ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev);
if (ret < 0) {
return ret;
}

return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq);
}

static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector)
{
IVShmemState *s = IVSHMEM(dev);
EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
int ret;

IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector);

ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n,
s->msi_vectors[vector].virq);
if (ret != 0) {
error_report("remove_irqfd_notifier_gsi failed");
}
}

static void ivshmem_vector_poll(PCIDevice *dev,
unsigned int vector_start,
unsigned int vector_end)
{
IVShmemState *s = IVSHMEM(dev);
unsigned int vector;

IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end);

vector_end = MIN(vector_end, s->vectors);

for (vector = vector_start; vector < vector_end; vector++) {
EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector];

if (!msix_is_masked(dev, vector)) {
continue;
}

if (event_notifier_test_and_clear(notifier)) {
msix_set_pending(dev, vector);
}
}
}

static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
int vector)
{
/* create a event character device based on the passed eventfd */
IVShmemState *s = opaque;
CharDriverState * chr;
PCIDevice *pdev = PCI_DEVICE(s);
int eventfd = event_notifier_get_fd(n);
CharDriverState *chr;

s->msi_vectors[vector].pdev = pdev;

chr = qemu_chr_open_eventfd(eventfd);

Expand Down Expand Up @@ -484,6 +546,58 @@ static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size,
return true;
}

static int ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector)
{
PCIDevice *pdev = PCI_DEVICE(s);
MSIMessage msg = msix_get_message(pdev, vector);
int ret;

IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);

if (s->msi_vectors[vector].pdev != NULL) {
return 0;
}

ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev);
if (ret < 0) {
error_report("ivshmem: kvm_irqchip_add_msi_route failed");
return -1;
}

s->msi_vectors[vector].virq = ret;
s->msi_vectors[vector].pdev = pdev;

return 0;
}

static void setup_interrupt(IVShmemState *s, int vector)
{
EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
bool with_irqfd = kvm_msi_via_irqfd_enabled() &&
ivshmem_has_feature(s, IVSHMEM_MSI);
PCIDevice *pdev = PCI_DEVICE(s);

IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector);

if (!with_irqfd) {
IVSHMEM_DPRINTF("with eventfd");
s->eventfd_chr[vector] = create_eventfd_chr_device(s, n, vector);
} else if (msix_enabled(pdev)) {
IVSHMEM_DPRINTF("with irqfd");
if (ivshmem_add_kvm_msi_virq(s, vector) < 0) {
return;
}

if (!msix_is_masked(pdev, vector)) {
kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL,
s->msi_vectors[vector].virq);
}
} else {
/* it will be delayed until msix is enabled, in write_config */
IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled");
}
}

static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
{
IVShmemState *s = opaque;
Expand Down Expand Up @@ -586,11 +700,10 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
new_eventfd, incoming_fd);
event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd);
fcntl_setfl(incoming_fd, O_NONBLOCK); /* msix/irqfd poll non block */

if (incoming_posn == s->vm_id) {
s->eventfd_chr[new_eventfd] = create_eventfd_chr_device(s,
&s->peers[s->vm_id].eventfds[new_eventfd],
new_eventfd);
setup_interrupt(s, new_eventfd);
}

if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
Expand Down Expand Up @@ -665,10 +778,65 @@ static int ivshmem_setup_msi(IVShmemState * s)
return 0;
}

static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
static void ivshmem_enable_irqfd(IVShmemState *s)
{
PCIDevice *pdev = PCI_DEVICE(s);
int i;

for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
ivshmem_add_kvm_msi_virq(s, i);
}

if (msix_set_vector_notifiers(pdev,
ivshmem_vector_unmask,
ivshmem_vector_mask,
ivshmem_vector_poll)) {
error_report("ivshmem: msix_set_vector_notifiers failed");
}
}

static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector)
{
IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector);

if (s->msi_vectors[vector].pdev == NULL) {
return;
}

/* it was cleaned when masked in the frontend. */
kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq);

s->msi_vectors[vector].pdev = NULL;
}

static void ivshmem_disable_irqfd(IVShmemState *s)
{
PCIDevice *pdev = PCI_DEVICE(s);
int i;

for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
ivshmem_remove_kvm_msi_virq(s, i);
}

msix_unset_vector_notifiers(pdev);
}

static void ivshmem_write_config(PCIDevice *pdev, uint32_t address,
uint32_t val, int len)
{
pci_default_write_config(pci_dev, address, val, len);
IVShmemState *s = IVSHMEM(pdev);
int is_enabled, was_enabled = msix_enabled(pdev);

pci_default_write_config(pdev, address, val, len);
is_enabled = msix_enabled(pdev);

if (kvm_msi_via_irqfd_enabled() && s->vm_id != -1) {
if (!was_enabled && is_enabled) {
ivshmem_enable_irqfd(s);
} else if (was_enabled && !is_enabled) {
ivshmem_disable_irqfd(s);
}
}
}

static void pci_ivshmem_realize(PCIDevice *dev, Error **errp)
Expand Down

0 comments on commit 660c97e

Please sign in to comment.