summaryrefslogtreecommitdiff
path: root/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'memory.c')
-rw-r--r--memory.c290
1 files changed, 167 insertions, 123 deletions
diff --git a/memory.c b/memory.c
index 757e9a5592..35553b52a6 100644
--- a/memory.c
+++ b/memory.c
@@ -17,6 +17,7 @@
#include "exec/address-spaces.h"
#include "exec/ioport.h"
#include "qemu/bitops.h"
+#include "qom/object.h"
#include "sysemu/kvm.h"
#include <assert.h>
@@ -28,12 +29,26 @@ static unsigned memory_region_transaction_depth;
static bool memory_region_update_pending;
static bool global_dirty_log = false;
+/* flat_view_mutex is taken around reading as->current_map; the critical
+ * section is extremely short, so I'm using a single mutex for every AS.
+ * We could also RCU for the read-side.
+ *
+ * The BQL is taken around transaction commits, hence both locks are taken
+ * while writing to as->current_map (with the BQL taken outside).
+ */
+static QemuMutex flat_view_mutex;
+
static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners
= QTAILQ_HEAD_INITIALIZER(memory_listeners);
static QTAILQ_HEAD(, AddressSpace) address_spaces
= QTAILQ_HEAD_INITIALIZER(address_spaces);
+static void memory_init(void)
+{
+ qemu_mutex_init(&flat_view_mutex);
+}
+
typedef struct AddrRange AddrRange;
/*
@@ -147,6 +162,7 @@ static bool memory_listener_match(MemoryListener *listener,
} \
} while (0)
+/* No need to ref/unref .mr, the FlatRange keeps it alive. */
#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback) \
MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \
.mr = (fr)->mr, \
@@ -223,6 +239,7 @@ struct FlatRange {
* order.
*/
struct FlatView {
+ unsigned ref;
FlatRange *ranges;
unsigned nr;
unsigned nr_allocated;
@@ -244,6 +261,7 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b)
static void flatview_init(FlatView *view)
{
+ view->ref = 1;
view->ranges = NULL;
view->nr = 0;
view->nr_allocated = 0;
@@ -262,12 +280,31 @@ static void flatview_insert(FlatView *view, unsigned pos, FlatRange *range)
memmove(view->ranges + pos + 1, view->ranges + pos,
(view->nr - pos) * sizeof(FlatRange));
view->ranges[pos] = *range;
+ memory_region_ref(range->mr);
++view->nr;
}
static void flatview_destroy(FlatView *view)
{
+ int i;
+
+ for (i = 0; i < view->nr; i++) {
+ memory_region_unref(view->ranges[i].mr);
+ }
g_free(view->ranges);
+ g_free(view);
+}
+
+static void flatview_ref(FlatView *view)
+{
+ atomic_inc(&view->ref);
+}
+
+static void flatview_unref(FlatView *view)
+{
+ if (atomic_fetch_dec(&view->ref) == 1) {
+ flatview_destroy(view);
+ }
}
static bool can_merge(FlatRange *r1, FlatRange *r2)
@@ -401,94 +438,6 @@ static void access_with_adjusted_size(hwaddr addr,
}
}
-static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset,
- unsigned width, bool write)
-{
- const MemoryRegionPortio *mrp;
-
- for (mrp = mr->ops->old_portio; mrp->size; ++mrp) {
- if (offset >= mrp->offset && offset < mrp->offset + mrp->len
- && width == mrp->size
- && (write ? (bool)mrp->write : (bool)mrp->read)) {
- return mrp;
- }
- }
- return NULL;
-}
-
-static void memory_region_iorange_read(IORange *iorange,
- uint64_t offset,
- unsigned width,
- uint64_t *data)
-{
- MemoryRegionIORange *mrio
- = container_of(iorange, MemoryRegionIORange, iorange);
- MemoryRegion *mr = mrio->mr;
-
- offset += mrio->offset;
- if (mr->ops->old_portio) {
- const MemoryRegionPortio *mrp = find_portio(mr, offset - mrio->offset,
- width, false);
-
- *data = ((uint64_t)1 << (width * 8)) - 1;
- if (mrp) {
- *data = mrp->read(mr->opaque, offset);
- } else if (width == 2) {
- mrp = find_portio(mr, offset - mrio->offset, 1, false);
- assert(mrp);
- *data = mrp->read(mr->opaque, offset) |
- (mrp->read(mr->opaque, offset + 1) << 8);
- }
- return;
- }
- *data = 0;
- access_with_adjusted_size(offset, data, width,
- mr->ops->impl.min_access_size,
- mr->ops->impl.max_access_size,
- memory_region_read_accessor, mr);
-}
-
-static void memory_region_iorange_write(IORange *iorange,
- uint64_t offset,
- unsigned width,
- uint64_t data)
-{
- MemoryRegionIORange *mrio
- = container_of(iorange, MemoryRegionIORange, iorange);
- MemoryRegion *mr = mrio->mr;
-
- offset += mrio->offset;
- if (mr->ops->old_portio) {
- const MemoryRegionPortio *mrp = find_portio(mr, offset - mrio->offset,
- width, true);
-
- if (mrp) {
- mrp->write(mr->opaque, offset, data);
- } else if (width == 2) {
- mrp = find_portio(mr, offset - mrio->offset, 1, true);
- assert(mrp);
- mrp->write(mr->opaque, offset, data & 0xff);
- mrp->write(mr->opaque, offset + 1, data >> 8);
- }
- return;
- }
- access_with_adjusted_size(offset, &data, width,
- mr->ops->impl.min_access_size,
- mr->ops->impl.max_access_size,
- memory_region_write_accessor, mr);
-}
-
-static void memory_region_iorange_destructor(IORange *iorange)
-{
- g_free(container_of(iorange, MemoryRegionIORange, iorange));
-}
-
-const IORangeOps memory_region_iorange_ops = {
- .read = memory_region_iorange_read,
- .write = memory_region_iorange_write,
- .destructor = memory_region_iorange_destructor,
-};
-
static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
{
AddressSpace *as;
@@ -592,17 +541,18 @@ static void render_memory_region(FlatView *view,
}
/* Render a memory topology into a list of disjoint absolute ranges. */
-static FlatView generate_memory_topology(MemoryRegion *mr)
+static FlatView *generate_memory_topology(MemoryRegion *mr)
{
- FlatView view;
+ FlatView *view;
- flatview_init(&view);
+ view = g_new(FlatView, 1);
+ flatview_init(view);
if (mr) {
- render_memory_region(&view, mr, int128_zero(),
+ render_memory_region(view, mr, int128_zero(),
addrrange_make(int128_zero(), int128_2_64()), false);
}
- flatview_simplify(&view);
+ flatview_simplify(view);
return view;
}
@@ -656,15 +606,28 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
}
}
+static FlatView *address_space_get_flatview(AddressSpace *as)
+{
+ FlatView *view;
+
+ qemu_mutex_lock(&flat_view_mutex);
+ view = as->current_map;
+ flatview_ref(view);
+ qemu_mutex_unlock(&flat_view_mutex);
+ return view;
+}
+
static void address_space_update_ioeventfds(AddressSpace *as)
{
+ FlatView *view;
FlatRange *fr;
unsigned ioeventfd_nb = 0;
MemoryRegionIoeventfd *ioeventfds = NULL;
AddrRange tmp;
unsigned i;
- FOR_EACH_FLAT_RANGE(fr, as->current_map) {
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
int128_sub(fr->addr.start,
@@ -685,11 +648,12 @@ static void address_space_update_ioeventfds(AddressSpace *as)
g_free(as->ioeventfds);
as->ioeventfds = ioeventfds;
as->ioeventfd_nb = ioeventfd_nb;
+ flatview_unref(view);
}
static void address_space_update_topology_pass(AddressSpace *as,
- FlatView old_view,
- FlatView new_view,
+ const FlatView *old_view,
+ const FlatView *new_view,
bool adding)
{
unsigned iold, inew;
@@ -699,14 +663,14 @@ static void address_space_update_topology_pass(AddressSpace *as,
* Kill ranges in the old map, and instantiate ranges in the new map.
*/
iold = inew = 0;
- while (iold < old_view.nr || inew < new_view.nr) {
- if (iold < old_view.nr) {
- frold = &old_view.ranges[iold];
+ while (iold < old_view->nr || inew < new_view->nr) {
+ if (iold < old_view->nr) {
+ frold = &old_view->ranges[iold];
} else {
frold = NULL;
}
- if (inew < new_view.nr) {
- frnew = &new_view.ranges[inew];
+ if (inew < new_view->nr) {
+ frnew = &new_view->ranges[inew];
} else {
frnew = NULL;
}
@@ -752,14 +716,25 @@ static void address_space_update_topology_pass(AddressSpace *as,
static void address_space_update_topology(AddressSpace *as)
{
- FlatView old_view = *as->current_map;
- FlatView new_view = generate_memory_topology(as->root);
+ FlatView *old_view = address_space_get_flatview(as);
+ FlatView *new_view = generate_memory_topology(as->root);
address_space_update_topology_pass(as, old_view, new_view, false);
address_space_update_topology_pass(as, old_view, new_view, true);
- *as->current_map = new_view;
- flatview_destroy(&old_view);
+ qemu_mutex_lock(&flat_view_mutex);
+ flatview_unref(as->current_map);
+ as->current_map = new_view;
+ qemu_mutex_unlock(&flat_view_mutex);
+
+ /* Note that all the old MemoryRegions are still alive up to this
+ * point. This relieves most MemoryListeners from the need to
+ * ref/unref the MemoryRegions they get---unless they use them
+ * outside the iothread mutex, in which case precise reference
+ * counting is necessary.
+ */
+ flatview_unref(old_view);
+
address_space_update_ioeventfds(as);
}
@@ -796,6 +771,11 @@ static void memory_region_destructor_ram(MemoryRegion *mr)
qemu_ram_free(mr->ram_addr);
}
+static void memory_region_destructor_alias(MemoryRegion *mr)
+{
+ memory_region_unref(mr->alias);
+}
+
static void memory_region_destructor_ram_from_ptr(MemoryRegion *mr)
{
qemu_ram_free_from_ptr(mr->ram_addr);
@@ -816,13 +796,16 @@ static bool memory_region_wrong_endianness(MemoryRegion *mr)
}
void memory_region_init(MemoryRegion *mr,
+ Object *owner,
const char *name,
uint64_t size)
{
mr->ops = &unassigned_mem_ops;
mr->opaque = NULL;
+ mr->owner = owner;
mr->iommu_ops = NULL;
mr->parent = NULL;
+ mr->owner = NULL;
mr->size = int128_make64(size);
if (size == UINT64_MAX) {
mr->size = int128_2_64();
@@ -1002,12 +985,13 @@ static bool memory_region_dispatch_write(MemoryRegion *mr,
}
void memory_region_init_io(MemoryRegion *mr,
+ Object *owner,
const MemoryRegionOps *ops,
void *opaque,
const char *name,
uint64_t size)
{
- memory_region_init(mr, name, size);
+ memory_region_init(mr, owner, name, size);
mr->ops = ops;
mr->opaque = opaque;
mr->terminates = true;
@@ -1015,10 +999,11 @@ void memory_region_init_io(MemoryRegion *mr,
}
void memory_region_init_ram(MemoryRegion *mr,
+ Object *owner,
const char *name,
uint64_t size)
{
- memory_region_init(mr, name, size);
+ memory_region_init(mr, owner, name, size);
mr->ram = true;
mr->terminates = true;
mr->destructor = memory_region_destructor_ram;
@@ -1026,11 +1011,12 @@ void memory_region_init_ram(MemoryRegion *mr,
}
void memory_region_init_ram_ptr(MemoryRegion *mr,
+ Object *owner,
const char *name,
uint64_t size,
void *ptr)
{
- memory_region_init(mr, name, size);
+ memory_region_init(mr, owner, name, size);
mr->ram = true;
mr->terminates = true;
mr->destructor = memory_region_destructor_ram_from_ptr;
@@ -1038,23 +1024,27 @@ void memory_region_init_ram_ptr(MemoryRegion *mr,
}
void memory_region_init_alias(MemoryRegion *mr,
+ Object *owner,
const char *name,
MemoryRegion *orig,
hwaddr offset,
uint64_t size)
{
- memory_region_init(mr, name, size);
+ memory_region_init(mr, owner, name, size);
+ memory_region_ref(orig);
+ mr->destructor = memory_region_destructor_alias;
mr->alias = orig;
mr->alias_offset = offset;
}
void memory_region_init_rom_device(MemoryRegion *mr,
+ Object *owner,
const MemoryRegionOps *ops,
void *opaque,
const char *name,
uint64_t size)
{
- memory_region_init(mr, name, size);
+ memory_region_init(mr, owner, name, size);
mr->ops = ops;
mr->opaque = opaque;
mr->terminates = true;
@@ -1064,21 +1054,23 @@ void memory_region_init_rom_device(MemoryRegion *mr,
}
void memory_region_init_iommu(MemoryRegion *mr,
+ Object *owner,
const MemoryRegionIOMMUOps *ops,
const char *name,
uint64_t size)
{
- memory_region_init(mr, name, size);
+ memory_region_init(mr, owner, name, size);
mr->iommu_ops = ops,
mr->terminates = true; /* then re-forwards */
notifier_list_init(&mr->iommu_notify);
}
void memory_region_init_reservation(MemoryRegion *mr,
+ Object *owner,
const char *name,
uint64_t size)
{
- memory_region_init_io(mr, &unassigned_mem_ops, mr, name, size);
+ memory_region_init_io(mr, owner, &unassigned_mem_ops, mr, name, size);
}
void memory_region_destroy(MemoryRegion *mr)
@@ -1091,6 +1083,25 @@ void memory_region_destroy(MemoryRegion *mr)
g_free(mr->ioeventfds);
}
+Object *memory_region_owner(MemoryRegion *mr)
+{
+ return mr->owner;
+}
+
+void memory_region_ref(MemoryRegion *mr)
+{
+ if (mr && mr->owner) {
+ object_ref(mr->owner);
+ }
+}
+
+void memory_region_unref(MemoryRegion *mr)
+{
+ if (mr && mr->owner) {
+ object_unref(mr->owner);
+ }
+}
+
uint64_t memory_region_size(MemoryRegion *mr)
{
if (int128_eq(mr->size, int128_2_64())) {
@@ -1188,11 +1199,13 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
FlatRange *fr;
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- FOR_EACH_FLAT_RANGE(fr, as->current_map) {
+ FlatView *view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
if (fr->mr == mr) {
MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
}
}
+ flatview_unref(view);
}
}
@@ -1238,12 +1251,14 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpace *as)
{
+ FlatView *view;
FlatRange *fr;
CoalescedMemoryRange *cmr;
AddrRange tmp;
MemoryRegionSection section;
- FOR_EACH_FLAT_RANGE(fr, as->current_map) {
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
if (fr->mr == mr) {
section = (MemoryRegionSection) {
.address_space = as,
@@ -1268,6 +1283,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
}
}
}
+ flatview_unref(view);
}
static void memory_region_update_coalesced_range(MemoryRegion *mr)
@@ -1400,6 +1416,7 @@ static void memory_region_add_subregion_common(MemoryRegion *mr,
memory_region_transaction_begin();
assert(!subregion->parent);
+ memory_region_ref(subregion);
subregion->parent = mr;
subregion->addr = offset;
QTAILQ_FOREACH(other, &mr->subregions, subregions_link) {
@@ -1462,6 +1479,7 @@ void memory_region_del_subregion(MemoryRegion *mr,
assert(subregion->parent == mr);
subregion->parent = NULL;
QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
+ memory_region_unref(subregion);
memory_region_update_pending |= mr->enabled && subregion->enabled;
memory_region_transaction_commit();
}
@@ -1489,12 +1507,14 @@ void memory_region_set_address(MemoryRegion *mr, hwaddr addr)
}
memory_region_transaction_begin();
+ memory_region_ref(mr);
memory_region_del_subregion(parent, mr);
if (may_overlap) {
memory_region_add_subregion_overlap(parent, addr, mr, priority);
} else {
memory_region_add_subregion(parent, addr, mr);
}
+ memory_region_unref(mr);
memory_region_transaction_commit();
}
@@ -1530,12 +1550,22 @@ static int cmp_flatrange_addr(const void *addr_, const void *fr_)
return 0;
}
-static FlatRange *address_space_lookup(AddressSpace *as, AddrRange addr)
+static FlatRange *flatview_lookup(FlatView *view, AddrRange addr)
{
- return bsearch(&addr, as->current_map->ranges, as->current_map->nr,
+ return bsearch(&addr, view->ranges, view->nr,
sizeof(FlatRange), cmp_flatrange_addr);
}
+bool memory_region_present(MemoryRegion *parent, hwaddr addr)
+{
+ MemoryRegion *mr = memory_region_find(parent, addr, 1).mr;
+ if (!mr) {
+ return false;
+ }
+ memory_region_unref(mr);
+ return true;
+}
+
MemoryRegionSection memory_region_find(MemoryRegion *mr,
hwaddr addr, uint64_t size)
{
@@ -1543,6 +1573,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
MemoryRegion *root;
AddressSpace *as;
AddrRange range;
+ FlatView *view;
FlatRange *fr;
addr += mr->addr;
@@ -1553,13 +1584,14 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
as = memory_region_to_address_space(root);
range = addrrange_make(int128_make64(addr), int128_make64(size));
- fr = address_space_lookup(as, range);
+
+ view = address_space_get_flatview(as);
+ fr = flatview_lookup(view, range);
if (!fr) {
return ret;
}
- while (fr > as->current_map->ranges
- && addrrange_intersects(fr[-1].addr, range)) {
+ while (fr > view->ranges && addrrange_intersects(fr[-1].addr, range)) {
--fr;
}
@@ -1572,16 +1604,22 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
ret.size = range.size;
ret.offset_within_address_space = int128_get64(range.start);
ret.readonly = fr->readonly;
+ memory_region_ref(ret.mr);
+
+ flatview_unref(view);
return ret;
}
void address_space_sync_dirty_bitmap(AddressSpace *as)
{
+ FlatView *view;
FlatRange *fr;
- FOR_EACH_FLAT_RANGE(fr, as->current_map) {
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
}
+ flatview_unref(view);
}
void memory_global_dirty_log_start(void)
@@ -1599,6 +1637,7 @@ void memory_global_dirty_log_stop(void)
static void listener_add_address_space(MemoryListener *listener,
AddressSpace *as)
{
+ FlatView *view;
FlatRange *fr;
if (listener->address_space_filter
@@ -1612,7 +1651,8 @@ static void listener_add_address_space(MemoryListener *listener,
}
}
- FOR_EACH_FLAT_RANGE(fr, as->current_map) {
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
MemoryRegionSection section = {
.mr = fr->mr,
.address_space = as,
@@ -1625,6 +1665,7 @@ static void listener_add_address_space(MemoryListener *listener,
listener->region_add(listener, &section);
}
}
+ flatview_unref(view);
}
void memory_listener_register(MemoryListener *listener, AddressSpace *filter)
@@ -1658,6 +1699,10 @@ void memory_listener_unregister(MemoryListener *listener)
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
{
+ if (QTAILQ_EMPTY(&address_spaces)) {
+ memory_init();
+ }
+
memory_region_transaction_begin();
as->root = root;
as->current_map = g_new(FlatView, 1);
@@ -1679,9 +1724,8 @@ void address_space_destroy(AddressSpace *as)
memory_region_transaction_commit();
QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
address_space_destroy_dispatch(as);
- flatview_destroy(as->current_map);
+ flatview_unref(as->current_map);
g_free(as->name);
- g_free(as->current_map);
g_free(as->ioeventfds);
}