From: Peter Xu <peterx@redhat.com>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: qemu-devel@nongnu.org,
	"Philippe Mathieu-Daudé" <philmd@linaro.org>,
	"David Hildenbrand" <david@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Akihiko Odaki" <odaki@rsg.ci.i.u-tokyo.ac.jp>,
	"Michael S . Tsirkin" <mst@redhat.com>
Subject: Re: [PATCH] memory: Fix leaks due to owner-shared MRs circular references
Date: Thu, 4 Sep 2025 18:23:50 -0400	[thread overview]
Message-ID: <aLoRdtk4RGNMk_fN@x1.local> (raw)
In-Reply-To: <CAFEAcA-4pqbqSwiHtpVnRE0taReP7evnwwTtekRMGo307NN=mQ@mail.gmail.com>
On Tue, Sep 02, 2025 at 11:06:13AM +0100, Peter Maydell wrote:
> On Tue, 26 Aug 2025 at 23:20, Peter Xu <peterx@redhat.com> wrote:
> >
> > Currently, QEMU refcounts the MR by always taking it from the owner.
> >
> > It's common that one object will have multiple MR objects embeded in the
> > object itself.  All the MRs in this case share the same lifespan of the
> > owner object.
> >
> > It's also common that in the instance_init() of an object, MR A can be a
> > container of MR B, C, D, by using memory_region_add_subregion*() set of
> > memory region APIs.
> >
> > Now we have a circular reference issue, as when adding subregions for MR A,
> > we essentially incremented the owner's refcount within the instance_init(),
> > meaning the object will be self-boosted and its refcount can never go down
> > to zero if the MRs won't get detached properly before object's finalize().
> >
> > Delete subregions within object's finalize() won't work either, because
> > finalize() will be invoked only if the refcount goes to zero first.  What
> > is worse, object_finalize() will do object_property_del_all() first before
> > object_deinit().  Since embeded MRs will be properties of the owner object,
> > it means they'll be freed _before_ the owner's finalize().
> >
> > To fix that, teach memory API to stop refcount on MRs that share the same
> > owner.  Because if they share the lifecycle of the owner, then they share
> > the same lifecycle between themselves, hence the refcount doesn't help but
> > only introduce troubles.
> >
> > Meanwhile, allow auto-detachments of MRs during finalize() of MRs even
> > against its container, as long as they belong to the same owner.
> >
> > The latter is needed because now it's possible to have MRs' finalize()
> > happen in any order when they share the same lifespan with a same owner.
> > In this case, we should allow finalize() to happen in any order of either
> > the parent or child MR.  Loose the mr->container check in MR's finalize()
> > to allow auto-detach.  Double check it shares the same owner.
> >
> > Proper document this behavior in code.
> >
> > This patch is heavily based on the work done by Akihiko Odaki:
> >
> > https://lore.kernel.org/r/CAFEAcA8DV40fGsci76r4yeP1P-SP_QjNRDD2OzPxjx5wRs0GEg@mail.gmail.com
> >
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> 
> I have some wordsmithing review stuff for doc and comment text
> below, but otherwise
> 
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
> Tested-by: Peter Maydell <peter.maydell@linaro.org>
> 
> > ---
> >  docs/devel/memory.rst |  7 +++++--
> >  system/memory.c       | 45 ++++++++++++++++++++++++++++++++++---------
> >  2 files changed, 41 insertions(+), 11 deletions(-)
> >
> > diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst
> > index 57fb2aec76..a325e97d7b 100644
> > --- a/docs/devel/memory.rst
> > +++ b/docs/devel/memory.rst
> > @@ -158,8 +158,11 @@ ioeventfd) can be changed during the region lifecycle.  They take effect
> >  as soon as the region is made visible.  This can be immediately, later,
> >  or never.
> >
> > -Destruction of a memory region happens automatically when the owner
> > -object dies.
> > +Destruction of a memory region happens automatically when the owner object
> > +dies.  When there are multiple memory regions under the same owner object,
> > +the memory API will guarantee all memory regions will be properly detached
> > +and finalized one by one.  The order which memory region will be finalized
> 
> "The order in which memory regions will be finalized is not
> guaranteed."
> 
> > +first is not guaranteed.
> >
> >  If however the memory region is part of a dynamically allocated data
> >  structure, you should call object_unparent() to destroy the memory region
> > diff --git a/system/memory.c b/system/memory.c
> > index 5646547940..d7f6ad9be2 100644
> > --- a/system/memory.c
> > +++ b/system/memory.c
> > @@ -1796,16 +1796,36 @@ static void memory_region_finalize(Object *obj)
> >  {
> >      MemoryRegion *mr = MEMORY_REGION(obj);
> >
> > -    assert(!mr->container);
> > -
> > -    /* We know the region is not visible in any address space (it
> > -     * does not have a container and cannot be a root either because
> > -     * it has no references, so we can blindly clear mr->enabled.
> > -     * memory_region_set_enabled instead could trigger a transaction
> > -     * and cause an infinite loop.
> > +    /*
> > +     * Each memory region (that can be dynamically freed..) must has an
> 
> s/..//
> s/must has/must have/
> 
> > +     * owner, and it always has the same lifecycle of its owner.  It means
> > +     * when reaching here, the memory region's owner refcount is zero.
> 
> "region's owner's refcount"
> 
> > +     *
> > +     * Here it is possible that the MR has:
> > +     *
> > +     * (1) mr->container set, which means this MR can be a subregion of a
> 
> "this MR is a subregion of"
> 
> > +     *     container MR, in this case it must share the same owner
> 
> s/, in/. In/
> 
> "same owner as that container"
> 
> > +     *     (otherwise the container should have kept a refcount of this
> > +     *     MR's owner), or,
> 
> s/, or,/
> 
> (it's possible for both 1 and 2 to be true for an MR here)
> 
> > +     *
> > +     * (2) mr->subregions non-empty, which means this MR can be a container
> 
> "is a container"
I'll fix all things until here (including Clement's suggestions in the
other email).
> 
> > +     *     of other MRs (share the owner or not).
> 
> "of another MR (which might have the same owner as this MR, or
>  a different owner)"
IIUC there can be one, or more than one MRs as children.  The finalize() of
this MR should release the rest refcounts for all of them if applicable.
Latest version of this paragraph looks like this now:
     * (2) mr->subregions non-empty, which means this MR is a container of
     *     one or more other MRs (which might have the the owner as this
     *     MR, or a different owner).
If above looks good I'll go ahead and merge this patch with the fixups.
Thanks a lot to both for the careful reviews (and tests).
-- 
Peter Xu
     prev parent reply	other threads:[~2025-09-04 22:27 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-26 22:17 [PATCH] memory: Fix leaks due to owner-shared MRs circular references Peter Xu
2025-08-27  5:44 ` CLEMENT MATHIEU--DRIF
2025-09-02 10:06 ` Peter Maydell
2025-09-04 22:23   ` Peter Xu [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox
  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):
  git send-email \
    --in-reply-to=aLoRdtk4RGNMk_fN@x1.local \
    --to=peterx@redhat.com \
    --cc=david@redhat.com \
    --cc=mst@redhat.com \
    --cc=odaki@rsg.ci.i.u-tokyo.ac.jp \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY
  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
  Be sure your reply has a Subject: header at the top and a blank line
  before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).