bd36adb8df
IOHUB object is added to manage PCI IRQs. It uses KVM_IRQFD ioctl to create irqfd to injecting PCI interrupts to the guest. IOHUB object forwards the irqfd to the remote process. Remote process uses this fd to directly send interrupts to the guest, bypassing QEMU. Signed-off-by: John G Johnson <john.g.johnson@oracle.com> Signed-off-by: Jagannathan Raman <jag.raman@oracle.com> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Message-id: 51d5c3d54e28a68b002e3875c59599c9f5a424a1.1611938319.git.jag.raman@oracle.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
120 lines
3.2 KiB
C
120 lines
3.2 KiB
C
/*
|
|
* Remote IO Hub
|
|
*
|
|
* Copyright © 2018, 2021 Oracle and/or its affiliates.
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu-common.h"
|
|
|
|
#include "hw/pci/pci.h"
|
|
#include "hw/pci/pci_ids.h"
|
|
#include "hw/pci/pci_bus.h"
|
|
#include "qemu/thread.h"
|
|
#include "hw/boards.h"
|
|
#include "hw/remote/machine.h"
|
|
#include "hw/remote/iohub.h"
|
|
#include "qemu/main-loop.h"
|
|
|
|
void remote_iohub_init(RemoteIOHubState *iohub)
|
|
{
|
|
int pirq;
|
|
|
|
memset(&iohub->irqfds, 0, sizeof(iohub->irqfds));
|
|
memset(&iohub->resamplefds, 0, sizeof(iohub->resamplefds));
|
|
|
|
for (pirq = 0; pirq < REMOTE_IOHUB_NB_PIRQS; pirq++) {
|
|
qemu_mutex_init(&iohub->irq_level_lock[pirq]);
|
|
iohub->irq_level[pirq] = 0;
|
|
event_notifier_init_fd(&iohub->irqfds[pirq], -1);
|
|
event_notifier_init_fd(&iohub->resamplefds[pirq], -1);
|
|
}
|
|
}
|
|
|
|
void remote_iohub_finalize(RemoteIOHubState *iohub)
|
|
{
|
|
int pirq;
|
|
|
|
for (pirq = 0; pirq < REMOTE_IOHUB_NB_PIRQS; pirq++) {
|
|
qemu_set_fd_handler(event_notifier_get_fd(&iohub->resamplefds[pirq]),
|
|
NULL, NULL, NULL);
|
|
event_notifier_cleanup(&iohub->irqfds[pirq]);
|
|
event_notifier_cleanup(&iohub->resamplefds[pirq]);
|
|
qemu_mutex_destroy(&iohub->irq_level_lock[pirq]);
|
|
}
|
|
}
|
|
|
|
int remote_iohub_map_irq(PCIDevice *pci_dev, int intx)
|
|
{
|
|
return pci_dev->devfn;
|
|
}
|
|
|
|
void remote_iohub_set_irq(void *opaque, int pirq, int level)
|
|
{
|
|
RemoteIOHubState *iohub = opaque;
|
|
|
|
assert(pirq >= 0);
|
|
assert(pirq < PCI_DEVFN_MAX);
|
|
|
|
QEMU_LOCK_GUARD(&iohub->irq_level_lock[pirq]);
|
|
|
|
if (level) {
|
|
if (++iohub->irq_level[pirq] == 1) {
|
|
event_notifier_set(&iohub->irqfds[pirq]);
|
|
}
|
|
} else if (iohub->irq_level[pirq] > 0) {
|
|
iohub->irq_level[pirq]--;
|
|
}
|
|
}
|
|
|
|
static void intr_resample_handler(void *opaque)
|
|
{
|
|
ResampleToken *token = opaque;
|
|
RemoteIOHubState *iohub = token->iohub;
|
|
int pirq, s;
|
|
|
|
pirq = token->pirq;
|
|
|
|
s = event_notifier_test_and_clear(&iohub->resamplefds[pirq]);
|
|
|
|
assert(s >= 0);
|
|
|
|
QEMU_LOCK_GUARD(&iohub->irq_level_lock[pirq]);
|
|
|
|
if (iohub->irq_level[pirq]) {
|
|
event_notifier_set(&iohub->irqfds[pirq]);
|
|
}
|
|
}
|
|
|
|
void process_set_irqfd_msg(PCIDevice *pci_dev, MPQemuMsg *msg)
|
|
{
|
|
RemoteMachineState *machine = REMOTE_MACHINE(current_machine);
|
|
RemoteIOHubState *iohub = &machine->iohub;
|
|
int pirq, intx;
|
|
|
|
intx = pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1;
|
|
|
|
pirq = remote_iohub_map_irq(pci_dev, intx);
|
|
|
|
if (event_notifier_get_fd(&iohub->irqfds[pirq]) != -1) {
|
|
qemu_set_fd_handler(event_notifier_get_fd(&iohub->resamplefds[pirq]),
|
|
NULL, NULL, NULL);
|
|
event_notifier_cleanup(&iohub->irqfds[pirq]);
|
|
event_notifier_cleanup(&iohub->resamplefds[pirq]);
|
|
memset(&iohub->token[pirq], 0, sizeof(ResampleToken));
|
|
}
|
|
|
|
event_notifier_init_fd(&iohub->irqfds[pirq], msg->fds[0]);
|
|
event_notifier_init_fd(&iohub->resamplefds[pirq], msg->fds[1]);
|
|
|
|
iohub->token[pirq].iohub = iohub;
|
|
iohub->token[pirq].pirq = pirq;
|
|
|
|
qemu_set_fd_handler(msg->fds[1], intr_resample_handler, NULL,
|
|
&iohub->token[pirq]);
|
|
}
|