aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2014-08-06 03:10:18 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2014-08-06 04:41:13 -0400
commit83f45fc360c8e16a330474860ebda872d1384c8c (patch)
treebaf51e3ed1e4c24493b8347ac9827fb321002434
parent168c02ec06f891990617cee2abbba70858c071e7 (diff)
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 46f297fb83d4f9a6f6891964beb184664341a28b 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>
-rw-r--r--drivers/gpu/drm/drm_crtc.c46
1 files changed, 28 insertions, 18 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index fa2be249999c..33ff631c8d23 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -515,9 +515,6 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
515 if (ret) 515 if (ret)
516 goto out; 516 goto out;
517 517
518 /* Grab the idr reference. */
519 drm_framebuffer_reference(fb);
520
521 dev->mode_config.num_fb++; 518 dev->mode_config.num_fb++;
522 list_add(&fb->head, &dev->mode_config.fb_list); 519 list_add(&fb->head, &dev->mode_config.fb_list);
523out: 520out:
@@ -527,10 +524,34 @@ out:
527} 524}
528EXPORT_SYMBOL(drm_framebuffer_init); 525EXPORT_SYMBOL(drm_framebuffer_init);
529 526
527/* dev->mode_config.fb_lock must be held! */
528static 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
530static void drm_framebuffer_free(struct kref *kref) 538static void drm_framebuffer_free(struct kref *kref)
531{ 539{
532 struct drm_framebuffer *fb = 540 struct drm_framebuffer *fb =
533 container_of(kref, struct drm_framebuffer, refcount); 541 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
534 fb->funcs->destroy(fb); 555 fb->funcs->destroy(fb);
535} 556}
536 557
@@ -567,8 +588,10 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
567 588
568 mutex_lock(&dev->mode_config.fb_lock); 589 mutex_lock(&dev->mode_config.fb_lock);
569 fb = __drm_framebuffer_lookup(dev, id); 590 fb = __drm_framebuffer_lookup(dev, id);
570 if (fb) 591 if (fb) {
571 drm_framebuffer_reference(fb); 592 if (!kref_get_unless_zero(&fb->refcount))
593 fb = NULL;
594 }
572 mutex_unlock(&dev->mode_config.fb_lock); 595 mutex_unlock(&dev->mode_config.fb_lock);
573 596
574 return fb; 597 return fb;
@@ -612,19 +635,6 @@ static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
612 kref_put(&fb->refcount, drm_framebuffer_free_bug); 635 kref_put(&fb->refcount, drm_framebuffer_free_bug);
613} 636}
614 637
615/* dev->mode_config.fb_lock must be held! */
616static 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
628/** 638/**
629 * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr 639 * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
630 * @fb: fb to unregister 640 * @fb: fb to unregister