[PATCH 1/2] drm/client: fix circular reference counting issue

Daniel Vetter daniel at ffwll.ch
Fri Feb 17 18:56:21 UTC 2023


On Fri, 17 Feb 2023 at 13:06, Christian König
<ckoenig.leichtzumerken at gmail.com> wrote:
>
> Am 16.02.23 um 15:34 schrieb Daniel Vetter:
> > On Thu, Jan 26, 2023 at 03:30:31PM +0100, Thomas Zimmermann wrote:
> >> Hi
> >>
> >> Am 26.01.23 um 11:28 schrieb Christian König:
> >>> We reference dump buffers both by their handle as well as their
> >>> object. The problem is now that when anybody iterates over the DRM
> >>> framebuffers and exports the underlying GEM objects through DMA-buf
> >>> we run into a circular reference count situation.
> >>>
> >>> The result is that the fbdev handling holds the GEM handle preventing
> >>> the DMA-buf in the GEM object to be released. This DMA-buf in turn
> >>> holds a reference to the driver module which on unload would release
> >>> the fbdev.
> >>>
> >>> Break that loop by releasing the handle as soon as the DRM
> >>> framebuffer object is created. The DRM framebuffer and the DRM client
> >>> buffer structure still hold a reference to the underlying GEM object
> >>> preventing its destruction.
> >>>
> >>> Signed-off-by: Christian König <christian.koenig at amd.com>
> >>> Fixes: c76f0f7cb546 ("drm: Begin an API for in-kernel clients")
> >>> Cc: <stable at vger.kernel.org>
> >> I tested with Weston and Gnome in X11 and Wayland mode under simpledrm,
> >> which I started stopped from the console. No obvious problems.
> >>
> >> I heard that sway/wlroots has issues with drivers that don't support
> >> dma-buf. Maybe(!) that could be affected by this patch.
> > dma-buf export should still work. Also the loop is imo a red herring, I
> > think if you force unbind the driver then this should all get resolved
> > automatically.
> >
> > What is true is that once we start refcounting everything correctly then
> > there will be elevated module refcounts, which means you cannot use module
> > unloading to provoke a driver unbind, which would kick out all the
> > leftover references. You instead need to manually unbind the driver first,
> > which should drop all remaining references to zero (might need to kill
> > also any userspace), and only then can you unload the driver.
> >
> > But this confusion is extremely common, a lot of people think that just
> > holding a module reference is enough, we really should also hold a
> > drm_device reference for dma-buf ...
>
> Yeah, hot plug removal of amdgpu revealed a couple of those as well.
>
> Essentially what DMA-buf does with grabbing a module reference on the
> owner of a DMA-buf is a bad idea.
>
> Instead we should reference the device or component which is exporting
> the buffer, but since we don't have a common structure here it's more
> work to generalize that approach.

Well the device/component still needs to eventually hold a reference
on the module, or bad things can happen. But yeah dma-buf also holding
one but not a device/component reference is definitely bad.
-Daniel

