diff mbox series

[RFC,qemu,3/4] memory: Share flat views and dispatch trees between address spaces

Message ID 20170907092010.3605-4-aik@ozlabs.ru
State New
Headers show
Series memory: Reduce memory use | expand

Commit Message

Alexey Kardashevskiy Sept. 7, 2017, 9:20 a.m. UTC
This allows sharing flat views between address spaces when the same root
memory region is used when creating a new address space.

This adds a global list of flat views and a list of attached address
spaces per a flat view. Each address space references a flat view.

This hard codes the dispatch tree building instead of doing so via
a memory listener.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---

This was suggested by Paolo in
https://lists.gnu.org/archive/html/qemu-devel/2017-08/msg05011.html

I am not putting "Suggested-by" as I want to make sure that this is doing
what was actually suggested.
---
 include/exec/memory-internal.h |   6 +-
 include/exec/memory.h          |   9 +-
 exec.c                         |  58 ++--------
 hw/pci/pci.c                   |   3 +-
 memory.c                       | 253 +++++++++++++++++++++++++++--------------
 5 files changed, 187 insertions(+), 142 deletions(-)

Comments

Philippe Mathieu-Daudé Sept. 7, 2017, 8:53 p.m. UTC | #1
On 09/07/2017 06:20 AM, Alexey Kardashevskiy wrote:
> This allows sharing flat views between address spaces when the same root
> memory region is used when creating a new address space.
> 
> This adds a global list of flat views and a list of attached address
> spaces per a flat view. Each address space references a flat view.
> 
> This hard codes the dispatch tree building instead of doing so via
> a memory listener.
> 
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> 
> This was suggested by Paolo in
> https://lists.gnu.org/archive/html/qemu-devel/2017-08/msg05011.html
> 
> I am not putting "Suggested-by" as I want to make sure that this is doing
> what was actually suggested.
> ---
>   include/exec/memory-internal.h |   6 +-
>   include/exec/memory.h          |   9 +-
>   exec.c                         |  58 ++--------
>   hw/pci/pci.c                   |   3 +-
>   memory.c                       | 253 +++++++++++++++++++++++++++--------------
>   5 files changed, 187 insertions(+), 142 deletions(-)
> 
> diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h
> index fb467acdba..8516e0b48f 100644
> --- a/include/exec/memory-internal.h
> +++ b/include/exec/memory-internal.h
> @@ -22,9 +22,11 @@
>   #ifndef CONFIG_USER_ONLY
>   typedef struct AddressSpaceDispatch AddressSpaceDispatch;
>   
> -void address_space_init_dispatch(AddressSpace *as);
>   void address_space_unregister(AddressSpace *as);
> -void address_space_destroy_dispatch(AddressSpace *as);
> +void address_space_dispatch_free(AddressSpaceDispatch *d);
> +AddressSpaceDispatch *mem_begin(void);
> +void mem_commit(AddressSpaceDispatch *d);
> +void mem_add(AddressSpaceDispatch *d, MemoryRegionSection *section);
>   
>   extern const MemoryRegionOps unassigned_mem_ops;
>   
> diff --git a/include/exec/memory.h b/include/exec/memory.h
> index 83e82e90d5..41ab165302 100644
> --- a/include/exec/memory.h
> +++ b/include/exec/memory.h
> @@ -27,6 +27,7 @@
>   #include "qemu/rcu.h"
>   #include "hw/qdev-core.h"
>   
> +typedef struct AddressSpaceDispatch AddressSpaceDispatch;
>   #define RAM_ADDR_INVALID (~(ram_addr_t)0)
>   
>   #define MAX_PHYS_ADDR_SPACE_BITS 62
> @@ -312,6 +313,7 @@ struct MemoryListener {
>   };
>   
>   AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as);
> +MemoryRegion *address_space_root(AddressSpace *as);
>   
>   /**
>    * AddressSpace: describes a mapping of addresses to #MemoryRegion objects
> @@ -320,20 +322,17 @@ struct AddressSpace {
>       /* All fields are private. */
>       struct rcu_head rcu;
>       char *name;
> -    MemoryRegion *root;
> -    int ref_count;
> -    bool malloced;
>   
>       /* Accessed via RCU.  */
>       struct FlatView *current_map;
>   
>       int ioeventfd_nb;
>       struct MemoryRegionIoeventfd *ioeventfds;
> -    struct AddressSpaceDispatch *dispatch;
> -    struct AddressSpaceDispatch *next_dispatch;
> +
>       MemoryListener dispatch_listener;
>       QTAILQ_HEAD(memory_listeners_as, MemoryListener) listeners;
>       QTAILQ_ENTRY(AddressSpace) address_spaces_link;
> +    QTAILQ_ENTRY(AddressSpace) flat_view_link;
>   };
>   
>   /**
> diff --git a/exec.c b/exec.c
> index 66f01f5fce..51243f57f4 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -188,15 +188,12 @@ typedef struct PhysPageMap {
>   } PhysPageMap;
>   
>   struct AddressSpaceDispatch {
> -    struct rcu_head rcu;
> -
>       MemoryRegionSection *mru_section;
>       /* This is a multi-level map on the physical address space.
>        * The bottom level has pointers to MemoryRegionSections.
>        */
>       PhysPageEntry phys_map;
>       PhysPageMap map;
> -    AddressSpace *as;
>   };
>   
>   typedef struct AddressSpaceDispatch AddressSpaceDispatch;
> @@ -240,11 +237,6 @@ struct DirtyBitmapSnapshot {
>       unsigned long dirty[];
>   };
>   
> -AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
> -{
> -    return atomic_rcu_read(&as->dispatch);
> -}
> -
>   #endif
>   
>   #if !defined(CONFIG_USER_ONLY)
> @@ -1354,10 +1346,8 @@ static void register_multipage(AddressSpaceDispatch *d,
>       phys_page_set(d, start_addr >> TARGET_PAGE_BITS, num_pages, section_index);
>   }
>   
> -static void mem_add(MemoryListener *listener, MemoryRegionSection *section)
> +void mem_add(AddressSpaceDispatch *d, MemoryRegionSection *section)
>   {
> -    AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
> -    AddressSpaceDispatch *d = as->next_dispatch;
>       MemoryRegionSection now = *section, remain = *section;
>       Int128 page_size = int128_make64(TARGET_PAGE_SIZE);
>   
> @@ -2680,9 +2670,8 @@ static void io_mem_init(void)
>                             NULL, UINT64_MAX);
>   }
>   
> -static void mem_begin(MemoryListener *listener)
> +AddressSpaceDispatch *mem_begin(void)
>   {
> -    AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
>       AddressSpaceDispatch *d = g_new0(AddressSpaceDispatch, 1);
>       uint16_t n;
>   
> @@ -2696,27 +2685,19 @@ static void mem_begin(MemoryListener *listener)
>       assert(n == PHYS_SECTION_WATCH);
>   
>       d->phys_map  = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .skip = 1 };
> -    as->next_dispatch = d;
> +
> +    return d;
>   }
>   
> -static void address_space_dispatch_free(AddressSpaceDispatch *d)
> +void address_space_dispatch_free(AddressSpaceDispatch *d)
>   {
>       phys_sections_free(&d->map);
>       g_free(d);
>   }
>   
> -static void mem_commit(MemoryListener *listener)
> +void mem_commit(AddressSpaceDispatch *d)
>   {
> -    AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
> -    AddressSpaceDispatch *cur = as->dispatch;
> -    AddressSpaceDispatch *next = as->next_dispatch;
> -
> -    phys_page_compact_all(next, next->map.nodes_nb);
> -
> -    atomic_rcu_set(&as->dispatch, next);
> -    if (cur) {
> -        call_rcu(cur, address_space_dispatch_free, rcu);
> -    }
> +    phys_page_compact_all(d, d->map.nodes_nb);
>   }
>   
>   static void tcg_commit(MemoryListener *listener)
> @@ -2732,39 +2713,16 @@ static void tcg_commit(MemoryListener *listener)
>        * We reload the dispatch pointer now because cpu_reloading_memory_map()
>        * may have split the RCU critical section.
>        */
> -    d = atomic_rcu_read(&cpuas->as->dispatch);
> +    d = address_space_to_dispatch(cpuas->as);
>       atomic_rcu_set(&cpuas->memory_dispatch, d);
>       tlb_flush(cpuas->cpu);
>   }
>   
> -void address_space_init_dispatch(AddressSpace *as)
> -{
> -    as->dispatch = NULL;
> -    as->dispatch_listener = (MemoryListener) {
> -        .begin = mem_begin,
> -        .commit = mem_commit,
> -        .region_add = mem_add,
> -        .region_nop = mem_add,
> -        .priority = 0,
> -    };
> -    memory_listener_register(&as->dispatch_listener, as);
> -}
> -
>   void address_space_unregister(AddressSpace *as)
>   {
>       memory_listener_unregister(&as->dispatch_listener);
>   }
>   
> -void address_space_destroy_dispatch(AddressSpace *as)
> -{
> -    AddressSpaceDispatch *d = as->dispatch;
> -
> -    atomic_rcu_set(&as->dispatch, NULL);
> -    if (d) {
> -        call_rcu(d, address_space_dispatch_free, rcu);
> -    }
> -}
> -
>   static void memory_map_init(void)
>   {
>       system_memory = g_malloc(sizeof(*system_memory));
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index 258fbe51e2..86b9e419fd 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -88,7 +88,8 @@ static void pci_init_bus_master(PCIDevice *pci_dev)
>   
>       memory_region_init_alias(&pci_dev->bus_master_enable_region,
>                                OBJECT(pci_dev), "bus master",
> -                             dma_as->root, 0, memory_region_size(dma_as->root));
> +                             address_space_root(dma_as), 0,
> +                             memory_region_size(address_space_root(dma_as)));
>       memory_region_set_enabled(&pci_dev->bus_master_enable_region, false);
>       memory_region_add_subregion(&pci_dev->bus_master_container_region, 0,
>                                   &pci_dev->bus_master_enable_region);
> diff --git a/memory.c b/memory.c
> index c6904a7deb..385a507511 100644
> --- a/memory.c
> +++ b/memory.c
> @@ -47,6 +47,9 @@ static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners
>   static QTAILQ_HEAD(, AddressSpace) address_spaces
>       = QTAILQ_HEAD_INITIALIZER(address_spaces);
>   
> +static QTAILQ_HEAD(FlatViewList, FlatView) flat_views
> +    = QTAILQ_HEAD_INITIALIZER(flat_views);
> +
>   typedef struct AddrRange AddrRange;
>   
>   /*
> @@ -230,6 +233,11 @@ struct FlatView {
>       FlatRange *ranges;
>       unsigned nr;
>       unsigned nr_allocated;
> +    MemoryRegion *root;
> +    struct AddressSpaceDispatch *dispatch;
> +
> +    QTAILQ_ENTRY(FlatView) flat_views_link;
> +    QTAILQ_HEAD(address_spaces, AddressSpace) address_spaces;
>   };
>   
>   typedef struct AddressSpaceOps AddressSpaceOps;
> @@ -259,12 +267,19 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b)
>           && a->readonly == b->readonly;
>   }
>   
> -static void flatview_init(FlatView *view)
> +static void flatview_ref(FlatView *view);
> +
> +static FlatView *flatview_alloc(MemoryRegion *mr_root)
>   {
> +    FlatView *view;
> +
> +    view = g_new0(FlatView, 1);
>       view->ref = 1;
> -    view->ranges = NULL;
> -    view->nr = 0;
> -    view->nr_allocated = 0;
> +    view->root = mr_root;
> +    memory_region_ref(mr_root);
> +    QTAILQ_INIT(&view->address_spaces);
> +
> +    return view;
>   }
>   
>   /* Insert a range into a given position.  Caller is responsible for maintaining
> @@ -292,6 +307,10 @@ static void flatview_destroy(FlatView *view)
>           memory_region_unref(view->ranges[i].mr);
>       }
>       g_free(view->ranges);
> +    if (view->dispatch) {
> +        address_space_dispatch_free(view->dispatch);
> +    }
> +    memory_region_unref(view->root);
>       g_free(view);
>   }
>   
> @@ -303,7 +322,7 @@ static void flatview_ref(FlatView *view)
>   static void flatview_unref(FlatView *view)
>   {
>       if (atomic_fetch_dec(&view->ref) == 1) {
> -        flatview_destroy(view);
> +        call_rcu(view, flatview_destroy, rcu);
>       }
>   }
>   
> @@ -608,7 +627,7 @@ static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
>           mr = mr->container;
>       }
>       QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
> -        if (mr == as->root) {
> +        if (mr == as->current_map->root) {
>               return as;
>           }
>       }
> @@ -702,23 +721,6 @@ 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)
> -{
> -    FlatView *view;
> -
> -    view = g_new(FlatView, 1);
> -    flatview_init(view);
> -
> -    if (mr) {
> -        render_memory_region(view, mr, int128_zero(),
> -                             addrrange_make(int128_zero(), int128_2_64()), false);
> -    }
> -    flatview_simplify(view);
> -
> -    return view;
> -}
> -
>   static void address_space_add_del_ioeventfds(AddressSpace *as,
>                                                MemoryRegionIoeventfd *fds_new,
>                                                unsigned fds_new_nb,
> @@ -769,12 +771,10 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
>       }
>   }
>   
> -static FlatView *address_space_get_flatview(AddressSpace *as)
> +static FlatView *flatview_get(FlatView *view)
>   {
> -    FlatView *view;
> -
>       rcu_read_lock();
> -    view = atomic_rcu_read(&as->current_map);
> +    view = atomic_rcu_read(&view);
>       flatview_ref(view);
>       rcu_read_unlock();
>       return view;
> @@ -789,7 +789,7 @@ static void address_space_update_ioeventfds(AddressSpace *as)
>       AddrRange tmp;
>       unsigned i;
>   
> -    view = address_space_get_flatview(as);
> +    view = flatview_get(as->current_map);
>       FOR_EACH_FLAT_RANGE(fr, view) {
>           for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
>               tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
> @@ -881,28 +881,89 @@ static void address_space_update_topology_pass(AddressSpace *as,
>       }
>   }
>   
> -
> -static void address_space_update_topology(AddressSpace *as)
> +static FlatView *address_space_update_flatview(FlatView *view)
>   {
> -    FlatView *old_view = address_space_get_flatview(as);
> -    FlatView *new_view = generate_memory_topology(as->root);
> +    AddressSpace *as, *asnext;
> +    FlatView *old_view = flatview_get(view);
> +    MemoryRegion *root = old_view->root;
> +    FlatView *new_view = flatview_alloc(root);
> +    unsigned iold, inew;
> +    FlatRange *frold, *frnew;
>   
> -    address_space_update_topology_pass(as, old_view, new_view, false);
> -    address_space_update_topology_pass(as, old_view, new_view, true);
> +    if (root) {
> +        render_memory_region(new_view, root, int128_zero(),
> +                             addrrange_make(int128_zero(), int128_2_64()),
> +                             false);
> +        flatview_simplify(new_view);
> +    }
>   
> -    /* Writes are protected by the BQL.  */
> -    atomic_rcu_set(&as->current_map, new_view);
> -    call_rcu(old_view, flatview_unref, rcu);
> +    new_view->dispatch = mem_begin();
>   
> -    /* 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.
> +    /*
> +     * FIXME: this is cut-n-paste from address_space_update_topology_pass,
> +     * simplify it
>        */
> +    iold = inew = 0;
> +    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];
> +        } else {
> +            frnew = NULL;
> +        }
> +
> +        if (frold
> +            && (!frnew
> +                || int128_lt(frold->addr.start, frnew->addr.start)
> +                || (int128_eq(frold->addr.start, frnew->addr.start)
> +                    && !flatrange_equal(frold, frnew)))) {
> +            ++iold;
> +        } else if (frold && frnew && flatrange_equal(frold, frnew)) {
> +            /* In both and unchanged (except logging may have changed) */
> +            MemoryRegionSection mrs = section_from_flat_range(frnew,
> +                    new_view->dispatch);
> +
> +            mem_add(new_view->dispatch, &mrs);
> +
> +            ++iold;
> +            ++inew;
> +        } else {
> +            /* In new */
> +            MemoryRegionSection mrs = section_from_flat_range(frnew,
> +                    new_view->dispatch);
> +
> +            mem_add(new_view->dispatch, &mrs);
> +
> +            ++inew;
> +        }
> +    }
> +
> +    mem_commit(new_view->dispatch);
> +
> +    QTAILQ_FOREACH(as, &old_view->address_spaces, flat_view_link) {
> +        address_space_update_topology_pass(as, old_view, new_view, false);
> +        address_space_update_topology_pass(as, old_view, new_view, true);
> +    }
> +
> +    QTAILQ_FOREACH_SAFE(as, &old_view->address_spaces, flat_view_link, asnext) {
> +        QTAILQ_REMOVE(&old_view->address_spaces, as, flat_view_link);
> +        flatview_unref(old_view);
> +
> +        atomic_rcu_set(&as->current_map, new_view);
> +
> +        flatview_ref(new_view);
> +        QTAILQ_INSERT_TAIL(&new_view->address_spaces, as, flat_view_link);
> +
> +        address_space_update_ioeventfds(as);
> +    }
> +
>       flatview_unref(old_view);
>   
> -    address_space_update_ioeventfds(as);
> +    return new_view;
>   }
>   
>   void memory_region_transaction_begin(void)
> @@ -921,11 +982,31 @@ void memory_region_transaction_commit(void)
>       --memory_region_transaction_depth;
>       if (!memory_region_transaction_depth) {
>           if (memory_region_update_pending) {
> +            FlatView *view, *vnext;
> +            struct FlatViewList fwstmp = QTAILQ_HEAD_INITIALIZER(fwstmp);
> +
>               MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
>   
> -            QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
> -                address_space_update_topology(as);
> +            QTAILQ_FOREACH_SAFE(view, &flat_views, flat_views_link, vnext) {
> +                FlatView *old_view, *new_view;
> +
> +                old_view = flatview_get(view);
> +                new_view = address_space_update_flatview(old_view);
> +
> +                QTAILQ_REMOVE(&flat_views, old_view, flat_views_link);
> +                flatview_unref(old_view);
> +                flatview_unref(old_view);
> +
> +                QTAILQ_INSERT_HEAD(&fwstmp, new_view, flat_views_link);
> +
> +                flatview_unref(new_view);
>               }
> +
> +            QTAILQ_FOREACH_SAFE(view, &fwstmp, flat_views_link, vnext) {
> +                QTAILQ_REMOVE(&fwstmp, view, flat_views_link);
> +                QTAILQ_INSERT_HEAD(&flat_views, view, flat_views_link);
> +            }
> +
>               memory_region_update_pending = false;
>               MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
>           } else if (ioeventfd_update_pending) {
> @@ -1834,7 +1915,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
>               continue;
>           }
>           as = listener->address_space;
> -        view = address_space_get_flatview(as);
> +        view = flatview_get(as->current_map);
>           FOR_EACH_FLAT_RANGE(fr, view) {
>               if (fr->mr == mr) {
>                   MemoryRegionSection mrs = section_from_flat_range(fr,
> @@ -1938,7 +2019,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
>       AddrRange tmp;
>       MemoryRegionSection section;
>   
> -    view = address_space_get_flatview(as);
> +    view = flatview_get(as->current_map);
>       FOR_EACH_FLAT_RANGE(fr, view) {
>           if (fr->mr == mr) {
>               section = (MemoryRegionSection) {
> @@ -2350,7 +2431,7 @@ void memory_global_dirty_log_sync(void)
>               continue;
>           }
>           as = listener->address_space;
> -        view = address_space_get_flatview(as);
> +        view = flatview_get(as->current_map);
>           FOR_EACH_FLAT_RANGE(fr, view) {
>               if (fr->dirty_log_mask) {
>                   MemoryRegionSection mrs = section_from_flat_range(fr,
> @@ -2436,7 +2517,7 @@ static void listener_add_address_space(MemoryListener *listener,
>           }
>       }
>   
> -    view = address_space_get_flatview(as);
> +    view = flatview_get(as->current_map);
>       FOR_EACH_FLAT_RANGE(fr, view) {
>           MemoryRegionSection section = {
>               .mr = fr->mr,
> @@ -2615,67 +2696,72 @@ void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset,
>   
>   void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
>   {
> -    memory_region_ref(root);
> -    memory_region_transaction_begin();
> -    as->ref_count = 1;
> -    as->root = root;
> -    as->malloced = false;
> -    as->current_map = g_new(FlatView, 1);
> -    flatview_init(as->current_map);
> +    FlatView *view;
> +
> +    as->current_map = NULL;
>       as->ioeventfd_nb = 0;
>       as->ioeventfds = NULL;
>       QTAILQ_INIT(&as->listeners);
>       QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
>       as->name = g_strdup(name ? name : "anonymous");
> -    address_space_init_dispatch(as);
> -    memory_region_update_pending |= root->enabled;
> -    memory_region_transaction_commit();
> +
> +    QTAILQ_FOREACH(view, &flat_views, flat_views_link) {
> +        assert(root);
> +        if (view->root == root) {
> +            as->current_map = flatview_get(view);
> +            break;
> +        }
> +    }
> +
> +    if (!as->current_map) {
> +        as->current_map = flatview_alloc(root);
> +
> +        QTAILQ_INSERT_TAIL(&flat_views, as->current_map, flat_views_link);
> +    }
> +
> +    QTAILQ_INSERT_TAIL(&as->current_map->address_spaces, as, flat_view_link);
> +}
> +
> +MemoryRegion *address_space_root(AddressSpace *as)
> +{
> +    return as->current_map->root;
> +}
> +
> +AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
> +{
> +    return atomic_rcu_read(&as->current_map)->dispatch;
>   }
>   
>   static void do_address_space_destroy(AddressSpace *as)
>   {
> -    bool do_free = as->malloced;
> +    FlatView *view = flatview_get(as->current_map);
>   
> -    address_space_destroy_dispatch(as);
>       assert(QTAILQ_EMPTY(&as->listeners));
>   
> -    flatview_unref(as->current_map);
> +    QTAILQ_REMOVE(&view->address_spaces, as, flat_view_link);
> +    flatview_unref(view);
> +
> +    flatview_unref(view);

incorrect copy/paste?

> +
>       g_free(as->name);
>       g_free(as->ioeventfds);
> -    memory_region_unref(as->root);
> -    if (do_free) {
> -        g_free(as);
> -    }
> +    g_free(as);
>   }
>   
>   AddressSpace *address_space_init_shareable(MemoryRegion *root, const char *name)
>   {
>       AddressSpace *as;
>   
> -    QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
> -        if (root == as->root && as->malloced) {
> -            as->ref_count++;
> -            return as;
> -        }
> -    }
> -
>       as = g_malloc0(sizeof *as);
>       address_space_init(as, root, name);
> -    as->malloced = true;
> +
>       return as;
>   }
>   
>   void address_space_destroy(AddressSpace *as)
>   {
> -    MemoryRegion *root = as->root;
> -
> -    as->ref_count--;
> -    if (as->ref_count) {
> -        return;
> -    }
>       /* 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);
> @@ -2684,7 +2770,6 @@ void address_space_destroy(AddressSpace *as)
>        * 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);
>   }
>   
> @@ -2816,7 +2901,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
>   static void mtree_print_flatview(fprintf_function p, void *f,
>                                    AddressSpace *as)
>   {
> -    FlatView *view = address_space_get_flatview(as);
> +    FlatView *view = flatview_get(as->current_map);
>       FlatRange *range = &view->ranges[0];
>       MemoryRegion *mr;
>       int n = view->nr;
> @@ -2873,7 +2958,7 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview)
>   
>       QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
>           mon_printf(f, "address-space: %s\n", as->name);
> -        mtree_print_mr(mon_printf, f, as->root, 1, 0, &ml_head);
> +        mtree_print_mr(mon_printf, f, as->current_map->root, 1, 0, &ml_head);
>           mon_printf(f, "\n");
>       }
>   
>
Alexey Kardashevskiy Sept. 7, 2017, 10:18 p.m. UTC | #2
On 08/09/17 06:53, Philippe Mathieu-Daudé wrote:
> On 09/07/2017 06:20 AM, Alexey Kardashevskiy wrote:
>> This allows sharing flat views between address spaces when the same root
>> memory region is used when creating a new address space.
>>
>> This adds a global list of flat views and a list of attached address
>> spaces per a flat view. Each address space references a flat view.
>>
>> This hard codes the dispatch tree building instead of doing so via
>> a memory listener.
>>
>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>> ---
>>
>> This was suggested by Paolo in
>> https://lists.gnu.org/archive/html/qemu-devel/2017-08/msg05011.html
>>
>> I am not putting "Suggested-by" as I want to make sure that this is doing
>> what was actually suggested.
>> ---
>>   include/exec/memory-internal.h |   6 +-
>>   include/exec/memory.h          |   9 +-
>>   exec.c                         |  58 ++--------
>>   hw/pci/pci.c                   |   3 +-
>>   memory.c                       | 253
>> +++++++++++++++++++++++++++--------------
>>   5 files changed, 187 insertions(+), 142 deletions(-)
>>
>> diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h
>> index fb467acdba..8516e0b48f 100644
>> --- a/include/exec/memory-internal.h
>> +++ b/include/exec/memory-internal.h
>> @@ -22,9 +22,11 @@
>>   #ifndef CONFIG_USER_ONLY
>>   typedef struct AddressSpaceDispatch AddressSpaceDispatch;
>>   -void address_space_init_dispatch(AddressSpace *as);
>>   void address_space_unregister(AddressSpace *as);
>> -void address_space_destroy_dispatch(AddressSpace *as);
>> +void address_space_dispatch_free(AddressSpaceDispatch *d);
>> +AddressSpaceDispatch *mem_begin(void);
>> +void mem_commit(AddressSpaceDispatch *d);
>> +void mem_add(AddressSpaceDispatch *d, MemoryRegionSection *section);
>>     extern const MemoryRegionOps unassigned_mem_ops;
>>   diff --git a/include/exec/memory.h b/include/exec/memory.h
>> index 83e82e90d5..41ab165302 100644
>> --- a/include/exec/memory.h
>> +++ b/include/exec/memory.h
>> @@ -27,6 +27,7 @@
>>   #include "qemu/rcu.h"
>>   #include "hw/qdev-core.h"
>>   +typedef struct AddressSpaceDispatch AddressSpaceDispatch;
>>   #define RAM_ADDR_INVALID (~(ram_addr_t)0)
>>     #define MAX_PHYS_ADDR_SPACE_BITS 62
>> @@ -312,6 +313,7 @@ struct MemoryListener {
>>   };
>>     AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as);
>> +MemoryRegion *address_space_root(AddressSpace *as);
>>     /**
>>    * AddressSpace: describes a mapping of addresses to #MemoryRegion objects
>> @@ -320,20 +322,17 @@ struct AddressSpace {
>>       /* All fields are private. */
>>       struct rcu_head rcu;
>>       char *name;
>> -    MemoryRegion *root;
>> -    int ref_count;
>> -    bool malloced;
>>         /* Accessed via RCU.  */
>>       struct FlatView *current_map;
>>         int ioeventfd_nb;
>>       struct MemoryRegionIoeventfd *ioeventfds;
>> -    struct AddressSpaceDispatch *dispatch;
>> -    struct AddressSpaceDispatch *next_dispatch;
>> +
>>       MemoryListener dispatch_listener;
>>       QTAILQ_HEAD(memory_listeners_as, MemoryListener) listeners;
>>       QTAILQ_ENTRY(AddressSpace) address_spaces_link;
>> +    QTAILQ_ENTRY(AddressSpace) flat_view_link;
>>   };
>>     /**
>> diff --git a/exec.c b/exec.c
>> index 66f01f5fce..51243f57f4 100644
>> --- a/exec.c
>> +++ b/exec.c
>> @@ -188,15 +188,12 @@ typedef struct PhysPageMap {
>>   } PhysPageMap;
>>     struct AddressSpaceDispatch {
>> -    struct rcu_head rcu;
>> -
>>       MemoryRegionSection *mru_section;
>>       /* This is a multi-level map on the physical address space.
>>        * The bottom level has pointers to MemoryRegionSections.
>>        */
>>       PhysPageEntry phys_map;
>>       PhysPageMap map;
>> -    AddressSpace *as;
>>   };
>>     typedef struct AddressSpaceDispatch AddressSpaceDispatch;
>> @@ -240,11 +237,6 @@ struct DirtyBitmapSnapshot {
>>       unsigned long dirty[];
>>   };
>>   -AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
>> -{
>> -    return atomic_rcu_read(&as->dispatch);
>> -}
>> -
>>   #endif
>>     #if !defined(CONFIG_USER_ONLY)
>> @@ -1354,10 +1346,8 @@ static void
>> register_multipage(AddressSpaceDispatch *d,
>>       phys_page_set(d, start_addr >> TARGET_PAGE_BITS, num_pages,
>> section_index);
>>   }
>>   -static void mem_add(MemoryListener *listener, MemoryRegionSection
>> *section)
>> +void mem_add(AddressSpaceDispatch *d, MemoryRegionSection *section)
>>   {
>> -    AddressSpace *as = container_of(listener, AddressSpace,
>> dispatch_listener);
>> -    AddressSpaceDispatch *d = as->next_dispatch;
>>       MemoryRegionSection now = *section, remain = *section;
>>       Int128 page_size = int128_make64(TARGET_PAGE_SIZE);
>>   @@ -2680,9 +2670,8 @@ static void io_mem_init(void)
>>                             NULL, UINT64_MAX);
>>   }
>>   -static void mem_begin(MemoryListener *listener)
>> +AddressSpaceDispatch *mem_begin(void)
>>   {
>> -    AddressSpace *as = container_of(listener, AddressSpace,
>> dispatch_listener);
>>       AddressSpaceDispatch *d = g_new0(AddressSpaceDispatch, 1);
>>       uint16_t n;
>>   @@ -2696,27 +2685,19 @@ static void mem_begin(MemoryListener *listener)
>>       assert(n == PHYS_SECTION_WATCH);
>>         d->phys_map  = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .skip
>> = 1 };
>> -    as->next_dispatch = d;
>> +
>> +    return d;
>>   }
>>   -static void address_space_dispatch_free(AddressSpaceDispatch *d)
>> +void address_space_dispatch_free(AddressSpaceDispatch *d)
>>   {
>>       phys_sections_free(&d->map);
>>       g_free(d);
>>   }
>>   -static void mem_commit(MemoryListener *listener)
>> +void mem_commit(AddressSpaceDispatch *d)
>>   {
>> -    AddressSpace *as = container_of(listener, AddressSpace,
>> dispatch_listener);
>> -    AddressSpaceDispatch *cur = as->dispatch;
>> -    AddressSpaceDispatch *next = as->next_dispatch;
>> -
>> -    phys_page_compact_all(next, next->map.nodes_nb);
>> -
>> -    atomic_rcu_set(&as->dispatch, next);
>> -    if (cur) {
>> -        call_rcu(cur, address_space_dispatch_free, rcu);
>> -    }
>> +    phys_page_compact_all(d, d->map.nodes_nb);
>>   }
>>     static void tcg_commit(MemoryListener *listener)
>> @@ -2732,39 +2713,16 @@ static void tcg_commit(MemoryListener *listener)
>>        * We reload the dispatch pointer now because
>> cpu_reloading_memory_map()
>>        * may have split the RCU critical section.
>>        */
>> -    d = atomic_rcu_read(&cpuas->as->dispatch);
>> +    d = address_space_to_dispatch(cpuas->as);
>>       atomic_rcu_set(&cpuas->memory_dispatch, d);
>>       tlb_flush(cpuas->cpu);
>>   }
>>   -void address_space_init_dispatch(AddressSpace *as)
>> -{
>> -    as->dispatch = NULL;
>> -    as->dispatch_listener = (MemoryListener) {
>> -        .begin = mem_begin,
>> -        .commit = mem_commit,
>> -        .region_add = mem_add,
>> -        .region_nop = mem_add,
>> -        .priority = 0,
>> -    };
>> -    memory_listener_register(&as->dispatch_listener, as);
>> -}
>> -
>>   void address_space_unregister(AddressSpace *as)
>>   {
>>       memory_listener_unregister(&as->dispatch_listener);
>>   }
>>   -void address_space_destroy_dispatch(AddressSpace *as)
>> -{
>> -    AddressSpaceDispatch *d = as->dispatch;
>> -
>> -    atomic_rcu_set(&as->dispatch, NULL);
>> -    if (d) {
>> -        call_rcu(d, address_space_dispatch_free, rcu);
>> -    }
>> -}
>> -
>>   static void memory_map_init(void)
>>   {
>>       system_memory = g_malloc(sizeof(*system_memory));
>> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
>> index 258fbe51e2..86b9e419fd 100644
>> --- a/hw/pci/pci.c
>> +++ b/hw/pci/pci.c
>> @@ -88,7 +88,8 @@ static void pci_init_bus_master(PCIDevice *pci_dev)
>>         memory_region_init_alias(&pci_dev->bus_master_enable_region,
>>                                OBJECT(pci_dev), "bus master",
>> -                             dma_as->root, 0,
>> memory_region_size(dma_as->root));
>> +                             address_space_root(dma_as), 0,
>> +                            
>> memory_region_size(address_space_root(dma_as)));
>>       memory_region_set_enabled(&pci_dev->bus_master_enable_region, false);
>>       memory_region_add_subregion(&pci_dev->bus_master_container_region, 0,
>>                                   &pci_dev->bus_master_enable_region);
>> diff --git a/memory.c b/memory.c
>> index c6904a7deb..385a507511 100644
>> --- a/memory.c
>> +++ b/memory.c
>> @@ -47,6 +47,9 @@ static QTAILQ_HEAD(memory_listeners, MemoryListener)
>> memory_listeners
>>   static QTAILQ_HEAD(, AddressSpace) address_spaces
>>       = QTAILQ_HEAD_INITIALIZER(address_spaces);
>>   +static QTAILQ_HEAD(FlatViewList, FlatView) flat_views
>> +    = QTAILQ_HEAD_INITIALIZER(flat_views);
>> +
>>   typedef struct AddrRange AddrRange;
>>     /*
>> @@ -230,6 +233,11 @@ struct FlatView {
>>       FlatRange *ranges;
>>       unsigned nr;
>>       unsigned nr_allocated;
>> +    MemoryRegion *root;
>> +    struct AddressSpaceDispatch *dispatch;
>> +
>> +    QTAILQ_ENTRY(FlatView) flat_views_link;
>> +    QTAILQ_HEAD(address_spaces, AddressSpace) address_spaces;
>>   };
>>     typedef struct AddressSpaceOps AddressSpaceOps;
>> @@ -259,12 +267,19 @@ static bool flatrange_equal(FlatRange *a, FlatRange
>> *b)
>>           && a->readonly == b->readonly;
>>   }
>>   -static void flatview_init(FlatView *view)
>> +static void flatview_ref(FlatView *view);
>> +
>> +static FlatView *flatview_alloc(MemoryRegion *mr_root)
>>   {
>> +    FlatView *view;
>> +
>> +    view = g_new0(FlatView, 1);
>>       view->ref = 1;
>> -    view->ranges = NULL;
>> -    view->nr = 0;
>> -    view->nr_allocated = 0;
>> +    view->root = mr_root;
>> +    memory_region_ref(mr_root);
>> +    QTAILQ_INIT(&view->address_spaces);
>> +
>> +    return view;
>>   }
>>     /* Insert a range into a given position.  Caller is responsible for
>> maintaining
>> @@ -292,6 +307,10 @@ static void flatview_destroy(FlatView *view)
>>           memory_region_unref(view->ranges[i].mr);
>>       }
>>       g_free(view->ranges);
>> +    if (view->dispatch) {
>> +        address_space_dispatch_free(view->dispatch);
>> +    }
>> +    memory_region_unref(view->root);
>>       g_free(view);
>>   }
>>   @@ -303,7 +322,7 @@ static void flatview_ref(FlatView *view)
>>   static void flatview_unref(FlatView *view)
>>   {
>>       if (atomic_fetch_dec(&view->ref) == 1) {
>> -        flatview_destroy(view);
>> +        call_rcu(view, flatview_destroy, rcu);
>>       }
>>   }
>>   @@ -608,7 +627,7 @@ static AddressSpace
>> *memory_region_to_address_space(MemoryRegion *mr)
>>           mr = mr->container;
>>       }
>>       QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
>> -        if (mr == as->root) {
>> +        if (mr == as->current_map->root) {
>>               return as;
>>           }
>>       }
>> @@ -702,23 +721,6 @@ 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)
>> -{
>> -    FlatView *view;
>> -
>> -    view = g_new(FlatView, 1);
>> -    flatview_init(view);
>> -
>> -    if (mr) {
>> -        render_memory_region(view, mr, int128_zero(),
>> -                             addrrange_make(int128_zero(),
>> int128_2_64()), false);
>> -    }
>> -    flatview_simplify(view);
>> -
>> -    return view;
>> -}
>> -
>>   static void address_space_add_del_ioeventfds(AddressSpace *as,
>>                                                MemoryRegionIoeventfd
>> *fds_new,
>>                                                unsigned fds_new_nb,
>> @@ -769,12 +771,10 @@ static void
>> address_space_add_del_ioeventfds(AddressSpace *as,
>>       }
>>   }
>>   -static FlatView *address_space_get_flatview(AddressSpace *as)
>> +static FlatView *flatview_get(FlatView *view)
>>   {
>> -    FlatView *view;
>> -
>>       rcu_read_lock();
>> -    view = atomic_rcu_read(&as->current_map);
>> +    view = atomic_rcu_read(&view);
>>       flatview_ref(view);
>>       rcu_read_unlock();
>>       return view;
>> @@ -789,7 +789,7 @@ static void
>> address_space_update_ioeventfds(AddressSpace *as)
>>       AddrRange tmp;
>>       unsigned i;
>>   -    view = address_space_get_flatview(as);
>> +    view = flatview_get(as->current_map);
>>       FOR_EACH_FLAT_RANGE(fr, view) {
>>           for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
>>               tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
>> @@ -881,28 +881,89 @@ static void
>> address_space_update_topology_pass(AddressSpace *as,
>>       }
>>   }
>>   -
>> -static void address_space_update_topology(AddressSpace *as)
>> +static FlatView *address_space_update_flatview(FlatView *view)
>>   {
>> -    FlatView *old_view = address_space_get_flatview(as);
>> -    FlatView *new_view = generate_memory_topology(as->root);
>> +    AddressSpace *as, *asnext;
>> +    FlatView *old_view = flatview_get(view);
>> +    MemoryRegion *root = old_view->root;
>> +    FlatView *new_view = flatview_alloc(root);
>> +    unsigned iold, inew;
>> +    FlatRange *frold, *frnew;
>>   -    address_space_update_topology_pass(as, old_view, new_view, false);
>> -    address_space_update_topology_pass(as, old_view, new_view, true);
>> +    if (root) {
>> +        render_memory_region(new_view, root, int128_zero(),
>> +                             addrrange_make(int128_zero(), int128_2_64()),
>> +                             false);
>> +        flatview_simplify(new_view);
>> +    }
>>   -    /* Writes are protected by the BQL.  */
>> -    atomic_rcu_set(&as->current_map, new_view);
>> -    call_rcu(old_view, flatview_unref, rcu);
>> +    new_view->dispatch = mem_begin();
>>   -    /* 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.
>> +    /*
>> +     * FIXME: this is cut-n-paste from address_space_update_topology_pass,
>> +     * simplify it
>>        */
>> +    iold = inew = 0;
>> +    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];
>> +        } else {
>> +            frnew = NULL;
>> +        }
>> +
>> +        if (frold
>> +            && (!frnew
>> +                || int128_lt(frold->addr.start, frnew->addr.start)
>> +                || (int128_eq(frold->addr.start, frnew->addr.start)
>> +                    && !flatrange_equal(frold, frnew)))) {
>> +            ++iold;
>> +        } else if (frold && frnew && flatrange_equal(frold, frnew)) {
>> +            /* In both and unchanged (except logging may have changed) */
>> +            MemoryRegionSection mrs = section_from_flat_range(frnew,
>> +                    new_view->dispatch);
>> +
>> +            mem_add(new_view->dispatch, &mrs);
>> +
>> +            ++iold;
>> +            ++inew;
>> +        } else {
>> +            /* In new */
>> +            MemoryRegionSection mrs = section_from_flat_range(frnew,
>> +                    new_view->dispatch);
>> +
>> +            mem_add(new_view->dispatch, &mrs);
>> +
>> +            ++inew;
>> +        }
>> +    }
>> +
>> +    mem_commit(new_view->dispatch);
>> +
>> +    QTAILQ_FOREACH(as, &old_view->address_spaces, flat_view_link) {
>> +        address_space_update_topology_pass(as, old_view, new_view, false);
>> +        address_space_update_topology_pass(as, old_view, new_view, true);
>> +    }
>> +
>> +    QTAILQ_FOREACH_SAFE(as, &old_view->address_spaces, flat_view_link,
>> asnext) {
>> +        QTAILQ_REMOVE(&old_view->address_spaces, as, flat_view_link);
>> +        flatview_unref(old_view);
>> +
>> +        atomic_rcu_set(&as->current_map, new_view);
>> +
>> +        flatview_ref(new_view);
>> +        QTAILQ_INSERT_TAIL(&new_view->address_spaces, as, flat_view_link);
>> +
>> +        address_space_update_ioeventfds(as);
>> +    }
>> +
>>       flatview_unref(old_view);
>>   -    address_space_update_ioeventfds(as);
>> +    return new_view;
>>   }
>>     void memory_region_transaction_begin(void)
>> @@ -921,11 +982,31 @@ void memory_region_transaction_commit(void)
>>       --memory_region_transaction_depth;
>>       if (!memory_region_transaction_depth) {
>>           if (memory_region_update_pending) {
>> +            FlatView *view, *vnext;
>> +            struct FlatViewList fwstmp = QTAILQ_HEAD_INITIALIZER(fwstmp);
>> +
>>               MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
>>   -            QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
>> -                address_space_update_topology(as);
>> +            QTAILQ_FOREACH_SAFE(view, &flat_views, flat_views_link,
>> vnext) {
>> +                FlatView *old_view, *new_view;
>> +
>> +                old_view = flatview_get(view);
>> +                new_view = address_space_update_flatview(old_view);
>> +
>> +                QTAILQ_REMOVE(&flat_views, old_view, flat_views_link);
>> +                flatview_unref(old_view);
>> +                flatview_unref(old_view);
>> +
>> +                QTAILQ_INSERT_HEAD(&fwstmp, new_view, flat_views_link);
>> +
>> +                flatview_unref(new_view);
>>               }
>> +
>> +            QTAILQ_FOREACH_SAFE(view, &fwstmp, flat_views_link, vnext) {
>> +                QTAILQ_REMOVE(&fwstmp, view, flat_views_link);
>> +                QTAILQ_INSERT_HEAD(&flat_views, view, flat_views_link);
>> +            }
>> +
>>               memory_region_update_pending = false;
>>               MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
>>           } else if (ioeventfd_update_pending) {
>> @@ -1834,7 +1915,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
>>               continue;
>>           }
>>           as = listener->address_space;
>> -        view = address_space_get_flatview(as);
>> +        view = flatview_get(as->current_map);
>>           FOR_EACH_FLAT_RANGE(fr, view) {
>>               if (fr->mr == mr) {
>>                   MemoryRegionSection mrs = section_from_flat_range(fr,
>> @@ -1938,7 +2019,7 @@ static void
>> memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
>>       AddrRange tmp;
>>       MemoryRegionSection section;
>>   -    view = address_space_get_flatview(as);
>> +    view = flatview_get(as->current_map);
>>       FOR_EACH_FLAT_RANGE(fr, view) {
>>           if (fr->mr == mr) {
>>               section = (MemoryRegionSection) {
>> @@ -2350,7 +2431,7 @@ void memory_global_dirty_log_sync(void)
>>               continue;
>>           }
>>           as = listener->address_space;
>> -        view = address_space_get_flatview(as);
>> +        view = flatview_get(as->current_map);
>>           FOR_EACH_FLAT_RANGE(fr, view) {
>>               if (fr->dirty_log_mask) {
>>                   MemoryRegionSection mrs = section_from_flat_range(fr,
>> @@ -2436,7 +2517,7 @@ static void
>> listener_add_address_space(MemoryListener *listener,
>>           }
>>       }
>>   -    view = address_space_get_flatview(as);
>> +    view = flatview_get(as->current_map);
>>       FOR_EACH_FLAT_RANGE(fr, view) {
>>           MemoryRegionSection section = {
>>               .mr = fr->mr,
>> @@ -2615,67 +2696,72 @@ void
>> memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset,
>>     void address_space_init(AddressSpace *as, MemoryRegion *root, const
>> char *name)
>>   {
>> -    memory_region_ref(root);
>> -    memory_region_transaction_begin();
>> -    as->ref_count = 1;
>> -    as->root = root;
>> -    as->malloced = false;
>> -    as->current_map = g_new(FlatView, 1);
>> -    flatview_init(as->current_map);
>> +    FlatView *view;
>> +
>> +    as->current_map = NULL;
>>       as->ioeventfd_nb = 0;
>>       as->ioeventfds = NULL;
>>       QTAILQ_INIT(&as->listeners);
>>       QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
>>       as->name = g_strdup(name ? name : "anonymous");
>> -    address_space_init_dispatch(as);
>> -    memory_region_update_pending |= root->enabled;
>> -    memory_region_transaction_commit();
>> +
>> +    QTAILQ_FOREACH(view, &flat_views, flat_views_link) {
>> +        assert(root);
>> +        if (view->root == root) {
>> +            as->current_map = flatview_get(view);
>> +            break;
>> +        }
>> +    }
>> +
>> +    if (!as->current_map) {
>> +        as->current_map = flatview_alloc(root);
>> +
>> +        QTAILQ_INSERT_TAIL(&flat_views, as->current_map, flat_views_link);
>> +    }
>> +
>> +    QTAILQ_INSERT_TAIL(&as->current_map->address_spaces, as,
>> flat_view_link);
>> +}
>> +
>> +MemoryRegion *address_space_root(AddressSpace *as)
>> +{
>> +    return as->current_map->root;
>> +}
>> +
>> +AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
>> +{
>> +    return atomic_rcu_read(&as->current_map)->dispatch;
>>   }
>>     static void do_address_space_destroy(AddressSpace *as)
>>   {
>> -    bool do_free = as->malloced;
>> +    FlatView *view = flatview_get(as->current_map);
>>   -    address_space_destroy_dispatch(as);
>>       assert(QTAILQ_EMPTY(&as->listeners));
>>   -    flatview_unref(as->current_map);
>> +    QTAILQ_REMOVE(&view->address_spaces, as, flat_view_link);
>> +    flatview_unref(view);
>> +
>> +    flatview_unref(view);


This one removes a reference after unlinking an address space from the
flatview.

> 
> incorrect copy/paste?

Nope, this one pairs flatview_get() from few lines above.
Paolo Bonzini Sept. 11, 2017, 7:40 a.m. UTC | #3
On 07/09/2017 11:20, Alexey Kardashevskiy wrote:
>  
>      /* Accessed via RCU.  */
>      struct FlatView *current_map;
>  
>      int ioeventfd_nb;
>      struct MemoryRegionIoeventfd *ioeventfds;
> -    struct AddressSpaceDispatch *dispatch;
> -    struct AddressSpaceDispatch *next_dispatch;
> +

The rough idea of the patch matches my suggestion indeed.  However, I am
not sure why all of the changes in patch 2 are needed.

Once you have built the FlatView and the dispatch within it, you can
still cache its dispatch tree in as->dispatch, and free it with RCU from
flatview_destroy.  This removes the need to use call_rcu from
flatview_unref.

In addition, you could change the computation of FlatView's root to
resolve 2^64-sized aliases; also set it to NULL if the AddressSpace's
root is disabled or the alias it resolves to is disabled (and so on
recursively until a non-alias is found).  This should remove the need
for address_space_root() and the change to pci_init_bus_master.

Paolo
Alexey Kardashevskiy Sept. 11, 2017, 9:06 a.m. UTC | #4
On 11/09/17 17:40, Paolo Bonzini wrote:
> On 07/09/2017 11:20, Alexey Kardashevskiy wrote:
>>  
>>      /* Accessed via RCU.  */
>>      struct FlatView *current_map;
>>  
>>      int ioeventfd_nb;
>>      struct MemoryRegionIoeventfd *ioeventfds;
>> -    struct AddressSpaceDispatch *dispatch;
>> -    struct AddressSpaceDispatch *next_dispatch;
>> +
> 
> The rough idea of the patch matches my suggestion indeed.  However, I am
> not sure why all of the changes in patch 2 are needed.

For this:

 struct MemoryRegionSection {
     MemoryRegion *mr;
-    AddressSpace *address_space;
+    AddressSpaceDispatch *dispatch;

as there are many ASes attached to the same flatview/dispatch.

And because of that, there is also:

 struct IOMMUTLBEntry {
-    AddressSpace    *target_as;
+    AddressSpaceDispatch *target_dispatch;

as the "section" in address_space_get_iotlb_entry() does not have
address_space any more, even though the only user of it -
vhost_device_iotlb_miss() - only checks if (iotlb.target_dispatch != NULL).


> Once you have built the FlatView and the dispatch within it, you can
> still cache its dispatch tree in as->dispatch, and free it with RCU from
> flatview_destroy.  This removes the need to use call_rcu from
> flatview_unref.

Ok, I will do that.

> In addition, you could change the computation of FlatView's root to
> resolve 2^64-sized aliases;

Here we reached the boundary of my english :)

Roots are given when AS/Flatview is created, and aliases are resolved already.

> also set it to NULL if the AddressSpace's
> root is disabled or the alias it resolves to is disabled (and so on
> recursively until a non-alias is found).  This should remove the need
> for address_space_root() and the change to pci_init_bus_master.
Paolo Bonzini Sept. 11, 2017, 9:37 a.m. UTC | #5
On 11/09/2017 11:06, Alexey Kardashevskiy wrote:
> On 11/09/17 17:40, Paolo Bonzini wrote:
>> On 07/09/2017 11:20, Alexey Kardashevskiy wrote:
>>>  
>>>      /* Accessed via RCU.  */
>>>      struct FlatView *current_map;
>>>  
>>>      int ioeventfd_nb;
>>>      struct MemoryRegionIoeventfd *ioeventfds;
>>> -    struct AddressSpaceDispatch *dispatch;
>>> -    struct AddressSpaceDispatch *next_dispatch;
>>> +
>>
>> The rough idea of the patch matches my suggestion indeed.  However, I am
>> not sure why all of the changes in patch 2 are needed.
> 
> For this:
> 
>  struct MemoryRegionSection {
>      MemoryRegion *mr;
> -    AddressSpace *address_space;
> +    AddressSpaceDispatch *dispatch;
> 
> as there are many ASes attached to the same flatview/dispatch.

Ok, this makes sense.  Maybe it should be a flatview rather than an
AddressSpaceDispatch (a FlatView is essentially a list of
MemoryRegionSections; attaching the ASD to the FlatView is more or less
an implementation detail).


> And because of that, there is also:
> 
>  struct IOMMUTLBEntry {
> -    AddressSpace    *target_as;
> +    AddressSpaceDispatch *target_dispatch;
> 
> as the "section" in address_space_get_iotlb_entry() does not have
> address_space any more, even though the only user of it -
> vhost_device_iotlb_miss() - only checks if (iotlb.target_dispatch != NULL).

You could change address_space_do_translate's "as" to AddressSpace **as.
 Then, address_space_get_iotlb_entry can populate the .target_as from
the output value of the argument.

>> In addition, you could change the computation of FlatView's root to
>> resolve 2^64-sized aliases;
> 
> Here we reached the boundary of my english :)
> 
> Roots are given when AS/Flatview is created, and aliases are resolved already.
> 

Currently, you're doing

+        if (view->root == root) {
+            as->current_map = flatview_get(view);
+            break;
+        }

(and by the way the above doesn't resolve aliases; aliases are only
resolved by render_memory_region).

Instead, the FlatViews should be shared at transaction commit time.  At
the beginning of commit, the list of flat views is empty.  As you
process each AddressSpace, you resolve the root alias(*) and search for
the resulting MemoryRegion in the list of FlatViews.  If you find it,
you do flatview_ref and point as->current_map to the FlatView you just
found.  Otherwise, you do generate_memory_topology etc. as in the old code.

(*) something like

	MemoryRegion *mr = as->root;
	MemoryRegion *root_mr;
	while (mr->alias && !mr->alias_offset &&
	       int128_ge(mr->size, mr->alias->size)) {
	    /* The alias is included in its entirety.  Use it as
	     * the "real" root, so that we can share more FlatViews.
	     */
	    mr = mr->alias;
	}	

	/* We found the "real" root, but maybe we can use a shared empty
	 * FlatView instead?
	 */
        for (root_mr = mr; root_mr; root_mr = root_mr->alias) {
	    if (!root_mr->enabled) {
		/* Use a shared empty FlatView.  */
		return NULL;
	    }
        }

        return mr;

Also:

> +static FlatView *flatview_get(FlatView *view)
>  {
> -    FlatView *view;
> -
>      rcu_read_lock();
> -    view = atomic_rcu_read(&as->current_map);
> +    view = atomic_rcu_read(&view);

This is "view = view" so it makes little sense, and then in the caller

+    view = flatview_get(as->current_map);

as->current_map is accessed without atomic_rcu_read.  So
address_space_get_flatview must stay.  Probably this will solve itself
when you do the rest.

Paolo

>> also set it to NULL if the AddressSpace's
>> root is disabled or the alias it resolves to is disabled (and so on
>> recursively until a non-alias is found).  This should remove the need
>> for address_space_root() and the change to pci_init_bus_master.
> 
> 
> 
>
Alexey Kardashevskiy Sept. 11, 2017, 12:08 p.m. UTC | #6
On 11/09/17 19:37, Paolo Bonzini wrote:
> On 11/09/2017 11:06, Alexey Kardashevskiy wrote:
>> On 11/09/17 17:40, Paolo Bonzini wrote:
>>> On 07/09/2017 11:20, Alexey Kardashevskiy wrote:
>>>>  
>>>>      /* Accessed via RCU.  */
>>>>      struct FlatView *current_map;
>>>>  
>>>>      int ioeventfd_nb;
>>>>      struct MemoryRegionIoeventfd *ioeventfds;
>>>> -    struct AddressSpaceDispatch *dispatch;
>>>> -    struct AddressSpaceDispatch *next_dispatch;
>>>> +
>>>
>>> The rough idea of the patch matches my suggestion indeed.  However, I am
>>> not sure why all of the changes in patch 2 are needed.
>>
>> For this:
>>
>>  struct MemoryRegionSection {
>>      MemoryRegion *mr;
>> -    AddressSpace *address_space;
>> +    AddressSpaceDispatch *dispatch;
>>
>> as there are many ASes attached to the same flatview/dispatch.
> 
> Ok, this makes sense.  Maybe it should be a flatview rather than an
> AddressSpaceDispatch (a FlatView is essentially a list of
> MemoryRegionSections; attaching the ASD to the FlatView is more or less
> an implementation detail).

The helpers I converted from AddressSpace to AddressSpaceDispatch do use
dispatch structure only and do not use FlatView so it seemed logical.

btw this address_space in MemoryRegionSection - it does not seem to make
much sense in the PhysPageMap::sections array, it only makes sense when
MemoryRegionSection uses as a temporary object when calling listeners. Will
it make sense if we enforce MemoryRegionSection::address_space to be NULL
in the array and not NULL when used temporary?


> 
> 
>> And because of that, there is also:
>>
>>  struct IOMMUTLBEntry {
>> -    AddressSpace    *target_as;
>> +    AddressSpaceDispatch *target_dispatch;
>>
>> as the "section" in address_space_get_iotlb_entry() does not have
>> address_space any more, even though the only user of it -
>> vhost_device_iotlb_miss() - only checks if (iotlb.target_dispatch != NULL).
> 
> You could change address_space_do_translate's "as" to AddressSpace **as.
>  Then, address_space_get_iotlb_entry can populate the .target_as from
> the output value of the argument.

Ok, since this seems better.


> 
>>> In addition, you could change the computation of FlatView's root to
>>> resolve 2^64-sized aliases;
>>
>> Here we reached the boundary of my english :)
>>
>> Roots are given when AS/Flatview is created, and aliases are resolved already.
>>
> 
> Currently, you're doing
> 
> +        if (view->root == root) {
> +            as->current_map = flatview_get(view);
> +            break;
> +        }
> 
> (and by the way the above doesn't resolve aliases; aliases are only
> resolved by render_memory_region).

I am learning as we speak :)


> 
> Instead, the FlatViews should be shared at transaction commit time.  At
> the beginning of commit, the list of flat views is empty.  As you
> process each AddressSpace, you resolve the root alias(*) and search for
> the resulting MemoryRegion in the list of FlatViews.  If you find it,
> you do flatview_ref and point as->current_map to the FlatView you just
> found.  Otherwise, you do generate_memory_topology etc. as in the old code.
> 
> (*) something like
> 
> 	MemoryRegion *mr = as->root;
> 	MemoryRegion *root_mr;
> 	while (mr->alias && !mr->alias_offset &&
> 	       int128_ge(mr->size, mr->alias->size)) {
> 	    /* The alias is included in its entirety.  Use it as
> 	     * the "real" root, so that we can share more FlatViews.
> 	     */
> 	    mr = mr->alias;
> 	}	
> 
> 	/* We found the "real" root, but maybe we can use a shared empty
> 	 * FlatView instead?
> 	 */
>         for (root_mr = mr; root_mr; root_mr = root_mr->alias) {
> 	    if (!root_mr->enabled) {
> 		/* Use a shared empty FlatView.  */
> 		return NULL;
> 	    }
>         }
> 
>         return mr;


Ah, makes sense now, thanks.

> 
> Also:
> 
>> +static FlatView *flatview_get(FlatView *view)
>>  {
>> -    FlatView *view;
>> -
>>      rcu_read_lock();
>> -    view = atomic_rcu_read(&as->current_map);
>> +    view = atomic_rcu_read(&view);
> 
> This is "view = view" so it makes little sense, and then in the caller
> 
> +    view = flatview_get(as->current_map);
> 
> as->current_map is accessed without atomic_rcu_read.  So
> address_space_get_flatview must stay.  Probably this will solve itself
> when you do the rest.
> 
> Paolo
> 
>>> also set it to NULL if the AddressSpace's
>>> root is disabled or the alias it resolves to is disabled (and so on
>>> recursively until a non-alias is found).  This should remove the need
>>> for address_space_root() and the change to pci_init_bus_master.
>>
>>
>>
>>
>
Paolo Bonzini Sept. 11, 2017, 3:30 p.m. UTC | #7
On 11/09/2017 14:08, Alexey Kardashevskiy wrote:
>> Ok, this makes sense.  Maybe it should be a flatview rather than an
>> AddressSpaceDispatch (a FlatView is essentially a list of
>> MemoryRegionSections; attaching the ASD to the FlatView is more or less
>> an implementation detail).
> The helpers I converted from AddressSpace to AddressSpaceDispatch do use
> dispatch structure only and do not use FlatView so it seemed logical.

Understood, but from a design POV FlatView makes more sense.

> btw this address_space in MemoryRegionSection - it does not seem to make
> much sense in the PhysPageMap::sections array, it only makes sense when
> MemoryRegionSection uses as a temporary object when calling listeners. Will
> it make sense if we enforce MemoryRegionSection::address_space to be NULL
> in the array and not NULL when used temporary?

memory_region_section_get_iotlb needs to access the AddressSpaceDispatch
for sections stored in the PhysPageMap array, because
memory_region_section_get_iotlb uses the ASD to compute the section index.

Paolo
Alexey Kardashevskiy Sept. 12, 2017, 5:55 a.m. UTC | #8
On 12/09/17 01:30, Paolo Bonzini wrote:
> On 11/09/2017 14:08, Alexey Kardashevskiy wrote:
>>> Ok, this makes sense.  Maybe it should be a flatview rather than an
>>> AddressSpaceDispatch (a FlatView is essentially a list of
>>> MemoryRegionSections; attaching the ASD to the FlatView is more or less
>>> an implementation detail).
>> The helpers I converted from AddressSpace to AddressSpaceDispatch do use
>> dispatch structure only and do not use FlatView so it seemed logical.
> 
> Understood, but from a design POV FlatView makes more sense.
> 
>> btw this address_space in MemoryRegionSection - it does not seem to make
>> much sense in the PhysPageMap::sections array, it only makes sense when
>> MemoryRegionSection uses as a temporary object when calling listeners. Will
>> it make sense if we enforce MemoryRegionSection::address_space to be NULL
>> in the array and not NULL when used temporary?
> 
> memory_region_section_get_iotlb needs to access the AddressSpaceDispatch
> for sections stored in the PhysPageMap array, because
> memory_region_section_get_iotlb uses the ASD to compute the section index.

Ohhh, not extremely trivial, out of curiosity - is that iotlb encoding
described anywhere?

Anyway, this can be simplified (or rather made more straightforward?) -
tlb_set_page_with_attrs() can calculate the section index and pass it to
memory_region_section_get_iotlb(). Still does not make much sense? It just
looks quite useless to keep that address_space pointer alive just for one
case which can easily avoid using this pointer.
Paolo Bonzini Sept. 12, 2017, 7:12 a.m. UTC | #9
On 12/09/2017 07:55, Alexey Kardashevskiy wrote:
> On 12/09/17 01:30, Paolo Bonzini wrote:
>> On 11/09/2017 14:08, Alexey Kardashevskiy wrote:
>>>> Ok, this makes sense.  Maybe it should be a flatview rather than an
>>>> AddressSpaceDispatch (a FlatView is essentially a list of
>>>> MemoryRegionSections; attaching the ASD to the FlatView is more or less
>>>> an implementation detail).
>>> The helpers I converted from AddressSpace to AddressSpaceDispatch do use
>>> dispatch structure only and do not use FlatView so it seemed logical.
>>
>> Understood, but from a design POV FlatView makes more sense.
>>
>>> btw this address_space in MemoryRegionSection - it does not seem to make
>>> much sense in the PhysPageMap::sections array, it only makes sense when
>>> MemoryRegionSection uses as a temporary object when calling listeners. Will
>>> it make sense if we enforce MemoryRegionSection::address_space to be NULL
>>> in the array and not NULL when used temporary?
>>
>> memory_region_section_get_iotlb needs to access the AddressSpaceDispatch
>> for sections stored in the PhysPageMap array, because
>> memory_region_section_get_iotlb uses the ASD to compute the section index.
> 
> Ohhh, not extremely trivial, out of curiosity - is that iotlb encoding
> described anywhere?

No, I don't think so.

> Anyway, this can be simplified (or rather made more straightforward?) -
> tlb_set_page_with_attrs() can calculate the section index and pass it to
> memory_region_section_get_iotlb(). Still does not make much sense? It just
> looks quite useless to keep that address_space pointer alive just for one
> case which can easily avoid using this pointer.

Hmm I suppose address_space_translate_for_iotlb knows the ASD and could
also return the index, basically combining it and
memory_region_section_get_iotlb() into one function.

Paolo
Alexey Kardashevskiy Sept. 12, 2017, 9:47 a.m. UTC | #10
On 12/09/17 17:12, Paolo Bonzini wrote:
> On 12/09/2017 07:55, Alexey Kardashevskiy wrote:
>> On 12/09/17 01:30, Paolo Bonzini wrote:
>>> On 11/09/2017 14:08, Alexey Kardashevskiy wrote:
>>>>> Ok, this makes sense.  Maybe it should be a flatview rather than an
>>>>> AddressSpaceDispatch (a FlatView is essentially a list of
>>>>> MemoryRegionSections; attaching the ASD to the FlatView is more or less
>>>>> an implementation detail).
>>>> The helpers I converted from AddressSpace to AddressSpaceDispatch do use
>>>> dispatch structure only and do not use FlatView so it seemed logical.
>>>
>>> Understood, but from a design POV FlatView makes more sense.
>>>
>>>> btw this address_space in MemoryRegionSection - it does not seem to make
>>>> much sense in the PhysPageMap::sections array, it only makes sense when
>>>> MemoryRegionSection uses as a temporary object when calling listeners. Will
>>>> it make sense if we enforce MemoryRegionSection::address_space to be NULL
>>>> in the array and not NULL when used temporary?
>>>
>>> memory_region_section_get_iotlb needs to access the AddressSpaceDispatch
>>> for sections stored in the PhysPageMap array, because
>>> memory_region_section_get_iotlb uses the ASD to compute the section index.
>>
>> Ohhh, not extremely trivial, out of curiosity - is that iotlb encoding
>> described anywhere?
> 
> No, I don't think so.
> 
>> Anyway, this can be simplified (or rather made more straightforward?) -
>> tlb_set_page_with_attrs() can calculate the section index and pass it to
>> memory_region_section_get_iotlb(). Still does not make much sense? It just
>> looks quite useless to keep that address_space pointer alive just for one
>> case which can easily avoid using this pointer.
> 
> Hmm I suppose address_space_translate_for_iotlb knows the ASD and could
> also return the index, basically combining it and
> memory_region_section_get_iotlb() into one function.

Ok, good.

One more question - how do we decide what goes to exec.c and what goes to
memory.c? I have a temptation to simply embed AddressSpaceDispatch into
FlatView instead of allocating it and storing the pointer (I'll probably
avoid doing so for now anyway but still curios).

The header comment for exec.c says it is "Virtual page mapping" but
AddressSpaceDispatch is a physical address space map and it seems to fit
into the memory.c's "Physical memory management" header comment.
diff mbox series

Patch

diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h
index fb467acdba..8516e0b48f 100644
--- a/include/exec/memory-internal.h
+++ b/include/exec/memory-internal.h
@@ -22,9 +22,11 @@ 
 #ifndef CONFIG_USER_ONLY
 typedef struct AddressSpaceDispatch AddressSpaceDispatch;
 
-void address_space_init_dispatch(AddressSpace *as);
 void address_space_unregister(AddressSpace *as);
-void address_space_destroy_dispatch(AddressSpace *as);
+void address_space_dispatch_free(AddressSpaceDispatch *d);
+AddressSpaceDispatch *mem_begin(void);
+void mem_commit(AddressSpaceDispatch *d);
+void mem_add(AddressSpaceDispatch *d, MemoryRegionSection *section);
 
 extern const MemoryRegionOps unassigned_mem_ops;
 
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 83e82e90d5..41ab165302 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -27,6 +27,7 @@ 
 #include "qemu/rcu.h"
 #include "hw/qdev-core.h"
 
+typedef struct AddressSpaceDispatch AddressSpaceDispatch;
 #define RAM_ADDR_INVALID (~(ram_addr_t)0)
 
 #define MAX_PHYS_ADDR_SPACE_BITS 62
@@ -312,6 +313,7 @@  struct MemoryListener {
 };
 
 AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as);
+MemoryRegion *address_space_root(AddressSpace *as);
 
 /**
  * AddressSpace: describes a mapping of addresses to #MemoryRegion objects
@@ -320,20 +322,17 @@  struct AddressSpace {
     /* All fields are private. */
     struct rcu_head rcu;
     char *name;
-    MemoryRegion *root;
-    int ref_count;
-    bool malloced;
 
     /* Accessed via RCU.  */
     struct FlatView *current_map;
 
     int ioeventfd_nb;
     struct MemoryRegionIoeventfd *ioeventfds;
-    struct AddressSpaceDispatch *dispatch;
-    struct AddressSpaceDispatch *next_dispatch;
+
     MemoryListener dispatch_listener;
     QTAILQ_HEAD(memory_listeners_as, MemoryListener) listeners;
     QTAILQ_ENTRY(AddressSpace) address_spaces_link;
+    QTAILQ_ENTRY(AddressSpace) flat_view_link;
 };
 
 /**
diff --git a/exec.c b/exec.c
index 66f01f5fce..51243f57f4 100644
--- a/exec.c
+++ b/exec.c
@@ -188,15 +188,12 @@  typedef struct PhysPageMap {
 } PhysPageMap;
 
 struct AddressSpaceDispatch {
-    struct rcu_head rcu;
-
     MemoryRegionSection *mru_section;
     /* This is a multi-level map on the physical address space.
      * The bottom level has pointers to MemoryRegionSections.
      */
     PhysPageEntry phys_map;
     PhysPageMap map;
-    AddressSpace *as;
 };
 
 typedef struct AddressSpaceDispatch AddressSpaceDispatch;
@@ -240,11 +237,6 @@  struct DirtyBitmapSnapshot {
     unsigned long dirty[];
 };
 
-AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
-{
-    return atomic_rcu_read(&as->dispatch);
-}
-
 #endif
 
 #if !defined(CONFIG_USER_ONLY)
@@ -1354,10 +1346,8 @@  static void register_multipage(AddressSpaceDispatch *d,
     phys_page_set(d, start_addr >> TARGET_PAGE_BITS, num_pages, section_index);
 }
 
-static void mem_add(MemoryListener *listener, MemoryRegionSection *section)
+void mem_add(AddressSpaceDispatch *d, MemoryRegionSection *section)
 {
-    AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
-    AddressSpaceDispatch *d = as->next_dispatch;
     MemoryRegionSection now = *section, remain = *section;
     Int128 page_size = int128_make64(TARGET_PAGE_SIZE);
 
@@ -2680,9 +2670,8 @@  static void io_mem_init(void)
                           NULL, UINT64_MAX);
 }
 
-static void mem_begin(MemoryListener *listener)
+AddressSpaceDispatch *mem_begin(void)
 {
-    AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
     AddressSpaceDispatch *d = g_new0(AddressSpaceDispatch, 1);
     uint16_t n;
 
@@ -2696,27 +2685,19 @@  static void mem_begin(MemoryListener *listener)
     assert(n == PHYS_SECTION_WATCH);
 
     d->phys_map  = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .skip = 1 };
-    as->next_dispatch = d;
+
+    return d;
 }
 
-static void address_space_dispatch_free(AddressSpaceDispatch *d)
+void address_space_dispatch_free(AddressSpaceDispatch *d)
 {
     phys_sections_free(&d->map);
     g_free(d);
 }
 
-static void mem_commit(MemoryListener *listener)
+void mem_commit(AddressSpaceDispatch *d)
 {
-    AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
-    AddressSpaceDispatch *cur = as->dispatch;
-    AddressSpaceDispatch *next = as->next_dispatch;
-
-    phys_page_compact_all(next, next->map.nodes_nb);
-
-    atomic_rcu_set(&as->dispatch, next);
-    if (cur) {
-        call_rcu(cur, address_space_dispatch_free, rcu);
-    }
+    phys_page_compact_all(d, d->map.nodes_nb);
 }
 
 static void tcg_commit(MemoryListener *listener)
@@ -2732,39 +2713,16 @@  static void tcg_commit(MemoryListener *listener)
      * We reload the dispatch pointer now because cpu_reloading_memory_map()
      * may have split the RCU critical section.
      */
-    d = atomic_rcu_read(&cpuas->as->dispatch);
+    d = address_space_to_dispatch(cpuas->as);
     atomic_rcu_set(&cpuas->memory_dispatch, d);
     tlb_flush(cpuas->cpu);
 }
 
-void address_space_init_dispatch(AddressSpace *as)
-{
-    as->dispatch = NULL;
-    as->dispatch_listener = (MemoryListener) {
-        .begin = mem_begin,
-        .commit = mem_commit,
-        .region_add = mem_add,
-        .region_nop = mem_add,
-        .priority = 0,
-    };
-    memory_listener_register(&as->dispatch_listener, as);
-}
-
 void address_space_unregister(AddressSpace *as)
 {
     memory_listener_unregister(&as->dispatch_listener);
 }
 
-void address_space_destroy_dispatch(AddressSpace *as)
-{
-    AddressSpaceDispatch *d = as->dispatch;
-
-    atomic_rcu_set(&as->dispatch, NULL);
-    if (d) {
-        call_rcu(d, address_space_dispatch_free, rcu);
-    }
-}
-
 static void memory_map_init(void)
 {
     system_memory = g_malloc(sizeof(*system_memory));
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 258fbe51e2..86b9e419fd 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -88,7 +88,8 @@  static void pci_init_bus_master(PCIDevice *pci_dev)
 
     memory_region_init_alias(&pci_dev->bus_master_enable_region,
                              OBJECT(pci_dev), "bus master",
-                             dma_as->root, 0, memory_region_size(dma_as->root));
+                             address_space_root(dma_as), 0,
+                             memory_region_size(address_space_root(dma_as)));
     memory_region_set_enabled(&pci_dev->bus_master_enable_region, false);
     memory_region_add_subregion(&pci_dev->bus_master_container_region, 0,
                                 &pci_dev->bus_master_enable_region);
diff --git a/memory.c b/memory.c
index c6904a7deb..385a507511 100644
--- a/memory.c
+++ b/memory.c
@@ -47,6 +47,9 @@  static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners
 static QTAILQ_HEAD(, AddressSpace) address_spaces
     = QTAILQ_HEAD_INITIALIZER(address_spaces);
 
+static QTAILQ_HEAD(FlatViewList, FlatView) flat_views
+    = QTAILQ_HEAD_INITIALIZER(flat_views);
+
 typedef struct AddrRange AddrRange;
 
 /*
@@ -230,6 +233,11 @@  struct FlatView {
     FlatRange *ranges;
     unsigned nr;
     unsigned nr_allocated;
+    MemoryRegion *root;
+    struct AddressSpaceDispatch *dispatch;
+
+    QTAILQ_ENTRY(FlatView) flat_views_link;
+    QTAILQ_HEAD(address_spaces, AddressSpace) address_spaces;
 };
 
 typedef struct AddressSpaceOps AddressSpaceOps;
@@ -259,12 +267,19 @@  static bool flatrange_equal(FlatRange *a, FlatRange *b)
         && a->readonly == b->readonly;
 }
 
-static void flatview_init(FlatView *view)
+static void flatview_ref(FlatView *view);
+
+static FlatView *flatview_alloc(MemoryRegion *mr_root)
 {
+    FlatView *view;
+
+    view = g_new0(FlatView, 1);
     view->ref = 1;
-    view->ranges = NULL;
-    view->nr = 0;
-    view->nr_allocated = 0;
+    view->root = mr_root;
+    memory_region_ref(mr_root);
+    QTAILQ_INIT(&view->address_spaces);
+
+    return view;
 }
 
 /* Insert a range into a given position.  Caller is responsible for maintaining
@@ -292,6 +307,10 @@  static void flatview_destroy(FlatView *view)
         memory_region_unref(view->ranges[i].mr);
     }
     g_free(view->ranges);
+    if (view->dispatch) {
+        address_space_dispatch_free(view->dispatch);
+    }
+    memory_region_unref(view->root);
     g_free(view);
 }
 
@@ -303,7 +322,7 @@  static void flatview_ref(FlatView *view)
 static void flatview_unref(FlatView *view)
 {
     if (atomic_fetch_dec(&view->ref) == 1) {
-        flatview_destroy(view);
+        call_rcu(view, flatview_destroy, rcu);
     }
 }
 
@@ -608,7 +627,7 @@  static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
         mr = mr->container;
     }
     QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
-        if (mr == as->root) {
+        if (mr == as->current_map->root) {
             return as;
         }
     }
@@ -702,23 +721,6 @@  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)
-{
-    FlatView *view;
-
-    view = g_new(FlatView, 1);
-    flatview_init(view);
-
-    if (mr) {
-        render_memory_region(view, mr, int128_zero(),
-                             addrrange_make(int128_zero(), int128_2_64()), false);
-    }
-    flatview_simplify(view);
-
-    return view;
-}
-
 static void address_space_add_del_ioeventfds(AddressSpace *as,
                                              MemoryRegionIoeventfd *fds_new,
                                              unsigned fds_new_nb,
@@ -769,12 +771,10 @@  static void address_space_add_del_ioeventfds(AddressSpace *as,
     }
 }
 
-static FlatView *address_space_get_flatview(AddressSpace *as)
+static FlatView *flatview_get(FlatView *view)
 {
-    FlatView *view;
-
     rcu_read_lock();
-    view = atomic_rcu_read(&as->current_map);
+    view = atomic_rcu_read(&view);
     flatview_ref(view);
     rcu_read_unlock();
     return view;
@@ -789,7 +789,7 @@  static void address_space_update_ioeventfds(AddressSpace *as)
     AddrRange tmp;
     unsigned i;
 
-    view = address_space_get_flatview(as);
+    view = flatview_get(as->current_map);
     FOR_EACH_FLAT_RANGE(fr, view) {
         for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
             tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
@@ -881,28 +881,89 @@  static void address_space_update_topology_pass(AddressSpace *as,
     }
 }
 
-
-static void address_space_update_topology(AddressSpace *as)
+static FlatView *address_space_update_flatview(FlatView *view)
 {
-    FlatView *old_view = address_space_get_flatview(as);
-    FlatView *new_view = generate_memory_topology(as->root);
+    AddressSpace *as, *asnext;
+    FlatView *old_view = flatview_get(view);
+    MemoryRegion *root = old_view->root;
+    FlatView *new_view = flatview_alloc(root);
+    unsigned iold, inew;
+    FlatRange *frold, *frnew;
 
-    address_space_update_topology_pass(as, old_view, new_view, false);
-    address_space_update_topology_pass(as, old_view, new_view, true);
+    if (root) {
+        render_memory_region(new_view, root, int128_zero(),
+                             addrrange_make(int128_zero(), int128_2_64()),
+                             false);
+        flatview_simplify(new_view);
+    }
 
-    /* Writes are protected by the BQL.  */
-    atomic_rcu_set(&as->current_map, new_view);
-    call_rcu(old_view, flatview_unref, rcu);
+    new_view->dispatch = mem_begin();
 
-    /* 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.
+    /*
+     * FIXME: this is cut-n-paste from address_space_update_topology_pass,
+     * simplify it
      */
+    iold = inew = 0;
+    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];
+        } else {
+            frnew = NULL;
+        }
+
+        if (frold
+            && (!frnew
+                || int128_lt(frold->addr.start, frnew->addr.start)
+                || (int128_eq(frold->addr.start, frnew->addr.start)
+                    && !flatrange_equal(frold, frnew)))) {
+            ++iold;
+        } else if (frold && frnew && flatrange_equal(frold, frnew)) {
+            /* In both and unchanged (except logging may have changed) */
+            MemoryRegionSection mrs = section_from_flat_range(frnew,
+                    new_view->dispatch);
+
+            mem_add(new_view->dispatch, &mrs);
+
+            ++iold;
+            ++inew;
+        } else {
+            /* In new */
+            MemoryRegionSection mrs = section_from_flat_range(frnew,
+                    new_view->dispatch);
+
+            mem_add(new_view->dispatch, &mrs);
+
+            ++inew;
+        }
+    }
+
+    mem_commit(new_view->dispatch);
+
+    QTAILQ_FOREACH(as, &old_view->address_spaces, flat_view_link) {
+        address_space_update_topology_pass(as, old_view, new_view, false);
+        address_space_update_topology_pass(as, old_view, new_view, true);
+    }
+
+    QTAILQ_FOREACH_SAFE(as, &old_view->address_spaces, flat_view_link, asnext) {
+        QTAILQ_REMOVE(&old_view->address_spaces, as, flat_view_link);
+        flatview_unref(old_view);
+
+        atomic_rcu_set(&as->current_map, new_view);
+
+        flatview_ref(new_view);
+        QTAILQ_INSERT_TAIL(&new_view->address_spaces, as, flat_view_link);
+
+        address_space_update_ioeventfds(as);
+    }
+
     flatview_unref(old_view);
 
-    address_space_update_ioeventfds(as);
+    return new_view;
 }
 
 void memory_region_transaction_begin(void)
@@ -921,11 +982,31 @@  void memory_region_transaction_commit(void)
     --memory_region_transaction_depth;
     if (!memory_region_transaction_depth) {
         if (memory_region_update_pending) {
+            FlatView *view, *vnext;
+            struct FlatViewList fwstmp = QTAILQ_HEAD_INITIALIZER(fwstmp);
+
             MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
 
-            QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
-                address_space_update_topology(as);
+            QTAILQ_FOREACH_SAFE(view, &flat_views, flat_views_link, vnext) {
+                FlatView *old_view, *new_view;
+
+                old_view = flatview_get(view);
+                new_view = address_space_update_flatview(old_view);
+
+                QTAILQ_REMOVE(&flat_views, old_view, flat_views_link);
+                flatview_unref(old_view);
+                flatview_unref(old_view);
+
+                QTAILQ_INSERT_HEAD(&fwstmp, new_view, flat_views_link);
+
+                flatview_unref(new_view);
             }
+
+            QTAILQ_FOREACH_SAFE(view, &fwstmp, flat_views_link, vnext) {
+                QTAILQ_REMOVE(&fwstmp, view, flat_views_link);
+                QTAILQ_INSERT_HEAD(&flat_views, view, flat_views_link);
+            }
+
             memory_region_update_pending = false;
             MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
         } else if (ioeventfd_update_pending) {
@@ -1834,7 +1915,7 @@  void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
             continue;
         }
         as = listener->address_space;
-        view = address_space_get_flatview(as);
+        view = flatview_get(as->current_map);
         FOR_EACH_FLAT_RANGE(fr, view) {
             if (fr->mr == mr) {
                 MemoryRegionSection mrs = section_from_flat_range(fr,
@@ -1938,7 +2019,7 @@  static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
     AddrRange tmp;
     MemoryRegionSection section;
 
-    view = address_space_get_flatview(as);
+    view = flatview_get(as->current_map);
     FOR_EACH_FLAT_RANGE(fr, view) {
         if (fr->mr == mr) {
             section = (MemoryRegionSection) {
@@ -2350,7 +2431,7 @@  void memory_global_dirty_log_sync(void)
             continue;
         }
         as = listener->address_space;
-        view = address_space_get_flatview(as);
+        view = flatview_get(as->current_map);
         FOR_EACH_FLAT_RANGE(fr, view) {
             if (fr->dirty_log_mask) {
                 MemoryRegionSection mrs = section_from_flat_range(fr,
@@ -2436,7 +2517,7 @@  static void listener_add_address_space(MemoryListener *listener,
         }
     }
 
-    view = address_space_get_flatview(as);
+    view = flatview_get(as->current_map);
     FOR_EACH_FLAT_RANGE(fr, view) {
         MemoryRegionSection section = {
             .mr = fr->mr,
@@ -2615,67 +2696,72 @@  void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset,
 
 void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
 {
-    memory_region_ref(root);
-    memory_region_transaction_begin();
-    as->ref_count = 1;
-    as->root = root;
-    as->malloced = false;
-    as->current_map = g_new(FlatView, 1);
-    flatview_init(as->current_map);
+    FlatView *view;
+
+    as->current_map = NULL;
     as->ioeventfd_nb = 0;
     as->ioeventfds = NULL;
     QTAILQ_INIT(&as->listeners);
     QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
     as->name = g_strdup(name ? name : "anonymous");
-    address_space_init_dispatch(as);
-    memory_region_update_pending |= root->enabled;
-    memory_region_transaction_commit();
+
+    QTAILQ_FOREACH(view, &flat_views, flat_views_link) {
+        assert(root);
+        if (view->root == root) {
+            as->current_map = flatview_get(view);
+            break;
+        }
+    }
+
+    if (!as->current_map) {
+        as->current_map = flatview_alloc(root);
+
+        QTAILQ_INSERT_TAIL(&flat_views, as->current_map, flat_views_link);
+    }
+
+    QTAILQ_INSERT_TAIL(&as->current_map->address_spaces, as, flat_view_link);
+}
+
+MemoryRegion *address_space_root(AddressSpace *as)
+{
+    return as->current_map->root;
+}
+
+AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
+{
+    return atomic_rcu_read(&as->current_map)->dispatch;
 }
 
 static void do_address_space_destroy(AddressSpace *as)
 {
-    bool do_free = as->malloced;
+    FlatView *view = flatview_get(as->current_map);
 
-    address_space_destroy_dispatch(as);
     assert(QTAILQ_EMPTY(&as->listeners));
 
-    flatview_unref(as->current_map);
+    QTAILQ_REMOVE(&view->address_spaces, as, flat_view_link);
+    flatview_unref(view);
+
+    flatview_unref(view);
+
     g_free(as->name);
     g_free(as->ioeventfds);
-    memory_region_unref(as->root);
-    if (do_free) {
-        g_free(as);
-    }
+    g_free(as);
 }
 
 AddressSpace *address_space_init_shareable(MemoryRegion *root, const char *name)
 {
     AddressSpace *as;
 
-    QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
-        if (root == as->root && as->malloced) {
-            as->ref_count++;
-            return as;
-        }
-    }
-
     as = g_malloc0(sizeof *as);
     address_space_init(as, root, name);
-    as->malloced = true;
+
     return as;
 }
 
 void address_space_destroy(AddressSpace *as)
 {
-    MemoryRegion *root = as->root;
-
-    as->ref_count--;
-    if (as->ref_count) {
-        return;
-    }
     /* 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);
@@ -2684,7 +2770,6 @@  void address_space_destroy(AddressSpace *as)
      * 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);
 }
 
@@ -2816,7 +2901,7 @@  static void mtree_print_mr(fprintf_function mon_printf, void *f,
 static void mtree_print_flatview(fprintf_function p, void *f,
                                  AddressSpace *as)
 {
-    FlatView *view = address_space_get_flatview(as);
+    FlatView *view = flatview_get(as->current_map);
     FlatRange *range = &view->ranges[0];
     MemoryRegion *mr;
     int n = view->nr;
@@ -2873,7 +2958,7 @@  void mtree_info(fprintf_function mon_printf, void *f, bool flatview)
 
     QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
         mon_printf(f, "address-space: %s\n", as->name);
-        mtree_print_mr(mon_printf, f, as->root, 1, 0, &ml_head);
+        mtree_print_mr(mon_printf, f, as->current_map->root, 1, 0, &ml_head);
         mon_printf(f, "\n");
     }