@@ -40,6 +40,7 @@ static unsigned memory_region_transaction_depth;
static bool memory_region_update_pending;
static bool ioeventfd_update_pending;
unsigned int global_dirty_tracking;
+static bool ioregionfd_update_pending;
static QTAILQ_HEAD(, MemoryListener) memory_listeners
= QTAILQ_HEAD_INITIALIZER(memory_listeners);
@@ -170,6 +171,13 @@ struct MemoryRegionIoeventfd {
EventNotifier *e;
};
+struct MemoryRegionIoregionfd {
+ AddrRange addr;
+ uint64_t data;
+ int fd;
+ bool pio;
+};
+
static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd *a,
MemoryRegionIoeventfd *b)
{
@@ -214,6 +222,33 @@ static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd *a,
return false;
}
+static bool memory_region_ioregionfd_before(MemoryRegionIoregionfd *a,
+ MemoryRegionIoregionfd *b)
+{
+ if (int128_lt(a->addr.start, b->addr.start)) {
+ return true;
+ } else if (int128_gt(a->addr.start, b->addr.start)) {
+ return false;
+ } else if (int128_lt(a->addr.size, b->addr.size)) {
+ return true;
+ } else if (int128_gt(a->addr.size, b->addr.size)) {
+ return false;
+ }
+ return false;
+}
+
+static bool memory_region_ioregionfd_equal(MemoryRegionIoregionfd *a,
+ MemoryRegionIoregionfd *b)
+{
+ if (int128_eq(a->addr.start, b->addr.start) &&
+ (!int128_nz(a->addr.size) || !int128_nz(b->addr.size) ||
+ (int128_eq(a->addr.size, b->addr.size) &&
+ (a->fd == b->fd))))
+ return true;
+
+ return false;
+}
+
/* Range of memory in the global map. Addresses are absolute. */
struct FlatRange {
MemoryRegion *mr;
@@ -800,6 +835,52 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
}
}
+static void address_space_add_del_ioregionfds(AddressSpace *as,
+ MemoryRegionIoregionfd *fds_new,
+ unsigned fds_new_nb,
+ MemoryRegionIoregionfd *fds_old,
+ unsigned fds_old_nb)
+{
+ unsigned iold, inew;
+ MemoryRegionIoregionfd *fd;
+ MemoryRegionSection section;
+
+ iold = inew = 0;
+ while (iold < fds_old_nb || inew < fds_new_nb) {
+ if (iold < fds_old_nb
+ && (inew == fds_new_nb
+ || memory_region_ioregionfd_before(&fds_old[iold],
+ &fds_new[inew]))) {
+ fd = &fds_old[iold];
+ section = (MemoryRegionSection) {
+ .fv = address_space_to_flatview(as),
+ .offset_within_address_space = int128_get64(fd->addr.start),
+ .size = fd->addr.size,
+ };
+ MEMORY_LISTENER_CALL(as, ioregionfd_del, Forward, §ion,
+ fd->data, fd->fd);
+ ++iold;
+
+ } else if (inew < fds_new_nb
+ && (iold == fds_old_nb
+ || memory_region_ioregionfd_before(&fds_new[inew],
+ &fds_old[iold]))) {
+ fd = &fds_new[inew];
+ section = (MemoryRegionSection) {
+ .fv = address_space_to_flatview(as),
+ .offset_within_address_space = int128_get64(fd->addr.start),
+ .size = fd->addr.size,
+ };
+ MEMORY_LISTENER_CALL(as, ioregionfd_add, Reverse, §ion,
+ fd->data, fd->fd);
+ ++inew;
+ } else {
+ ++iold;
+ ++inew;
+ }
+ }
+}
+
FlatView *address_space_get_flatview(AddressSpace *as)
{
FlatView *view;
@@ -814,6 +895,52 @@ FlatView *address_space_get_flatview(AddressSpace *as)
return view;
}
+static void address_space_update_ioregionfds(AddressSpace *as)
+{
+ FlatView *view;
+ FlatRange *fr;
+ unsigned ioregionfd_nb = 0;
+ unsigned ioregionfd_max;
+ MemoryRegionIoregionfd *ioregionfds;
+ AddrRange tmp;
+ unsigned i;
+
+ /*
+ * It is likely that the number of ioregionfds hasn't changed much, so use
+ * the previous size as the starting value, with some headroom to avoid
+ * gratuitous reallocations.
+ */
+ ioregionfd_max = QEMU_ALIGN_UP(as->ioregionfd_nb, 4);
+ ioregionfds = g_new(MemoryRegionIoregionfd, ioregionfd_max);
+
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ for (i = 0; i < fr->mr->ioregionfd_nb; ++i) {
+ tmp = addrrange_shift(fr->mr->ioregionfds[i].addr,
+ int128_sub(fr->addr.start,
+ int128_make64(fr->offset_in_region)));
+ if (addrrange_intersects(fr->addr, tmp)) {
+ ++ioregionfd_nb;
+ if (ioregionfd_nb > ioregionfd_max) {
+ ioregionfd_max = MAX(ioregionfd_max * 2, 4);
+ ioregionfds = g_realloc(ioregionfds,
+ ioregionfd_max * sizeof(*ioregionfds));
+ }
+ ioregionfds[ioregionfd_nb - 1] = fr->mr->ioregionfds[i];
+ ioregionfds[ioregionfd_nb - 1].addr = tmp;
+ }
+ }
+ }
+
+ address_space_add_del_ioregionfds(as, ioregionfds, ioregionfd_nb,
+ as->ioregionfds, as->ioregionfd_nb);
+
+ g_free(as->ioregionfds);
+ as->ioregionfds = ioregionfds;
+ as->ioregionfd_nb = ioregionfd_nb;
+ flatview_unref(view);
+}
+
static void address_space_update_ioeventfds(AddressSpace *as)
{
FlatView *view;
@@ -1102,15 +1229,22 @@ void memory_region_transaction_commit(void)
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
address_space_set_flatview(as);
address_space_update_ioeventfds(as);
+ address_space_update_ioregionfds(as);
}
memory_region_update_pending = false;
ioeventfd_update_pending = false;
+ ioregionfd_update_pending = false;
MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
} else if (ioeventfd_update_pending) {
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
address_space_update_ioeventfds(as);
}
ioeventfd_update_pending = false;
+ } else if (ioregionfd_update_pending) {
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ address_space_update_ioregionfds(as);
+ }
+ ioregionfd_update_pending = false;
}
}
}
@@ -1757,6 +1891,7 @@ static void memory_region_finalize(Object *obj)
memory_region_clear_coalescing(mr);
g_free((char *)mr->name);
g_free(mr->ioeventfds);
+ g_free(mr->ioregionfds);
}
Object *memory_region_owner(MemoryRegion *mr)
@@ -2434,6 +2569,42 @@ void memory_region_clear_flush_coalesced(MemoryRegion *mr)
static bool userspace_eventfd_warning;
+void memory_region_add_ioregionfd(MemoryRegion *mr,
+ hwaddr addr,
+ unsigned size,
+ uint64_t data,
+ int fd,
+ bool pio)
+{
+ MemoryRegionIoregionfd mriofd = {
+ .addr.start = int128_make64(addr),
+ .addr.size = int128_make64(size),
+ .data = data,
+ .fd = fd,
+ };
+ unsigned i;
+
+ if (kvm_enabled() && !kvm_ioregionfds_enabled()) {
+ error_report("KVM does not support KVM_CAP_IOREGIONFD");
+ }
+
+ memory_region_transaction_begin();
+ for (i = 0; i < mr->ioregionfd_nb; ++i) {
+ if (memory_region_ioregionfd_before(&mriofd, &mr->ioregionfds[i])) {
+ break;
+ }
+ }
+ ++mr->ioregionfd_nb;
+ mr->ioregionfds = g_realloc(mr->ioregionfds,
+ sizeof(*mr->ioregionfds) * mr->ioregionfd_nb);
+ memmove(&mr->ioregionfds[i + 1], &mr->ioregionfds[i],
+ sizeof(*mr->ioregionfds) * (mr->ioregionfd_nb - 1 - i));
+ mr->ioregionfds[i] = mriofd;
+
+ memory_region_transaction_commit();
+ ioregionfd_update_pending = true;
+}
+
void memory_region_add_eventfd(MemoryRegion *mr,
hwaddr addr,
unsigned size,
@@ -2511,6 +2682,38 @@ void memory_region_del_eventfd(MemoryRegion *mr,
memory_region_transaction_commit();
}
+void memory_region_del_ioregionfd(MemoryRegion *mr,
+ hwaddr addr,
+ unsigned size,
+ uint64_t data,
+ int fd)
+{
+ MemoryRegionIoregionfd mriofd = {
+ .addr.start = int128_make64(addr),
+ .addr.size = int128_make64(size),
+ .data = data,
+ .fd = fd,
+ };
+ unsigned i;
+
+ memory_region_transaction_begin();
+ for (i = 0; i < mr->ioregionfd_nb; ++i) {
+ if (memory_region_ioregionfd_equal(&mriofd, &mr->ioregionfds[i])) {
+ break;
+ }
+ }
+ assert(i != mr->ioregionfd_nb);
+ memmove(&mr->ioregionfds[i], &mr->ioregionfds[i + 1],
+ sizeof(*mr->ioregionfds) * (mr->ioregionfd_nb - (i + 1)));
+ --mr->ioregionfd_nb;
+ mr->ioregionfds = g_realloc(mr->ioregionfds,
+ sizeof(*mr->ioregionfds) *
+ mr->ioregionfd_nb + 1);
+ memory_region_transaction_commit();
+
+ ioregionfd_update_pending = true;
+}
+
static void memory_region_update_container_subregions(MemoryRegion *subregion)
{
MemoryRegion *mr = subregion->container;
@@ -2956,11 +3159,14 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
as->current_map = NULL;
as->ioeventfd_nb = 0;
as->ioeventfds = NULL;
+ as->ioregionfd_nb = 0;
+ as->ioregionfds = NULL;
QTAILQ_INIT(&as->listeners);
QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
as->name = g_strdup(name ? name : "anonymous");
address_space_update_topology(as);
address_space_update_ioeventfds(as);
+ address_space_update_ioregionfds(as);
}
static void do_address_space_destroy(AddressSpace *as)
@@ -2970,6 +3176,7 @@ static void do_address_space_destroy(AddressSpace *as)
flatview_unref(as->current_map);
g_free(as->name);
g_free(as->ioeventfds);
+ g_free(as->ioregionfds);
memory_region_unref(as->root);
}
Similar to ioeventfd, introduce the ioregionfd functions to add and delete ioregionfds. Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com> --- softmmu/memory.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+)