Commit 6e1490cf authored by Noralf Trønnes's avatar Noralf Trønnes

drm/fb-helper: generic: Fix setup error path

If register_framebuffer() fails during fbdev setup we will leak the
framebuffer, the GEM buffer and the shadow buffer for defio. This is
because drm_fb_helper_fbdev_setup() just calls drm_fb_helper_fini() on
error not taking into account that register_framebuffer() can fail.

Since the generic emulation uses DRM client for its framebuffer and
backing buffer in addition to a shadow buffer, it's necessary to open code
drm_fb_helper_fbdev_setup() to properly handle the error path.

Error cleanup is removed from .fb_probe and is handled by one function for
all paths.

Fixes: 9060d7f4 ("drm/fb-helper: Finish the generic fbdev emulation")
Reported-by: default avatarPeter Wu <peter@lekensteyn.nl>
Signed-off-by: default avatarNoralf Trønnes <noralf@tronnes.org>
Acked-by: default avatarGerd Hoffmann <kraxel@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190105181846.26495-1-noralf@tronnes.org
parent cb66c6da
...@@ -2961,18 +2961,16 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user) ...@@ -2961,18 +2961,16 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
return 0; return 0;
} }
/* static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper)
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
* unregister_framebuffer() or fb_release().
*/
static void drm_fbdev_fb_destroy(struct fb_info *info)
{ {
struct drm_fb_helper *fb_helper = info->par;
struct fb_info *fbi = fb_helper->fbdev; struct fb_info *fbi = fb_helper->fbdev;
struct fb_ops *fbops = NULL; struct fb_ops *fbops = NULL;
void *shadow = NULL; void *shadow = NULL;
if (fbi->fbdefio) { if (!fb_helper->dev)
return;
if (fbi && fbi->fbdefio) {
fb_deferred_io_cleanup(fbi); fb_deferred_io_cleanup(fbi);
shadow = fbi->screen_buffer; shadow = fbi->screen_buffer;
fbops = fbi->fbops; fbops = fbi->fbops;
...@@ -2986,6 +2984,12 @@ static void drm_fbdev_fb_destroy(struct fb_info *info) ...@@ -2986,6 +2984,12 @@ static void drm_fbdev_fb_destroy(struct fb_info *info)
} }
drm_client_framebuffer_delete(fb_helper->buffer); drm_client_framebuffer_delete(fb_helper->buffer);
}
static void drm_fbdev_release(struct drm_fb_helper *fb_helper)
{
drm_fbdev_cleanup(fb_helper);
/* /*
* FIXME: * FIXME:
* Remove conditional when all CMA drivers have been moved over to using * Remove conditional when all CMA drivers have been moved over to using
...@@ -2997,6 +3001,15 @@ static void drm_fbdev_fb_destroy(struct fb_info *info) ...@@ -2997,6 +3001,15 @@ static void drm_fbdev_fb_destroy(struct fb_info *info)
} }
} }
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
* unregister_framebuffer() or fb_release().
*/
static void drm_fbdev_fb_destroy(struct fb_info *info)
{
drm_fbdev_release(info->par);
}
static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{ {
struct drm_fb_helper *fb_helper = info->par; struct drm_fb_helper *fb_helper = info->par;
...@@ -3047,7 +3060,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -3047,7 +3060,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
struct fb_info *fbi; struct fb_info *fbi;
u32 format; u32 format;
int ret;
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height, sizes->surface_width, sizes->surface_height,
...@@ -3064,10 +3076,8 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -3064,10 +3076,8 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
fb = buffer->fb; fb = buffer->fb;
fbi = drm_fb_helper_alloc_fbi(fb_helper); fbi = drm_fb_helper_alloc_fbi(fb_helper);
if (IS_ERR(fbi)) { if (IS_ERR(fbi))
ret = PTR_ERR(fbi); return PTR_ERR(fbi);
goto err_free_buffer;
}
fbi->par = fb_helper; fbi->par = fb_helper;
fbi->fbops = &drm_fbdev_fb_ops; fbi->fbops = &drm_fbdev_fb_ops;
...@@ -3098,8 +3108,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -3098,8 +3108,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
if (!fbops || !shadow) { if (!fbops || !shadow) {
kfree(fbops); kfree(fbops);
vfree(shadow); vfree(shadow);
ret = -ENOMEM; return -ENOMEM;
goto err_fb_info_destroy;
} }
*fbops = *fbi->fbops; *fbops = *fbi->fbops;
...@@ -3111,13 +3120,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -3111,13 +3120,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
} }
return 0; return 0;
err_fb_info_destroy:
drm_fb_helper_fini(fb_helper);
err_free_buffer:
drm_client_framebuffer_delete(buffer);
return ret;
} }
EXPORT_SYMBOL(drm_fb_helper_generic_probe); EXPORT_SYMBOL(drm_fb_helper_generic_probe);
...@@ -3129,18 +3131,11 @@ static void drm_fbdev_client_unregister(struct drm_client_dev *client) ...@@ -3129,18 +3131,11 @@ static void drm_fbdev_client_unregister(struct drm_client_dev *client)
{ {
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
if (fb_helper->fbdev) { if (fb_helper->fbdev)
drm_fb_helper_unregister_fbi(fb_helper);
/* drm_fbdev_fb_destroy() takes care of cleanup */ /* drm_fbdev_fb_destroy() takes care of cleanup */
return; drm_fb_helper_unregister_fbi(fb_helper);
} else
drm_fbdev_release(fb_helper);
/* Did drm_fb_helper_fbdev_setup() run? */
if (fb_helper->dev)
drm_fb_helper_fini(fb_helper);
drm_client_release(client);
kfree(fb_helper);
} }
static int drm_fbdev_client_restore(struct drm_client_dev *client) static int drm_fbdev_client_restore(struct drm_client_dev *client)
...@@ -3158,7 +3153,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) ...@@ -3158,7 +3153,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
struct drm_device *dev = client->dev; struct drm_device *dev = client->dev;
int ret; int ret;
/* If drm_fb_helper_fbdev_setup() failed, we only try once */ /* Setup is not retried if it has failed */
if (!fb_helper->dev && fb_helper->funcs) if (!fb_helper->dev && fb_helper->funcs)
return 0; return 0;
...@@ -3170,15 +3165,34 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) ...@@ -3170,15 +3165,34 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
return 0; return 0;
} }
ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_helper_generic_funcs, drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs);
fb_helper->preferred_bpp, 0);
if (ret) { ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector);
if (ret)
goto err;
ret = drm_fb_helper_single_add_all_connectors(fb_helper);
if (ret)
goto err_cleanup;
if (!drm_drv_uses_atomic_modeset(dev))
drm_helper_disable_unused_functions(dev);
ret = drm_fb_helper_initial_config(fb_helper, fb_helper->preferred_bpp);
if (ret)
goto err_cleanup;
return 0;
err_cleanup:
drm_fbdev_cleanup(fb_helper);
err:
fb_helper->dev = NULL; fb_helper->dev = NULL;
fb_helper->fbdev = NULL; fb_helper->fbdev = NULL;
return ret;
}
return 0; DRM_DEV_ERROR(dev->dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret);
return ret;
} }
static const struct drm_client_funcs drm_fbdev_client_funcs = { static const struct drm_client_funcs drm_fbdev_client_funcs = {
...@@ -3237,6 +3251,10 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) ...@@ -3237,6 +3251,10 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
drm_client_add(&fb_helper->client); drm_client_add(&fb_helper->client);
if (!preferred_bpp)
preferred_bpp = dev->mode_config.preferred_depth;
if (!preferred_bpp)
preferred_bpp = 32;
fb_helper->preferred_bpp = preferred_bpp; fb_helper->preferred_bpp = preferred_bpp;
ret = drm_fbdev_client_hotplug(&fb_helper->client); ret = drm_fbdev_client_hotplug(&fb_helper->client);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment