From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1OA5ME-0006J8-FU for qemu-devel@nongnu.org; Thu, 06 May 2010 13:59:34 -0400 Received: from [140.186.70.92] (port=38584 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OA5M8-0006Gz-3y for qemu-devel@nongnu.org; Thu, 06 May 2010 13:59:34 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OA5Lx-0001Wo-TP for qemu-devel@nongnu.org; Thu, 06 May 2010 13:59:27 -0400 Received: from mail-vw0-f45.google.com ([209.85.212.45]:51754) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OA5Lx-0001WL-FB for qemu-devel@nongnu.org; Thu, 06 May 2010 13:59:17 -0400 Received: by vws17 with SMTP id 17so220908vws.4 for ; Thu, 06 May 2010 10:59:15 -0700 (PDT) MIME-Version: 1.0 Sender: camm@ualberta.ca In-Reply-To: <4BE2FD45.8000601@codemonkey.ws> References: <1271872408-22842-1-git-send-email-cam@cs.ualberta.ca> <1271872408-22842-2-git-send-email-cam@cs.ualberta.ca> <1271872408-22842-3-git-send-email-cam@cs.ualberta.ca> <1271872408-22842-4-git-send-email-cam@cs.ualberta.ca> <1271872408-22842-5-git-send-email-cam@cs.ualberta.ca> <4BE2FD45.8000601@codemonkey.ws> Date: Thu, 6 May 2010 11:59:15 -0600 Message-ID: From: Cam Macdonell Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] Re: [PATCH v5 4/5] Inter-VM shared memory PCI device List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Anthony Liguori Cc: qemu-devel@nongnu.org, kvm@vger.kernel.org On Thu, May 6, 2010 at 11:32 AM, Anthony Liguori wr= ote: > On 04/21/2010 12:53 PM, Cam Macdonell wrote: >> >> Support an inter-vm shared memory device that maps a shared-memory objec= t >> as a >> PCI device in the guest. =A0This patch also supports interrupts between >> guest by >> communicating over a unix domain socket. =A0This patch applies to the >> qemu-kvm >> repository. >> >> =A0 =A0 -device ivshmem,size=3D[,shm=3D] >> >> Interrupts are supported between multiple VMs by using a shared memory >> server >> by using a chardev socket. >> >> =A0 =A0 -device ivshmem,size=3D[,shm=3D] >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 [,chardev=3D][,msi=3Don][,ir= qfd=3Don][,vectors=3Dn] >> =A0 =A0 -chardev socket,path=3D,id=3D >> >> (shared memory server is qemu.git/contrib/ivshmem-server) >> >> Sample programs and init scripts are in a git repo here: >> >> =A0 =A0 www.gitorious.org/nahanni >> --- >> =A0Makefile.target | =A0 =A03 + >> =A0hw/ivshmem.c =A0 =A0| =A0727 >> +++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> =A0qemu-char.c =A0 =A0 | =A0 =A06 + >> =A0qemu-char.h =A0 =A0 | =A0 =A03 + >> =A0qemu-doc.texi =A0 | =A0 25 ++ >> =A05 files changed, 764 insertions(+), 0 deletions(-) >> =A0create mode 100644 hw/ivshmem.c >> >> diff --git a/Makefile.target b/Makefile.target >> index 1ffd802..bc9a681 100644 >> --- a/Makefile.target >> +++ b/Makefile.target >> @@ -199,6 +199,9 @@ obj-$(CONFIG_USB_OHCI) +=3D usb-ohci.o >> =A0obj-y +=3D rtl8139.o >> =A0obj-y +=3D e1000.o >> >> +# Inter-VM PCI shared memory >> +obj-y +=3D ivshmem.o >> + >> =A0# Hardware support >> =A0obj-i386-y =3D pckbd.o dma.o >> =A0obj-i386-y +=3D vga.o >> diff --git a/hw/ivshmem.c b/hw/ivshmem.c >> new file mode 100644 >> index 0000000..f8d8fdb >> --- /dev/null >> +++ b/hw/ivshmem.c >> @@ -0,0 +1,727 @@ >> +/* >> + * Inter-VM Shared Memory PCI device. >> + * >> + * Author: >> + * =A0 =A0 =A0Cam Macdonell >> + * >> + * Based On: cirrus_vga.c and rtl8139.c >> + * >> + * This code is licensed under the GNU GPL v2. >> + */ >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> > > This will break the Windows along with any non-Linux unix or any Linux ol= d > enough to not have eventfd support. I'll wrap it with #ifdef CONFIG_EVENTFD > If it's based on cirrus_vga.c and rtl8139.c, then it ought to carry the > respective copyrights, no? Sure, I can add those Cam > > Regards, > > Anthony Liguori > >> +#include "hw.h" >> +#include "console.h" >> +#include "pc.h" >> +#include "pci.h" >> +#include "sysemu.h" >> + >> +#include "msix.h" >> +#include "qemu-kvm.h" >> +#include "libkvm.h" >> + >> +#include >> +#include >> +#include >> +#include >> + >> +#define IVSHMEM_IRQFD =A0 0 >> +#define IVSHMEM_MSI =A0 =A0 1 >> + >> +#define DEBUG_IVSHMEM >> +#ifdef DEBUG_IVSHMEM >> +#define IVSHMEM_DPRINTF(fmt, args...) =A0 =A0 =A0 =A0\ >> + =A0 =A0do {printf("IVSHMEM: " fmt, ##args); } while (0) >> +#else >> +#define IVSHMEM_DPRINTF(fmt, args...) >> +#endif >> + >> +typedef struct EventfdEntry { >> + =A0 =A0PCIDevice *pdev; >> + =A0 =A0int vector; >> +} EventfdEntry; >> + >> +typedef struct IVShmemState { >> + =A0 =A0PCIDevice dev; >> + =A0 =A0uint32_t intrmask; >> + =A0 =A0uint32_t intrstatus; >> + =A0 =A0uint32_t doorbell; >> + >> + =A0 =A0CharDriverState * chr; >> + =A0 =A0CharDriverState ** eventfd_chr; >> + =A0 =A0int ivshmem_mmio_io_addr; >> + >> + =A0 =A0pcibus_t mmio_addr; >> + =A0 =A0unsigned long ivshmem_offset; >> + =A0 =A0uint64_t ivshmem_size; /* size of shared memory region */ >> + =A0 =A0int shm_fd; /* shared memory file descriptor */ >> + >> + =A0 =A0int nr_allocated_vms; >> + =A0 =A0/* array of eventfds for each guest */ >> + =A0 =A0int ** eventfds; >> + =A0 =A0/* keep track of # of eventfds for each guest*/ >> + =A0 =A0int * eventfds_posn_count; >> + >> + =A0 =A0int nr_alloc_guests; >> + =A0 =A0int vm_id; >> + =A0 =A0int num_eventfds; >> + =A0 =A0uint32_t vectors; >> + =A0 =A0uint32_t features; >> + =A0 =A0EventfdEntry *eventfd_table; >> + >> + =A0 =A0char * shmobj; >> + =A0 =A0char * sizearg; >> +} IVShmemState; >> + >> +/* registers for the Inter-VM shared memory device */ >> +enum ivshmem_registers { >> + =A0 =A0IntrMask =3D 0, >> + =A0 =A0IntrStatus =3D 4, >> + =A0 =A0IVPosition =3D 8, >> + =A0 =A0Doorbell =3D 12, >> +}; >> + >> +static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, int >> feature) { >> + =A0 =A0return (ivs->features& =A0(1<< =A0feature)); >> +} >> + >> +static inline int is_power_of_two(int x) { >> + =A0 =A0return (x& =A0(x-1)) =3D=3D 0; >> +} >> + >> +static void ivshmem_map(PCIDevice *pci_dev, int region_num, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pcibus_t addr, pcibus_t size, i= nt type) >> +{ >> + =A0 =A0IVShmemState *s =3D DO_UPCAST(IVShmemState, dev, pci_dev); >> + >> + =A0 =A0IVSHMEM_DPRINTF("addr =3D %u size =3D %u\n", (uint32_t)addr, >> (uint32_t)size); >> + =A0 =A0cpu_register_physical_memory(addr, s->ivshmem_size, >> s->ivshmem_offset); >> + >> +} >> + >> +/* accessing registers - based on rtl8139 */ >> +static void ivshmem_update_irq(IVShmemState *s, int val) >> +{ >> + =A0 =A0int isr; >> + =A0 =A0isr =3D (s->intrstatus& =A0s->intrmask)& =A00xffffffff; >> + >> + =A0 =A0/* don't print ISR resets */ >> + =A0 =A0if (isr) { >> + =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", >> + =A0 =A0 =A0 =A0 =A0 isr ? 1 : 0, s->intrstatus, s->intrmask); >> + =A0 =A0} >> + >> + =A0 =A0qemu_set_irq(s->dev.irq[0], (isr !=3D 0)); >> +} >> + >> +static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) >> +{ >> + =A0 =A0IVSHMEM_DPRINTF("IntrMask write(w) val =3D 0x%04x\n", val); >> + >> + =A0 =A0s->intrmask =3D val; >> + >> + =A0 =A0ivshmem_update_irq(s, val); >> +} >> + >> +static uint32_t ivshmem_IntrMask_read(IVShmemState *s) >> +{ >> + =A0 =A0uint32_t ret =3D s->intrmask; >> + >> + =A0 =A0IVSHMEM_DPRINTF("intrmask read(w) val =3D 0x%04x\n", ret); >> + >> + =A0 =A0return ret; >> +} >> + >> +static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) >> +{ >> + =A0 =A0IVSHMEM_DPRINTF("IntrStatus write(w) val =3D 0x%04x\n", val); >> + >> + =A0 =A0s->intrstatus =3D val; >> + >> + =A0 =A0ivshmem_update_irq(s, val); >> + =A0 =A0return; >> +} >> + >> +static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) >> +{ >> + =A0 =A0uint32_t ret =3D s->intrstatus; >> + >> + =A0 =A0/* reading ISR clears all interrupts */ >> + =A0 =A0s->intrstatus =3D 0; >> + >> + =A0 =A0ivshmem_update_irq(s, 0); >> + >> + =A0 =A0return ret; >> +} >> + >> +static void ivshmem_io_writew(void *opaque, uint8_t addr, uint32_t val) >> +{ >> + >> + =A0 =A0IVSHMEM_DPRINTF("We shouldn't be writing words\n"); >> +} >> + >> +static void ivshmem_io_writel(void *opaque, uint8_t addr, uint32_t val) >> +{ >> + =A0 =A0IVShmemState *s =3D opaque; >> + >> + =A0 =A0u_int64_t write_one =3D 1; >> + =A0 =A0u_int16_t dest =3D val>> =A016; >> + =A0 =A0u_int16_t vector =3D val& =A00xff; >> + >> + =A0 =A0addr&=3D 0xfe; >> + >> + =A0 =A0switch (addr) >> + =A0 =A0{ >> + =A0 =A0 =A0 =A0case IntrMask: >> + =A0 =A0 =A0 =A0 =A0 =A0ivshmem_IntrMask_write(s, val); >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + >> + =A0 =A0 =A0 =A0case IntrStatus: >> + =A0 =A0 =A0 =A0 =A0 =A0ivshmem_IntrStatus_write(s, val); >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + >> + =A0 =A0 =A0 =A0case Doorbell: >> + =A0 =A0 =A0 =A0 =A0 =A0/* check doorbell range */ >> + =A0 =A0 =A0 =A0 =A0 =A0if ((vector>=3D 0)&& =A0(vector< =A0s->eventfds= _posn_count[dest])) >> { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("Writing %ld to VM %d o= n vector %d\n", >> write_one, dest, vector); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (write(s->eventfds[dest][vector],&(w= rite_one), 8) !=3D >> 8) { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("error writing = to eventfd\n"); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + =A0 =A0 =A0 =A0default: >> + =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", = dest); >> + =A0 =A0} >> +} >> + >> +static void ivshmem_io_writeb(void *opaque, uint8_t addr, uint32_t val) >> +{ >> + =A0 =A0IVSHMEM_DPRINTF("We shouldn't be writing bytes\n"); >> +} >> + >> +static uint32_t ivshmem_io_readw(void *opaque, uint8_t addr) >> +{ >> + >> + =A0 =A0IVSHMEM_DPRINTF("We shouldn't be reading words\n"); >> + =A0 =A0return 0; >> +} >> + >> +static uint32_t ivshmem_io_readl(void *opaque, uint8_t addr) >> +{ >> + >> + =A0 =A0IVShmemState *s =3D opaque; >> + =A0 =A0uint32_t ret; >> + >> + =A0 =A0switch (addr) >> + =A0 =A0{ >> + =A0 =A0 =A0 =A0case IntrMask: >> + =A0 =A0 =A0 =A0 =A0 =A0ret =3D ivshmem_IntrMask_read(s); >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + >> + =A0 =A0 =A0 =A0case IntrStatus: >> + =A0 =A0 =A0 =A0 =A0 =A0ret =3D ivshmem_IntrStatus_read(s); >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + >> + =A0 =A0 =A0 =A0case IVPosition: >> + =A0 =A0 =A0 =A0 =A0 =A0/* return my id in the ivshmem list */ >> + =A0 =A0 =A0 =A0 =A0 =A0ret =3D s->vm_id; >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + >> + =A0 =A0 =A0 =A0default: >> + =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("why are we reading 0x%x\n", ad= dr); >> + =A0 =A0 =A0 =A0 =A0 =A0ret =3D 0; >> + =A0 =A0} >> + >> + =A0 =A0return ret; >> + >> +} >> + >> +static uint32_t ivshmem_io_readb(void *opaque, uint8_t addr) >> +{ >> + =A0 =A0IVSHMEM_DPRINTF("We shouldn't be reading bytes\n"); >> + >> + =A0 =A0return 0; >> +} >> + >> +static void ivshmem_mmio_writeb(void *opaque, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0target_= phys_addr_t addr, uint32_t val) >> +{ >> + =A0 =A0ivshmem_io_writeb(opaque, addr& =A00xFF, val); >> +} >> + >> +static void ivshmem_mmio_writew(void *opaque, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0target_= phys_addr_t addr, uint32_t val) >> +{ >> + =A0 =A0ivshmem_io_writew(opaque, addr& =A00xFF, val); >> +} >> + >> +static void ivshmem_mmio_writel(void *opaque, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0target_= phys_addr_t addr, uint32_t val) >> +{ >> + =A0 =A0ivshmem_io_writel(opaque, addr& =A00xFF, val); >> +} >> + >> +static uint32_t ivshmem_mmio_readb(void *opaque, target_phys_addr_t add= r) >> +{ >> + =A0 =A0return ivshmem_io_readb(opaque, addr& =A00xFF); >> +} >> + >> +static uint32_t ivshmem_mmio_readw(void *opaque, target_phys_addr_t add= r) >> +{ >> + =A0 =A0uint32_t val =3D ivshmem_io_readw(opaque, addr& =A00xFF); >> + =A0 =A0return val; >> +} >> + >> +static uint32_t ivshmem_mmio_readl(void *opaque, target_phys_addr_t add= r) >> +{ >> + =A0 =A0uint32_t val =3D ivshmem_io_readl(opaque, addr& =A00xFF); >> + =A0 =A0return val; >> +} >> + >> +static CPUReadMemoryFunc *ivshmem_mmio_read[3] =3D { >> + =A0 =A0ivshmem_mmio_readb, >> + =A0 =A0ivshmem_mmio_readw, >> + =A0 =A0ivshmem_mmio_readl, >> +}; >> + >> +static CPUWriteMemoryFunc *ivshmem_mmio_write[3] =3D { >> + =A0 =A0ivshmem_mmio_writeb, >> + =A0 =A0ivshmem_mmio_writew, >> + =A0 =A0ivshmem_mmio_writel, >> +}; >> + >> +static void ivshmem_receive(void *opaque, const uint8_t *buf, int size) >> +{ >> + =A0 =A0IVShmemState *s =3D opaque; >> + >> + =A0 =A0ivshmem_IntrStatus_write(s, *buf); >> + >> + =A0 =A0IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf); >> +} >> + >> +static int ivshmem_can_receive(void * opaque) >> +{ >> + =A0 =A0return 8; >> +} >> + >> +static void ivshmem_event(void *opaque, int event) >> +{ >> + =A0 =A0IVSHMEM_DPRINTF("ivshmem_event %d\n", event); >> +} >> + >> +static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { >> + >> + =A0 =A0EventfdEntry *entry =3D opaque; >> + =A0 =A0PCIDevice *pdev =3D entry->pdev; >> + >> + =A0 =A0IVSHMEM_DPRINTF("fake irqfd on vector %d\n", entry->vector); >> + =A0 =A0msix_notify(pdev, entry->vector); >> +} >> + >> +static CharDriverState* create_eventfd_chr_device(void * opaque, int >> eventfd, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int >> vector) >> +{ >> + =A0 =A0/* create a event character device based on the passed eventfd = */ >> + =A0 =A0IVShmemState *s =3D opaque; >> + =A0 =A0CharDriverState * chr; >> + >> + =A0 =A0chr =3D qemu_chr_open_eventfd(eventfd); >> + >> + =A0 =A0if (chr =3D=3D NULL) { >> + =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("creating eventfd for eventfd %d failed= \n", >> eventfd); >> + =A0 =A0 =A0 =A0exit(-1); >> + =A0 =A0} >> + >> + =A0 =A0if (ivshmem_has_feature(s, IVSHMEM_MSI)) { >> + =A0 =A0 =A0 =A0s->eventfd_table[vector].pdev =3D&s->dev; >> + =A0 =A0 =A0 =A0s->eventfd_table[vector].vector =3D vector; >> + >> + =A0 =A0 =A0 =A0qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_ir= qfd, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ivshmem_event,&s->eventfd_t= able[vector]); >> + =A0 =A0} else { >> + =A0 =A0 =A0 =A0qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem= _receive, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ivshmem_event, s); >> + =A0 =A0} >> + >> + =A0 =A0return chr; >> + >> +} >> + >> +static int check_shm_size(IVShmemState *s, int shmemfd) { >> + =A0 =A0/* check that the guest isn't going to try and map more memory = than >> the >> + =A0 =A0 * card server allocated return -1 to indicate error */ >> + >> + =A0 =A0struct stat buf; >> + >> + =A0 =A0fstat(shmemfd,&buf); >> + >> + =A0 =A0if (s->ivshmem_size> =A0buf.st_size) { >> + =A0 =A0 =A0 =A0fprintf(stderr, "IVSHMEM ERROR: Requested memory size g= reater"); >> + =A0 =A0 =A0 =A0fprintf(stderr, " than shared object size (%ld> =A0%ld)= \n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0s->ivshmem_size, buf.st_size); >> + =A0 =A0 =A0 =A0return -1; >> + =A0 =A0} else { >> + =A0 =A0 =A0 =A0return 0; >> + =A0 =A0} >> +} >> + >> +static void create_shared_memory_BAR(IVShmemState *s, int fd) { >> + >> + =A0 =A0s->shm_fd =3D fd; >> + >> + =A0 =A0s->ivshmem_offset =3D qemu_ram_mmap(s->shm_fd, s->ivshmem_size, >> + =A0 =A0 =A0 =A0 =A0 =A0 MAP_SHARED, 0); >> + >> + =A0 =A0/* region for shared memory */ >> + =A0 =A0pci_register_bar(&s->dev, 2, s->ivshmem_size, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0PCI_BASE_ADDRESS_SPACE_MEMORY, >> ivshmem_map); >> +} >> + >> +static void close_guest_eventfds(IVShmemState *s, int posn) >> +{ >> + =A0 =A0int i, guest_curr_max; >> + >> + =A0 =A0guest_curr_max =3D s->eventfds_posn_count[posn]; >> + >> + =A0 =A0for (i =3D 0; i< =A0guest_curr_max; i++) >> + =A0 =A0 =A0 =A0close(s->eventfds[posn][i]); >> + >> + =A0 =A0free(s->eventfds[posn]); >> + =A0 =A0s->eventfds_posn_count[posn] =3D 0; >> +} >> + >> +/* this function increase the dynamic storage need to store data about >> other >> + * guests */ >> +static void increase_dynamic_storage(IVShmemState *s, int new_min_size)= { >> + >> + =A0 =A0int j, old_nr_alloc; >> + >> + =A0 =A0old_nr_alloc =3D s->nr_alloc_guests; >> + >> + =A0 =A0while (s->nr_alloc_guests< =A0new_min_size) >> + =A0 =A0 =A0 =A0s->nr_alloc_guests =3D s->nr_alloc_guests * 2; >> + >> + =A0 =A0IVSHMEM_DPRINTF("bumping storage to %d guests\n", >> s->nr_alloc_guests); >> + =A0 =A0s->eventfds =3D qemu_realloc(s->eventfds, s->nr_alloc_guests * >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof(int *)); >> + =A0 =A0s->eventfds_posn_count =3D qemu_realloc(s->eventfds_posn_count, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s->nr_alloc_guests * >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof(int)); >> + =A0 =A0s->eventfd_table =3D qemu_realloc(s->eventfd_table, s->nr_alloc= _guests >> * >> + >> =A0sizeof(EventfdEntry)); >> + >> + =A0 =A0if ((s->eventfds =3D=3D NULL) || (s->eventfds_posn_count =3D=3D= NULL) || >> + =A0 =A0 =A0 =A0 =A0 =A0(s->eventfd_table =3D=3D NULL)) { >> + =A0 =A0 =A0 =A0fprintf(stderr, "Allocation error - exiting\n"); >> + =A0 =A0 =A0 =A0exit(1); >> + =A0 =A0} >> + >> + =A0 =A0if (!ivshmem_has_feature(s, IVSHMEM_IRQFD)) { >> + =A0 =A0 =A0 =A0s->eventfd_chr =3D (CharDriverState **)qemu_realloc(s->= eventfd_chr, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0s->nr_alloc_guests * sizeof(void *)); >> + =A0 =A0 =A0 =A0if (s->eventfd_chr =3D=3D NULL) { >> + =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "Allocation error - exiting\n")= ; >> + =A0 =A0 =A0 =A0 =A0 =A0exit(1); >> + =A0 =A0 =A0 =A0} >> + =A0 =A0} >> + >> + =A0 =A0/* zero out new pointers */ >> + =A0 =A0for (j =3D old_nr_alloc; j< =A0s->nr_alloc_guests; j++) { >> + =A0 =A0 =A0 =A0s->eventfds[j] =3D NULL; >> + =A0 =A0} >> +} >> + >> +static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) >> +{ >> + =A0 =A0IVShmemState *s =3D opaque; >> + =A0 =A0int incoming_fd, tmp_fd; >> + =A0 =A0int guest_curr_max; >> + =A0 =A0long incoming_posn; >> + >> + =A0 =A0memcpy(&incoming_posn, buf, sizeof(long)); >> + =A0 =A0/* pick off s->chr->msgfd and store it, posn should accompany m= sg */ >> + =A0 =A0tmp_fd =3D qemu_chr_get_msgfd(s->chr); >> + =A0 =A0IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_f= d); >> + >> + =A0 =A0/* make sure we have enough space for this guest */ >> + =A0 =A0if (incoming_posn>=3D s->nr_alloc_guests) { >> + =A0 =A0 =A0 =A0increase_dynamic_storage(s, incoming_posn); >> + =A0 =A0} >> + >> + =A0 =A0if (tmp_fd =3D=3D -1) { >> + =A0 =A0 =A0 =A0/* if posn is positive and unseen before then this is o= ur posn*/ >> + =A0 =A0 =A0 =A0if ((incoming_posn>=3D 0)&& =A0(s->eventfds[incoming_po= sn] =3D=3D NULL)) >> { >> + =A0 =A0 =A0 =A0 =A0 =A0/* receive our posn */ >> + =A0 =A0 =A0 =A0 =A0 =A0s->vm_id =3D incoming_posn; >> + =A0 =A0 =A0 =A0 =A0 =A0return; >> + =A0 =A0 =A0 =A0} else { >> + =A0 =A0 =A0 =A0 =A0 =A0/* otherwise an fd =3D=3D -1 means an existing = guest has gone >> away */ >> + =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("posn %ld has gone away\n", inc= oming_posn); >> + =A0 =A0 =A0 =A0 =A0 =A0close_guest_eventfds(s, incoming_posn); >> + =A0 =A0 =A0 =A0 =A0 =A0return; >> + =A0 =A0 =A0 =A0} >> + =A0 =A0} >> + >> + =A0 =A0/* because of the implementation of get_msgfd, we need a dup */ >> + =A0 =A0incoming_fd =3D dup(tmp_fd); >> + >> + =A0 =A0/* if the position is -1, then it's shared memory region fd */ >> + =A0 =A0if (incoming_posn =3D=3D -1) { >> + >> + =A0 =A0 =A0 =A0s->num_eventfds =3D 0; >> + >> + =A0 =A0 =A0 =A0if (check_shm_size(s, incoming_fd) =3D=3D -1) { >> + =A0 =A0 =A0 =A0 =A0 =A0exit(-1); >> + =A0 =A0 =A0 =A0} >> + >> + =A0 =A0 =A0 =A0/* creating a BAR in qemu_chr callback may be crazy */ >> + =A0 =A0 =A0 =A0create_shared_memory_BAR(s, incoming_fd); >> + >> + =A0 =A0 =A0 return; >> + =A0 =A0} >> + >> + =A0 =A0/* each guest has an array of eventfds, and we keep track of ho= w many >> + =A0 =A0 * guests for each VM */ >> + =A0 =A0guest_curr_max =3D s->eventfds_posn_count[incoming_posn]; >> + =A0 =A0if (guest_curr_max =3D=3D 0) { >> + =A0 =A0 =A0 =A0/* one eventfd per MSI vector */ >> + =A0 =A0 =A0 =A0s->eventfds[incoming_posn] =3D (int *) qemu_malloc(s->v= ectors * >> + >> =A0sizeof(int)); >> + =A0 =A0} >> + >> + =A0 =A0/* this is an eventfd for a particular guest VM */ >> + =A0 =A0IVSHMEM_DPRINTF("eventfds[%ld][%d] =3D %d\n", incoming_posn, >> guest_curr_max, >> + >> =A0incoming_fd); >> + =A0 =A0s->eventfds[incoming_posn][guest_curr_max] =3D incoming_fd; >> + >> + =A0 =A0/* increment count for particular guest */ >> + =A0 =A0s->eventfds_posn_count[incoming_posn]++; >> + >> + =A0 =A0/* ioeventfd and irqfd are enabled together, >> + =A0 =A0 * so the flag IRQFD refers to both */ >> + =A0 =A0if (ivshmem_has_feature(s, IVSHMEM_IRQFD)&& =A0guest_curr_max>= =3D 0) { >> + =A0 =A0 =A0 =A0/* allocate ioeventfd for the new fd >> + =A0 =A0 =A0 =A0 * received for guest @ incoming_posn */ >> + =A0 =A0 =A0 =A0kvm_set_ioeventfd_mmio_long(incoming_fd, s->mmio_addr += Doorbell, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(incomi= ng_posn<< =A016) | guest_curr_max, >> 1); >> + =A0 =A0} >> + >> + =A0 =A0/* keep track of the maximum VM ID */ >> + =A0 =A0if (incoming_posn> =A0s->num_eventfds) { >> + =A0 =A0 =A0 =A0s->num_eventfds =3D incoming_posn; >> + =A0 =A0} >> + >> + =A0 =A0if (incoming_posn =3D=3D s->vm_id) { >> + =A0 =A0 =A0 =A0if (ivshmem_has_feature(s, IVSHMEM_IRQFD)) { >> + =A0 =A0 =A0 =A0 =A0 =A0/* setup irqfd for this VM's eventfd */ >> + =A0 =A0 =A0 =A0 =A0 =A0int vector =3D guest_curr_max; >> + =A0 =A0 =A0 =A0 =A0 =A0kvm_set_irqfd(s->eventfds[s->vm_id][guest_curr_= max], vector, >> + >> =A0s->dev.msix_irq_entries[vector].gsi); >> + =A0 =A0 =A0 =A0} else { >> + =A0 =A0 =A0 =A0 =A0 =A0/* initialize char device for callback >> + =A0 =A0 =A0 =A0 =A0 =A0 * if this is one of my eventfd */ >> + =A0 =A0 =A0 =A0 =A0 =A0s->eventfd_chr[guest_curr_max] =3D create_event= fd_chr_device(s, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s->eventfds[s->vm_id][guest_curr_max], = guest_curr_max); >> + =A0 =A0 =A0 =A0} >> + =A0 =A0} >> + >> + =A0 =A0return; >> +} >> + >> +static void ivshmem_reset(DeviceState *d) >> +{ >> + =A0 =A0return; >> +} >> + >> +static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pcibus_t addr, pcibus_t si= ze, int type) >> +{ >> + =A0 =A0IVShmemState *s =3D DO_UPCAST(IVShmemState, dev, pci_dev); >> + >> + =A0 =A0s->mmio_addr =3D addr; >> + =A0 =A0cpu_register_physical_memory(addr + 0, 0x400, >> s->ivshmem_mmio_io_addr); >> + >> + =A0 =A0/* now that our mmio region has been allocated, we can receive >> + =A0 =A0 * the file descriptors */ >> + =A0 =A0if (s->chr !=3D NULL) { >> + =A0 =A0 =A0 =A0qemu_chr_add_handlers(s->chr, ivshmem_can_receive, ivsh= mem_read, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ivshmem_event, s); >> + =A0 =A0} >> + >> +} >> + >> +static uint64_t ivshmem_get_size(IVShmemState * s) { >> + >> + =A0 =A0uint64_t value; >> + =A0 =A0char *ptr; >> + >> + =A0 =A0value =3D strtoul(s->sizearg,&ptr, 10); >> + =A0 =A0switch (*ptr) { >> + =A0 =A0 =A0 =A0case 0: case 'M': case 'm': >> + =A0 =A0 =A0 =A0 =A0 =A0value<<=3D 20; >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + =A0 =A0 =A0 =A0case 'G': case 'g': >> + =A0 =A0 =A0 =A0 =A0 =A0value<<=3D 30; >> + =A0 =A0 =A0 =A0 =A0 =A0break; >> + =A0 =A0 =A0 =A0default: >> + =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "qemu: invalid ram size: %s\n",= s->sizearg); >> + =A0 =A0 =A0 =A0 =A0 =A0exit(1); >> + =A0 =A0} >> + >> + =A0 =A0/* BARs must be a power of 2 */ >> + =A0 =A0if (!is_power_of_two(value)) { >> + =A0 =A0 =A0 =A0fprintf(stderr, "ivshmem: size must be power of 2\n"); >> + =A0 =A0 =A0 =A0exit(1); >> + =A0 =A0} >> + >> + =A0 =A0return value; >> + >> +} >> + >> +static int pci_ivshmem_init(PCIDevice *dev) >> +{ >> + =A0 =A0IVShmemState *s =3D DO_UPCAST(IVShmemState, dev, dev); >> + =A0 =A0uint8_t *pci_conf; >> + =A0 =A0int i; >> + >> + =A0 =A0if (s->sizearg =3D=3D NULL) >> + =A0 =A0 =A0 =A0s->ivshmem_size =3D 4<< =A020; /* 4 MB default */ >> + =A0 =A0else { >> + =A0 =A0 =A0 =A0s->ivshmem_size =3D ivshmem_get_size(s); >> + =A0 =A0} >> + >> + =A0 =A0/* IRQFD requires MSI */ >> + =A0 =A0if (ivshmem_has_feature(s, IVSHMEM_IRQFD)&& >> + =A0 =A0 =A0 =A0!ivshmem_has_feature(s, IVSHMEM_MSI)) { >> + =A0 =A0 =A0 =A0fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\= n"); >> + =A0 =A0 =A0 =A0exit(1); >> + =A0 =A0} >> + >> + =A0 =A0pci_conf =3D s->dev.config; >> + =A0 =A0pci_conf[0x00] =3D 0xf4; /* Qumranet vendor ID 0x5002 */ >> + =A0 =A0pci_conf[0x01] =3D 0x1a; >> + =A0 =A0pci_conf[0x02] =3D 0x10; >> + =A0 =A0pci_conf[0x03] =3D 0x11; >> + =A0 =A0pci_conf[0x04] =3D PCI_COMMAND_IO | PCI_COMMAND_MEMORY; >> + =A0 =A0pci_conf[0x0a] =3D 0x00; /* RAM controller */ >> + =A0 =A0pci_conf[0x0b] =3D 0x05; >> + =A0 =A0pci_conf[0x0e] =3D 0x00; /* header_type */ >> + >> + =A0 =A0s->ivshmem_mmio_io_addr =3D cpu_register_io_memory(ivshmem_mmio= _read, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0ivshmem_mmio_write, s); >> + =A0 =A0/* region for registers*/ >> + =A0 =A0pci_register_bar(&s->dev, 0, 0x400, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 PCI_BASE_ADDRESS_S= PACE_MEMORY, >> ivshmem_mmio_map); >> + >> + =A0 =A0/* allocate the MSI-X vectors */ >> + =A0 =A0if (ivshmem_has_feature(s, IVSHMEM_MSI)) { >> + >> + =A0 =A0 =A0 =A0if (!msix_init(&s->dev, s->vectors, 1, 0)) { >> + =A0 =A0 =A0 =A0 =A0 =A0pci_register_bar(&s->dev, 1, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 msix_bar_size(= &s->dev), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 PCI_BASE_ADDRE= SS_SPACE_MEMORY, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 msix_mmio_map)= ; >> + =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("msix initialized (%d vectors)\= n", >> s->vectors); >> + =A0 =A0 =A0 =A0} else { >> + =A0 =A0 =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("msix initialization failed\n")= ; >> + =A0 =A0 =A0 =A0} >> + >> + =A0 =A0 =A0 =A0/* 'activate' the vectors */ >> + =A0 =A0 =A0 =A0for (i =3D 0; i< =A0s->vectors; i++) { >> + =A0 =A0 =A0 =A0 =A0 =A0msix_vector_use(&s->dev, i); >> + =A0 =A0 =A0 =A0} >> + =A0 =A0} >> + >> + =A0 =A0if ((s->chr !=3D NULL)&& =A0(strncmp(s->chr->filename, "unix:",= 5) =3D=3D 0)) >> { >> + =A0 =A0 =A0 =A0/* if we get a UNIX socket as the parameter we will tal= k >> + =A0 =A0 =A0 =A0 * to the ivshmem server later once the MMIO BAR is act= ually >> + =A0 =A0 =A0 =A0 * allocated (see ivshmem_mmio_map) */ >> + >> + =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("using shared memory server (socket =3D= %s)\n", >> + >> =A0s->chr->filename); >> + >> + =A0 =A0 =A0 =A0/* we allocate enough space for 16 guests and grow as n= eeded */ >> + =A0 =A0 =A0 =A0s->nr_alloc_guests =3D 16; >> + =A0 =A0 =A0 =A0s->vm_id =3D -1; >> + >> + =A0 =A0 =A0 =A0/* allocate/initialize space for interrupt handling */ >> + =A0 =A0 =A0 =A0s->eventfds =3D qemu_mallocz(s->nr_alloc_guests * sizeo= f(int *)); >> + =A0 =A0 =A0 =A0s->eventfd_table =3D qemu_mallocz(s->vectors * >> sizeof(EventfdEntry)); >> + =A0 =A0 =A0 =A0s->eventfds_posn_count =3D qemu_mallocz(s->nr_alloc_gue= sts * >> sizeof(int)); >> + >> + =A0 =A0 =A0 =A0pci_conf[PCI_INTERRUPT_PIN] =3D 1; /* we are going to s= upport >> interrupts */ >> + >> + =A0 =A0 =A0 =A0if (!ivshmem_has_feature(s, IVSHMEM_IRQFD)) { >> + =A0 =A0 =A0 =A0 =A0 =A0s->eventfd_chr =3D (CharDriverState >> **)qemu_malloc(s->nr_alloc_guests * >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof(void >> *)); >> + =A0 =A0 =A0 =A0} >> + >> + =A0 =A0} else { >> + =A0 =A0 =A0 =A0/* just map the file immediately, we're not using a ser= ver */ >> + =A0 =A0 =A0 =A0int fd; >> + >> + =A0 =A0 =A0 =A0if (s->shmobj =3D=3D NULL) { >> + =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "Must specify 'chardev' or 'shm= ' to >> ivshmem\n"); >> + =A0 =A0 =A0 =A0} >> + >> + =A0 =A0 =A0 =A0IVSHMEM_DPRINTF("using shm_open (shm object =3D %s)\n",= s->shmobj); >> + >> + =A0 =A0 =A0 =A0/* try opening with O_EXCL and if it succeeds zero the = memory >> + =A0 =A0 =A0 =A0 * by truncating to 0 */ >> + =A0 =A0 =A0 =A0if ((fd =3D shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0S_IRWXU|S_IRWXG|S_IRWXO= ))> =A00) { >> + =A0 =A0 =A0 =A0 =A0 /* truncate file to length PCI device's memory */ >> + =A0 =A0 =A0 =A0 =A0 =A0if (ftruncate(fd, s->ivshmem_size) !=3D 0) { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "kvm_ivshmem: could not= truncate shared >> file\n"); >> + =A0 =A0 =A0 =A0 =A0 =A0} >> + >> + =A0 =A0 =A0 =A0} else if ((fd =3D shm_open(s->shmobj, O_CREAT|O_RDWR, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0S_IRWXU|S_IRWXG|S_IRWXO= ))< =A00) { >> + =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "kvm_ivshmem: could not open sh= ared file\n"); >> + =A0 =A0 =A0 =A0 =A0 =A0exit(-1); >> + =A0 =A0 =A0 =A0} >> + >> + =A0 =A0 =A0 =A0create_shared_memory_BAR(s, fd); >> + >> + =A0 =A0} >> + >> + >> + =A0 =A0return 0; >> +} >> + >> +static int pci_ivshmem_uninit(PCIDevice *dev) >> +{ >> + =A0 =A0IVShmemState *s =3D DO_UPCAST(IVShmemState, dev, dev); >> + >> + =A0 =A0cpu_unregister_io_memory(s->ivshmem_mmio_io_addr); >> + >> + =A0 =A0return 0; >> +} >> + >> +static PCIDeviceInfo ivshmem_info =3D { >> + =A0 =A0.qdev.name =A0=3D "ivshmem", >> + =A0 =A0.qdev.size =A0=3D sizeof(IVShmemState), >> + =A0 =A0.qdev.reset =3D ivshmem_reset, >> + =A0 =A0.init =A0 =A0 =A0 =3D pci_ivshmem_init, >> + =A0 =A0.exit =A0 =A0 =A0 =3D pci_ivshmem_uninit, >> + =A0 =A0.qdev.props =3D (Property[]) { >> + =A0 =A0 =A0 =A0DEFINE_PROP_CHR("chardev", IVShmemState, chr), >> + =A0 =A0 =A0 =A0DEFINE_PROP_STRING("size", IVShmemState, sizearg), >> + =A0 =A0 =A0 =A0DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1)= , >> + =A0 =A0 =A0 =A0DEFINE_PROP_BIT("irqfd", IVShmemState, features, IVSHME= M_IRQFD, >> false), >> + =A0 =A0 =A0 =A0DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_= MSI, >> true), >> + =A0 =A0 =A0 =A0DEFINE_PROP_STRING("shm", IVShmemState, shmobj), >> + =A0 =A0 =A0 =A0DEFINE_PROP_END_OF_LIST(), >> + =A0 =A0} >> +}; >> + >> +static void ivshmem_register_devices(void) >> +{ >> + =A0 =A0pci_qdev_register(&ivshmem_info); >> +} >> + >> +device_init(ivshmem_register_devices) >> diff --git a/qemu-char.c b/qemu-char.c >> index 048da3f..41cb8c7 100644 >> --- a/qemu-char.c >> +++ b/qemu-char.c >> @@ -2076,6 +2076,12 @@ static void tcp_chr_read(void *opaque) >> =A0 =A0 =A0} >> =A0} >> >> +CharDriverState *qemu_chr_open_eventfd(int eventfd){ >> + >> + =A0 =A0return qemu_chr_open_fd(eventfd, eventfd); >> + >> +} >> + >> =A0static void tcp_chr_connect(void *opaque) >> =A0{ >> =A0 =A0 =A0CharDriverState *chr =3D opaque; >> diff --git a/qemu-char.h b/qemu-char.h >> index 3a9427b..1571091 100644 >> --- a/qemu-char.h >> +++ b/qemu-char.h >> @@ -93,6 +93,9 @@ void qemu_chr_info_print(Monitor *mon, const QObject >> *ret_data); >> =A0void qemu_chr_info(Monitor *mon, QObject **ret_data); >> =A0CharDriverState *qemu_chr_find(const char *name); >> >> +/* add an eventfd to the qemu devices that are polled */ >> +CharDriverState *qemu_chr_open_eventfd(int eventfd); >> + >> =A0extern int term_escape_char; >> >> =A0/* async I/O support */ >> diff --git a/qemu-doc.texi b/qemu-doc.texi >> index 6647b7b..2df4687 100644 >> --- a/qemu-doc.texi >> +++ b/qemu-doc.texi >> @@ -706,6 +706,31 @@ Using the @option{-net socket} option, it is possib= le >> to make VLANs >> =A0that span several QEMU instances. See @ref{sec_invocation} to have a >> =A0basic example. >> >> +@section Other Devices >> + >> +@subsection Inter-VM Shared Memory device >> + >> +With KVM enabled on a Linux host, a shared memory device is available. >> =A0Guests >> +map a POSIX shared memory region into the guest as a PCI device that >> enables >> +zero-copy communication to the application level of the guests. =A0The >> basic >> +syntax is: >> + >> +@example >> +qemu -device ivshmem,size=3D[,shm=3D> name>] >> +@end example >> + >> +If desired, interrupts can be sent between guest VMs accessing the same >> shared >> +memory region. =A0Interrupt support requires using a shared memory serv= er >> and >> +using a chardev socket to connect to it. =A0The code for the shared mem= ory >> server >> +is qemu.git/contrib/ivshmem-server. =A0An example syntax when using the >> shared >> +memory server is: >> + >> +@example >> +qemu -device ivshmem,size=3D[,shm=3D> name>] >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[,chardev=3D][,msi= =3Don][,irqfd=3Don][,vectors=3Dn] >> +qemu -chardev socket,path=3D,id=3D >> +@end example >> + >> =A0@node direct_linux_boot >> =A0@section Direct Linux Boot >> >> > >