[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC v3 36/56] memory: add ioeventfd support
From: |
Avi Kivity |
Subject: |
[Qemu-devel] [RFC v3 36/56] memory: add ioeventfd support |
Date: |
Sun, 10 Jul 2011 21:14:49 +0300 |
As with the rest of the memory API, the caller associates an eventfd
with an address, and the memory API takes care of registering or
unregistering when the address is made visible or invisible to the
guest.
Signed-off-by: Avi Kivity <address@hidden>
---
memory.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
memory.h | 20 ++++++
2 files changed, 238 insertions(+), 0 deletions(-)
diff --git a/memory.c b/memory.c
index 62bd60b..0d2ecf1 100644
--- a/memory.c
+++ b/memory.c
@@ -14,6 +14,7 @@
#include "memory.h"
#include "exec-memory.h"
#include "ioport.h"
+#include "kvm.h"
#include <assert.h>
typedef struct AddrRange AddrRange;
@@ -63,6 +64,38 @@ struct CoalescedMemoryRange {
QTAILQ_ENTRY(CoalescedMemoryRange) link;
};
+struct MemoryRegionIoeventfd {
+ AddrRange addr;
+ bool match_data;
+ uint64_t data;
+ int fd;
+};
+
+static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a,
+ MemoryRegionIoeventfd b)
+{
+ if (a.addr.start < b.addr.start) return true;
+ if (a.addr.start > b.addr.start) return false;
+ if (a.addr.size < b.addr.size) return true;
+ if (a.addr.size > b.addr.size) return false;
+ if (a.match_data < b.match_data) return true;
+ if (a.match_data > b.match_data) return false;
+ if (a.match_data) {
+ if (a.data < b.data) return true;
+ if (a.data > b.data) return false;
+ }
+ if (a.fd < b.fd) return true;
+ if (a.fd > b.fd) return false;
+ return false;
+}
+
+static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a,
+ MemoryRegionIoeventfd b)
+{
+ return !memory_region_ioeventfd_before(a, b)
+ && !memory_region_ioeventfd_before(b, a);
+}
+
typedef struct FlatRange FlatRange;
typedef struct FlatView FlatView;
@@ -91,6 +124,8 @@ struct AddressSpace {
const AddressSpaceOps *ops;
MemoryRegion *root;
FlatView current_map;
+ int ioeventfd_nb;
+ MemoryRegionIoeventfd *ioeventfds;
};
struct AddressSpaceOps {
@@ -98,6 +133,8 @@ struct AddressSpaceOps {
void (*range_del)(AddressSpace *as, FlatRange *fr);
void (*log_start)(AddressSpace *as, FlatRange *fr);
void (*log_stop)(AddressSpace *as, FlatRange *fr);
+ void (*ioeventfd_add)(AddressSpace *as, MemoryRegionIoeventfd *fd);
+ void (*ioeventfd_del)(AddressSpace *as, MemoryRegionIoeventfd *fd);
};
#define FOR_EACH_FLAT_RANGE(var, view) \
@@ -200,11 +237,37 @@ static void as_memory_log_stop(AddressSpace *as,
FlatRange *fr)
cpu_physical_log_stop(fr->addr.start, fr->addr.size);
}
+static void as_memory_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd
*fd)
+{
+ int r;
+
+ if (!fd->match_data || fd->addr.size != 4) {
+ abort();
+ }
+
+ r = kvm_set_ioeventfd_mmio_long(fd->fd, fd->addr.start, fd->data, true);
+ if (r < 0) {
+ abort();
+ }
+}
+
+static void as_memory_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd
*fd)
+{
+ int r;
+
+ r = kvm_set_ioeventfd_mmio_long(fd->fd, fd->addr.start, fd->data, false);
+ if (r < 0) {
+ abort();
+ }
+}
+
static const AddressSpaceOps address_space_ops_memory = {
.range_add = as_memory_range_add,
.range_del = as_memory_range_del,
.log_start = as_memory_log_start,
.log_stop = as_memory_log_stop,
+ .ioeventfd_add = as_memory_ioeventfd_add,
+ .ioeventfd_del = as_memory_ioeventfd_del,
};
static AddressSpace address_space_memory = {
@@ -248,9 +311,35 @@ static void as_io_range_del(AddressSpace *as, FlatRange
*fr)
isa_unassign_ioport(fr->addr.start, fr->addr.size);
}
+static void as_io_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd)
+{
+ int r;
+
+ if (!fd->match_data || fd->addr.size != 2) {
+ abort();
+ }
+
+ r = kvm_set_ioeventfd_pio_word(fd->fd, fd->addr.start, fd->data, true);
+ if (r < 0) {
+ abort();
+ }
+}
+
+static void as_io_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd)
+{
+ int r;
+
+ r = kvm_set_ioeventfd_pio_word(fd->fd, fd->addr.start, fd->data, false);
+ if (r < 0) {
+ abort();
+ }
+}
+
static const AddressSpaceOps address_space_ops_io = {
.range_add = as_io_range_add,
.range_del = as_io_range_del,
+ .ioeventfd_add = as_io_ioeventfd_add,
+ .ioeventfd_del = as_io_ioeventfd_del,
};
static AddressSpace address_space_io = {
@@ -349,6 +438,69 @@ static FlatView generate_memory_topology(MemoryRegion *mr)
return view;
}
+static void address_space_add_del_ioeventfds(AddressSpace *as,
+ MemoryRegionIoeventfd *fds_new,
+ unsigned fds_new_nb,
+ MemoryRegionIoeventfd *fds_old,
+ unsigned fds_old_nb)
+{
+ unsigned iold, inew;
+
+ /* Generate a symmetric difference of the old and new fd sets, adding
+ * and deleting as necessary.
+ */
+
+ iold = inew = 0;
+ while (iold < fds_old_nb || inew < fds_new_nb) {
+ if (iold < fds_old_nb
+ && (inew == fds_new_nb
+ || memory_region_ioeventfd_before(fds_old[iold],
+ fds_new[inew]))) {
+ as->ops->ioeventfd_del(as, &fds_old[iold]);
+ ++iold;
+ } else if (inew < fds_new_nb
+ && (iold == fds_old_nb
+ || memory_region_ioeventfd_before(fds_new[inew],
+ fds_old[iold]))) {
+ as->ops->ioeventfd_add(as, &fds_new[inew]);
+ ++inew;
+ } else {
+ ++iold;
+ ++inew;
+ }
+ }
+}
+
+static void address_space_update_ioeventfds(AddressSpace *as)
+{
+ FlatRange *fr;
+ unsigned ioeventfd_nb = 0;
+ MemoryRegionIoeventfd *ioeventfds = NULL;
+ AddrRange tmp;
+ unsigned i;
+
+ FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
+ for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
+ tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
+ fr->addr.start - fr->offset_in_region);
+ if (addrrange_intersects(fr->addr, tmp)) {
+ ++ioeventfd_nb;
+ ioeventfds = qemu_realloc(ioeventfds,
+ ioeventfd_nb * sizeof(*ioeventfds));
+ ioeventfds[ioeventfd_nb-1] = fr->mr->ioeventfds[i];
+ ioeventfds[ioeventfd_nb-1].addr = tmp;
+ }
+ }
+ }
+
+ address_space_add_del_ioeventfds(as, ioeventfds, ioeventfd_nb,
+ as->ioeventfds, as->ioeventfd_nb);
+
+ qemu_free(as->ioeventfds);
+ as->ioeventfds = ioeventfds;
+ as->ioeventfd_nb = ioeventfd_nb;
+}
+
static void address_space_update_topology(AddressSpace *as)
{
FlatView old_view = as->current_map;
@@ -401,6 +553,7 @@ static void address_space_update_topology(AddressSpace *as)
}
as->current_map = new_view;
flatview_destroy(&old_view);
+ address_space_update_ioeventfds(as);
}
static void memory_region_update_topology(void)
@@ -431,6 +584,8 @@ void memory_region_init(MemoryRegion *mr,
QTAILQ_INIT(&mr->coalesced);
mr->name = qemu_strdup(name);
mr->dirty_log_mask = 0;
+ mr->ioeventfd_nb = 0;
+ mr->ioeventfds = NULL;
}
static bool memory_region_access_valid(MemoryRegion *mr,
@@ -633,6 +788,7 @@ void memory_region_destroy(MemoryRegion *mr)
assert(QTAILQ_EMPTY(&mr->subregions));
memory_region_clear_coalescing(mr);
qemu_free((char *)mr->name);
+ qemu_free(mr->ioeventfds);
}
target_phys_addr_t memory_region_size(MemoryRegion *mr)
@@ -756,6 +912,68 @@ void memory_region_clear_coalescing(MemoryRegion *mr)
memory_region_update_coalesced_range(mr);
}
+void memory_region_add_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd)
+{
+ MemoryRegionIoeventfd mrfd = {
+ .addr.start = addr,
+ .addr.size = size,
+ .match_data = match_data,
+ .data = data,
+ .fd = fd,
+ };
+ unsigned i;
+
+ for (i = 0; i < mr->ioeventfd_nb; ++i) {
+ if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) {
+ break;
+ }
+ }
+ ++mr->ioeventfd_nb;
+ mr->ioeventfds = qemu_realloc(mr->ioeventfds,
+ sizeof(*mr->ioeventfds) * mr->ioeventfd_nb);
+ memmove(&mr->ioeventfds[i+1], &mr->ioeventfds[i],
+ sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb-1 - i));
+ mr->ioeventfds[i] = mrfd;
+ memory_region_update_topology();
+}
+
+void memory_region_del_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd)
+{
+ MemoryRegionIoeventfd mrfd = {
+ .addr.start = addr,
+ .addr.size = size,
+ .match_data = match_data,
+ .data = data,
+ .fd = fd,
+ };
+ unsigned i;
+
+ for (i = 0; i < mr->ioeventfd_nb; ++i) {
+ if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) {
+ break;
+ }
+ }
+ if (i == mr->ioeventfd_nb) {
+ abort();
+ }
+ memmove(&mr->ioeventfds[i], &mr->ioeventfds[i+1],
+ sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb - (i+1)));
+ --mr->ioeventfd_nb;
+ mr->ioeventfds = qemu_realloc(mr->ioeventfds,
+ sizeof(*mr->ioeventfds)*mr->ioeventfd_nb +
1);
+ memory_region_update_topology();
+}
+
static void memory_region_add_subregion_common(MemoryRegion *mr,
target_phys_addr_t offset,
MemoryRegion *subregion)
diff --git a/memory.h b/memory.h
index 2afbf13..34e0acd 100644
--- a/memory.h
+++ b/memory.h
@@ -68,6 +68,7 @@ struct MemoryRegionOps {
};
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
+typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
struct MemoryRegion {
/* All fields are private - violators will be prosecuted */
@@ -90,6 +91,8 @@ struct MemoryRegion {
QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced;
const char *name;
uint8_t dirty_log_mask;
+ unsigned ioeventfd_nb;
+ MemoryRegionIoeventfd *ioeventfds;
};
/* Initialize a memory region
@@ -181,6 +184,23 @@ void memory_region_add_coalescing(MemoryRegion *mr,
/* Disable MMIO coalescing for the region. */
void memory_region_clear_coalescing(MemoryRegion *mr);
+
+/* Request an eventfd to be triggered when a word is written to a location */
+void memory_region_add_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd);
+
+/* Cancel an existing eventfd */
+void memory_region_del_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd);
+
/* Add a sub-region at @offset. The sub-region may not overlap with other
* subregions (except for those explicitly marked as overlapping)
*/
--
1.7.5.3
- [Qemu-devel] [RFC v3 10/56] ioport: register ranges by byte aligned addresses always, (continued)
- [Qemu-devel] [RFC v3 10/56] ioport: register ranges by byte aligned addresses always, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 53/56] pci: convert pci rom to memory API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 23/56] cirrus: simplify vga window mmio access functions, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 04/56] Internal interfaces for memory API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 33/56] eepro100: convert to memory API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 22/56] cirrus: simplify bitblt BAR access functions, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 14/56] pci: pass address space to pci bus when created, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 05/56] memory: abstract address space operations, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 27/56] exec.c: fix initialization of system I/O memory region, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 01/56] Hierarchical memory region API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 36/56] memory: add ioeventfd support,
Avi Kivity <=
- [Qemu-devel] [RFC v3 39/56] ahci: convert to memory API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 29/56] pci: allow I/O BARs to be registered with pci_register_bar_region(), Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 43/56] ne2000: convert to memory API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 52/56] pci: remove pci_register_bar_simple(), Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 50/56] xen-platform: convert to memory API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 26/56] Integrate I/O memory regions into qemu, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 13/56] pc: move global memory map out of pc_init1() and into its callers, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 15/56] pci: add MemoryRegion based BAR management API, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 28/56] pci: pass I/O address space to new PCI bus, Avi Kivity, 2011/07/10
- [Qemu-devel] [RFC v3 09/56] exec.c: initialize memory map, Avi Kivity, 2011/07/10