X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/f2426947decc96bde4f9ea50097dac66a0a48acc..6a19cd34b175bfc99bf5aa877e6e8eda636848f9:/memory.c diff --git a/memory.c b/memory.c index ef0be1c3aa..20f6d9eeac 100644 --- a/memory.c +++ b/memory.c @@ -33,26 +33,12 @@ static bool memory_region_update_pending; static bool ioeventfd_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; /* @@ -242,6 +228,7 @@ struct FlatRange { * order. */ struct FlatView { + struct rcu_head rcu; unsigned ref; FlatRange *ranges; unsigned nr; @@ -654,10 +641,10 @@ static FlatView *address_space_get_flatview(AddressSpace *as) { FlatView *view; - qemu_mutex_lock(&flat_view_mutex); - view = as->current_map; + rcu_read_lock(); + view = atomic_rcu_read(&as->current_map); flatview_ref(view); - qemu_mutex_unlock(&flat_view_mutex); + rcu_read_unlock(); return view; } @@ -766,10 +753,9 @@ static void address_space_update_topology(AddressSpace *as) address_space_update_topology_pass(as, old_view, new_view, false); address_space_update_topology_pass(as, old_view, new_view, true); - qemu_mutex_lock(&flat_view_mutex); - flatview_unref(as->current_map); - as->current_map = new_view; - qemu_mutex_unlock(&flat_view_mutex); + /* Writes are protected by the BQL. */ + atomic_rcu_set(&as->current_map, new_view); + call_rcu(old_view, flatview_unref, rcu); /* Note that all the old MemoryRegions are still alive up to this * point. This relieves most MemoryListeners from the need to @@ -876,30 +862,6 @@ static char *memory_region_escape_name(const char *name) return escaped; } -static void object_property_add_child_array(Object *owner, - const char *name, - Object *child) -{ - int i; - char *base_name = memory_region_escape_name(name); - - for (i = 0; ; i++) { - char *full_name = g_strdup_printf("%s[%d]", base_name, i); - Error *local_err = NULL; - - object_property_add_child(owner, full_name, child, &local_err); - g_free(full_name); - if (!local_err) { - break; - } - - error_free(local_err); - } - - g_free(base_name); -} - - void memory_region_init(MemoryRegion *mr, Object *owner, const char *name, @@ -917,8 +879,12 @@ void memory_region_init(MemoryRegion *mr, mr->name = g_strdup(name); if (name) { - object_property_add_child_array(owner, name, OBJECT(mr)); + char *escaped_name = memory_region_escape_name(name); + char *name_array = g_strdup_printf("%s[*]", escaped_name); + object_property_add_child(owner, name_array, OBJECT(mr), &error_abort); object_unref(OBJECT(mr)); + g_free(name_array); + g_free(escaped_name); } } @@ -1162,13 +1128,31 @@ void memory_region_init_io(MemoryRegion *mr, void memory_region_init_ram(MemoryRegion *mr, Object *owner, const char *name, - uint64_t size) + uint64_t size, + Error **errp) { memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_addr = qemu_ram_alloc(size, mr); + mr->ram_addr = qemu_ram_alloc(size, mr, errp); +} + +void memory_region_init_resizeable_ram(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + uint64_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + Error **errp) +{ + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->ram_addr = qemu_ram_alloc_resizeable(size, max_size, resized, mr, errp); } #ifdef __linux__ @@ -1198,7 +1182,15 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram_from_ptr; - mr->ram_addr = qemu_ram_alloc_from_ptr(size, ptr, mr); + + /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */ + assert(ptr != NULL); + mr->ram_addr = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_abort); +} + +void memory_region_set_skip_dump(MemoryRegion *mr) +{ + mr->skip_dump = true; } void memory_region_init_alias(MemoryRegion *mr, @@ -1220,7 +1212,8 @@ void memory_region_init_rom_device(MemoryRegion *mr, const MemoryRegionOps *ops, void *opaque, const char *name, - uint64_t size) + uint64_t size, + Error **errp) { memory_region_init(mr, owner, name, size); mr->ops = ops; @@ -1228,7 +1221,7 @@ void memory_region_init_rom_device(MemoryRegion *mr, mr->terminates = true; mr->rom_device = true; mr->destructor = memory_region_destructor_rom_device; - mr->ram_addr = qemu_ram_alloc(size, mr); + mr->ram_addr = qemu_ram_alloc(size, mr, errp); } void memory_region_init_iommu(MemoryRegion *mr, @@ -1256,7 +1249,6 @@ static void memory_region_finalize(Object *obj) MemoryRegion *mr = MEMORY_REGION(obj); assert(QTAILQ_EMPTY(&mr->subregions)); - assert(memory_region_transaction_depth == 0); mr->destructor(mr); memory_region_clear_coalescing(mr); g_free((char *)mr->name); @@ -1321,6 +1313,11 @@ bool memory_region_is_ram(MemoryRegion *mr) return mr->ram; } +bool memory_region_is_skip_dump(MemoryRegion *mr) +{ + return mr->skip_dump; +} + bool memory_region_is_logging(MemoryRegion *mr) { return mr->dirty_log_mask; @@ -1712,6 +1709,22 @@ void memory_region_set_enabled(MemoryRegion *mr, bool enabled) memory_region_transaction_commit(); } +void memory_region_set_size(MemoryRegion *mr, uint64_t size) +{ + Int128 s = int128_make64(size); + + if (size == UINT64_MAX) { + s = int128_2_64(); + } + if (int128_eq(s, mr->size)) { + return; + } + memory_region_transaction_begin(); + mr->size = s; + memory_region_update_pending = true; + memory_region_transaction_commit(); +} + static void memory_region_readd_subregion(MemoryRegion *mr) { MemoryRegion *container = mr->container; @@ -1754,6 +1767,11 @@ ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr) return mr->ram_addr; } +uint64_t memory_region_get_alignment(const MemoryRegion *mr) +{ + return mr->align; +} + static int cmp_flatrange_addr(const void *addr_, const void *fr_) { const AddrRange *addr = addr_; @@ -1810,11 +1828,11 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, } range = addrrange_make(int128_make64(addr), int128_make64(size)); - view = address_space_get_flatview(as); + rcu_read_lock(); + view = atomic_rcu_read(&as->current_map); fr = flatview_lookup(view, range); if (!fr) { - flatview_unref(view); - return ret; + goto out; } while (fr > view->ranges && addrrange_intersects(fr[-1].addr, range)) { @@ -1831,8 +1849,8 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, ret.offset_within_address_space = int128_get64(range.start); ret.readonly = fr->readonly; memory_region_ref(ret.mr); - - flatview_unref(view); +out: + rcu_read_unlock(); return ret; } @@ -1925,10 +1943,7 @@ 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_ref(root); memory_region_transaction_begin(); as->root = root; as->current_map = g_new(FlatView, 1); @@ -1942,15 +1957,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) memory_region_transaction_commit(); } -void address_space_destroy(AddressSpace *as) +static void do_address_space_destroy(AddressSpace *as) { MemoryListener *listener; - /* Flush out anything from MemoryListeners listening in on this */ - memory_region_transaction_begin(); - as->root = NULL; - memory_region_transaction_commit(); - QTAILQ_REMOVE(&address_spaces, as, address_spaces_link); address_space_destroy_dispatch(as); QTAILQ_FOREACH(listener, &memory_listeners, link) { @@ -1960,6 +1970,26 @@ void address_space_destroy(AddressSpace *as) flatview_unref(as->current_map); g_free(as->name); g_free(as->ioeventfds); + memory_region_unref(as->root); +} + +void address_space_destroy(AddressSpace *as) +{ + MemoryRegion *root = as->root; + + /* Flush out anything from MemoryListeners listening in on this */ + memory_region_transaction_begin(); + as->root = NULL; + memory_region_transaction_commit(); + QTAILQ_REMOVE(&address_spaces, as, address_spaces_link); + address_space_unregister(as); + + /* At this point, as->dispatch and as->current_map are dummy + * entries that the guest should never use. Wait for the old + * values to expire before freeing the data. + */ + as->root = root; + call_rcu(as, do_address_space_destroy, rcu); } bool io_mem_read(MemoryRegion *mr, hwaddr addr, uint64_t *pval, unsigned size)