>
> Christian.
>
> > -Daniel
> >
> >> Anyway, take my r-b, t-b tags.
> >>
> >> Reviewed-by: Thomas Zimmermann <tzimmermann at suse.de>
> >> Tested-by: Thomas Zimmermann <tzimmermann at suse.de>
> >>
> >> Thank you for fixing this bug.
> >>
> >> Best regards
> >> Thomas
> >>
> >>> ---
> >>>    drivers/gpu/drm/drm_client.c | 33 ++++++++++++++++++++-------------
> >>>    include/drm/drm_client.h     |  5 -----
> >>>    2 files changed, 20 insertions(+), 18 deletions(-)
> >>>
> >>> diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
> >>> index 009e7b10455c..f6292ba0e6fc 100644
> >>> --- a/drivers/gpu/drm/drm_client.c
> >>> +++ b/drivers/gpu/drm/drm_client.c
> >>> @@ -243,21 +243,17 @@ void drm_client_dev_restore(struct drm_device *dev)
> >>>    static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
> >>>    {
> >>> -   struct drm_device *dev = buffer->client->dev;
> >>> -
> >>>     if (buffer->gem) {
> >>>             drm_gem_vunmap_unlocked(buffer->gem, &buffer->map);
> >>>             drm_gem_object_put(buffer->gem);
> >>>     }
> >>> -   if (buffer->handle)
> >>> -           drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
> >>> -
> >>>     kfree(buffer);
> >>>    }
> >>>    static struct drm_client_buffer *
> >>> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
> >>> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
> >>> +                    u32 format, u32 *handle)
> >>>    {
> >>>     const struct drm_format_info *info = drm_format_info(format);
> >>>     struct drm_mode_create_dumb dumb_args = { };
> >>> @@ -279,16 +275,15 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
> >>>     if (ret)
> >>>             goto err_delete;
> >>> -   buffer->handle = dumb_args.handle;
> >>> -   buffer->pitch = dumb_args.pitch;
> >>> -
> >>>     obj = drm_gem_object_lookup(client->file, dumb_args.handle);
> >>>     if (!obj)  {
> >>>             ret = -ENOENT;
> >>>             goto err_delete;
> >>>     }
> >>> +   buffer->pitch = dumb_args.pitch;
> >>>     buffer->gem = obj;
> >>> +   *handle = dumb_args.handle;
> >>>     return buffer;
> >>> @@ -375,7 +370,8 @@ static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
> >>>    }
> >>>    static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
> >>> -                              u32 width, u32 height, u32 format)
> >>> +                              u32 width, u32 height, u32 format,
> >>> +                              u32 handle)
> >>>    {
> >>>     struct drm_client_dev *client = buffer->client;
> >>>     struct drm_mode_fb_cmd fb_req = { };
> >>> @@ -387,7 +383,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
> >>>     fb_req.depth = info->depth;
> >>>     fb_req.width = width;
> >>>     fb_req.height = height;
> >>> -   fb_req.handle = buffer->handle;
> >>> +   fb_req.handle = handle;
> >>>     fb_req.pitch = buffer->pitch;
> >>>     ret = drm_mode_addfb(client->dev, &fb_req, client->file);
> >>> @@ -424,13 +420,24 @@ struct drm_client_buffer *
> >>>    drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
> >>>    {
> >>>     struct drm_client_buffer *buffer;
> >>> +   u32 handle;
> >>>     int ret;
> >>> -   buffer = drm_client_buffer_create(client, width, height, format);
> >>> +   buffer = drm_client_buffer_create(client, width, height, format,
> >>> +                                     &handle);
> >>>     if (IS_ERR(buffer))
> >>>             return buffer;
> >>> -   ret = drm_client_buffer_addfb(buffer, width, height, format);
> >>> +   ret = drm_client_buffer_addfb(buffer, width, height, format, handle);
> >>> +
> >>> +   /*
> >>> +    * The handle is only needed for creating the framebuffer, destroy it
> >>> +    * again to solve a circular dependency should anybody export the GEM
> >>> +    * object as DMA-buf. The framebuffer and our buffer structure are still
> >>> +    * holding references to the GEM object to prevent its destruction.
> >>> +    */
> >>> +   drm_mode_destroy_dumb(client->dev, handle, client->file);
> >>> +
> >>>     if (ret) {
> >>>             drm_client_buffer_delete(buffer);
> >>>             return ERR_PTR(ret);
> >>> diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
> >>> index 39482527a775..b5acdab73766 100644
> >>> --- a/include/drm/drm_client.h
> >>> +++ b/include/drm/drm_client.h
> >>> @@ -134,11 +134,6 @@ struct drm_client_buffer {
> >>>      */
> >>>     struct drm_client_dev *client;
> >>> -   /**
> >>> -    * @handle: Buffer handle
> >>> -    */
> >>> -   u32 handle;
> >>> -
> >>>     /**
> >>>      * @pitch: Buffer pitch
> >>>      */
> >> --
> >> Thomas Zimmermann
> >> Graphics Driver Developer
> >> SUSE Software Solutions Germany GmbH
> >> Maxfeldstr. 5, 90409 Nürnberg, Germany
> >> (HRB 36809, AG Nürnberg)
> >> Geschäftsführer: Ivo Totev
> >
> >
> >
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list