提交 5cef29aa 编写于 作者: D Daniel Vetter 提交者: Dave Airlie

drm: fix fb leak in setcrtc

Drivers are allowed (actually have to) disable unrelated crtcs in
their ->set_config callback (when we steal all the connectors from
that crtc). If they do that they'll clear crtc->fb to NULL.

Which results in a refcount leak, since the drm core is keeping track
of that reference.

To fix this track the old fb of all crtcs and adjust references for
all of them. Of course, since we only hold an additional reference for
the fb for the current crtc we need to increase refcounts before we
drop the old one.

This approach has the benefit that it inches us a bit closer to an
atomic modeset world, where we want to update the config of all crtcs
in one step.

This regression has been introduce in the framebuffer refcount
conversion, specifically in

commit b0d12325
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date:   Tue Dec 11 01:07:12 2012 +0100

    drm: refcounting for crtc framebuffers
Reported-by: NRussell King <linux@arm.linux.org.uk>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: stable@vger.kernel.org
Signed-off-by: NDaniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: NDave Airlie <airlied@redhat.com>
上级 cc85e121
...@@ -1972,21 +1972,31 @@ int drm_mode_setplane(struct drm_device *dev, void *data, ...@@ -1972,21 +1972,31 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
int drm_mode_set_config_internal(struct drm_mode_set *set) int drm_mode_set_config_internal(struct drm_mode_set *set)
{ {
struct drm_crtc *crtc = set->crtc; struct drm_crtc *crtc = set->crtc;
struct drm_framebuffer *fb, *old_fb; struct drm_framebuffer *fb;
struct drm_crtc *tmp;
int ret; int ret;
old_fb = crtc->fb; /*
* NOTE: ->set_config can also disable other crtcs (if we steal all
* connectors from it), hence we need to refcount the fbs across all
* crtcs. Atomic modeset will have saner semantics ...
*/
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
tmp->old_fb = tmp->fb;
fb = set->fb; fb = set->fb;
ret = crtc->funcs->set_config(set); ret = crtc->funcs->set_config(set);
if (ret == 0) { if (ret == 0) {
/* crtc->fb must be updated by ->set_config, enforces this. */ /* crtc->fb must be updated by ->set_config, enforces this. */
WARN_ON(fb != crtc->fb); WARN_ON(fb != crtc->fb);
}
if (old_fb) list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
drm_framebuffer_unreference(old_fb); if (tmp->fb)
if (fb) drm_framebuffer_reference(tmp->fb);
drm_framebuffer_reference(fb); if (tmp->old_fb)
drm_framebuffer_unreference(tmp->old_fb);
} }
return ret; return ret;
......
...@@ -409,6 +409,10 @@ struct drm_crtc { ...@@ -409,6 +409,10 @@ struct drm_crtc {
/* framebuffer the connector is currently bound to */ /* framebuffer the connector is currently bound to */
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
/* Temporary tracking of the old fb while a modeset is ongoing. Used
* by drm_mode_set_config_internal to implement correct refcounting. */
struct drm_framebuffer *old_fb;
bool enabled; bool enabled;
/* Requested mode from modesetting. */ /* Requested mode from modesetting. */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册