Skip to content

Commit 83f45fc

Browse files
committed
drm: Don't grab an fb reference for the idr
The current refcounting scheme is that the fb lookup idr also holds a reference. This works out nicely bacause thus far we've always explicitly cleaned up idr entries for framebuffers: - Userspace fbs get removed in the rmfb ioctl or when the drm file gets closed. - Kernel fbs (for fbdev emulation) get cleaned up by the driver code at module unload time. But now i915 also reconstructs the bios fbs for a smooth transition. And that fb is purely transitional and should get removed immmediately once all crtcs stop using it. Of course if the i915 fbdev code decides to reuse it as the main fbdev fb then it shouldn't be cleaned up, but in that case the fbdev code will grab it's own reference. The problem is now that we also want to register that takeover fb in the idr, so that userspace can do a smooth transition (animated maybe even!) itself. But currently we have no one who will clean up the idr reference once that fb isn't useful any more, and so essentially leak it. Fix this by no longer holding a full fb reference for the idr, but instead just have a weak reference using kref_get_unless_zero. But that requires us to synchronize and clean up with the idr and fb_lock in drm_framebuffer_free, so add that. It's a bit ugly that we have to unconditionally grab the fb_lock, but without that someone might creep through a race. This leak was caught by the fb leak check in drm_mode_config_cleanup. Originally the leak was introduced in commit 46f297f Author: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri Mar 7 08:57:48 2014 -0800 drm/i915: add plane_config fetching infrastructure v2 Cc: Jesse Barnes <jbarnes@virtuousgeek.org> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=77511 Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
1 parent 168c02e commit 83f45fc

File tree

1 file changed

+28
-18
lines changed

1 file changed

+28
-18
lines changed

drivers/gpu/drm/drm_crtc.c

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,6 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
515515
if (ret)
516516
goto out;
517517

518-
/* Grab the idr reference. */
519-
drm_framebuffer_reference(fb);
520-
521518
dev->mode_config.num_fb++;
522519
list_add(&fb->head, &dev->mode_config.fb_list);
523520
out:
@@ -527,10 +524,34 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
527524
}
528525
EXPORT_SYMBOL(drm_framebuffer_init);
529526

527+
/* dev->mode_config.fb_lock must be held! */
528+
static void __drm_framebuffer_unregister(struct drm_device *dev,
529+
struct drm_framebuffer *fb)
530+
{
531+
mutex_lock(&dev->mode_config.idr_mutex);
532+
idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
533+
mutex_unlock(&dev->mode_config.idr_mutex);
534+
535+
fb->base.id = 0;
536+
}
537+
530538
static void drm_framebuffer_free(struct kref *kref)
531539
{
532540
struct drm_framebuffer *fb =
533541
container_of(kref, struct drm_framebuffer, refcount);
542+
struct drm_device *dev = fb->dev;
543+
544+
/*
545+
* The lookup idr holds a weak reference, which has not necessarily been
546+
* removed at this point. Check for that.
547+
*/
548+
mutex_lock(&dev->mode_config.fb_lock);
549+
if (fb->base.id) {
550+
/* Mark fb as reaped and drop idr ref. */
551+
__drm_framebuffer_unregister(dev, fb);
552+
}
553+
mutex_unlock(&dev->mode_config.fb_lock);
554+
534555
fb->funcs->destroy(fb);
535556
}
536557

@@ -567,8 +588,10 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
567588

568589
mutex_lock(&dev->mode_config.fb_lock);
569590
fb = __drm_framebuffer_lookup(dev, id);
570-
if (fb)
571-
drm_framebuffer_reference(fb);
591+
if (fb) {
592+
if (!kref_get_unless_zero(&fb->refcount))
593+
fb = NULL;
594+
}
572595
mutex_unlock(&dev->mode_config.fb_lock);
573596

574597
return fb;
@@ -612,19 +635,6 @@ static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
612635
kref_put(&fb->refcount, drm_framebuffer_free_bug);
613636
}
614637

615-
/* dev->mode_config.fb_lock must be held! */
616-
static void __drm_framebuffer_unregister(struct drm_device *dev,
617-
struct drm_framebuffer *fb)
618-
{
619-
mutex_lock(&dev->mode_config.idr_mutex);
620-
idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
621-
mutex_unlock(&dev->mode_config.idr_mutex);
622-
623-
fb->base.id = 0;
624-
625-
__drm_framebuffer_unreference(fb);
626-
}
627-
628638
/**
629639
* drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
630640
* @fb: fb to unregister

0 commit comments

Comments
 (0)