Commit de5dcfbc authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Kleber Sacilotto de Souza

fb: fix lost console when the user unplugs a USB adapter

BugLink: https://bugs.launchpad.net/bugs/1792419

commit 8c5b0442 upstream.

I have a USB display adapter using the udlfb driver and I use it on an ARM
board that doesn't have any graphics card. When I plug the adapter in, the
console is properly displayed, however when I unplug and re-plug the
adapter, the console is not displayed and I can't access it until I reboot
the board.

The reason is this:
When the adapter is unplugged, dlfb_usb_disconnect calls
unlink_framebuffer, then it waits until the reference count drops to zero
and then it deallocates the framebuffer. However, the console that is
attached to the framebuffer device keeps the reference count non-zero, so
the framebuffer device is never destroyed. When the USB adapter is plugged
again, it creates a new device /dev/fb1 and the console is not attached to
it.

This patch fixes the bug by unbinding the console from unlink_framebuffer.
The code to unbind the console is moved from do_unregister_framebuffer to
a function unbind_console. When the console is unbound, the reference
count drops to zero and the udlfb driver frees the framebuffer. When the
adapter is plugged back, a new framebuffer is created and the console is
attached to it.
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Bernie Thompson <bernie@plugable.com>
Cc: Ladislav Michl <ladis@linux-mips.org>
Cc: stable@vger.kernel.org
[b.zolnierkie: preserve old behavior for do_unregister_framebuffer()]
Signed-off-by: default avatarBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent d36e2947
...@@ -1687,12 +1687,12 @@ static int do_register_framebuffer(struct fb_info *fb_info) ...@@ -1687,12 +1687,12 @@ static int do_register_framebuffer(struct fb_info *fb_info)
return 0; return 0;
} }
static int do_unregister_framebuffer(struct fb_info *fb_info) static int unbind_console(struct fb_info *fb_info)
{ {
struct fb_event event; struct fb_event event;
int i, ret = 0; int ret;
int i = fb_info->node;
i = fb_info->node;
if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
return -EINVAL; return -EINVAL;
...@@ -1707,17 +1707,29 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) ...@@ -1707,17 +1707,29 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
unlock_fb_info(fb_info); unlock_fb_info(fb_info);
console_unlock(); console_unlock();
return ret;
}
static int __unlink_framebuffer(struct fb_info *fb_info);
static int do_unregister_framebuffer(struct fb_info *fb_info)
{
struct fb_event event;
int ret;
ret = unbind_console(fb_info);
if (ret) if (ret)
return -EINVAL; return -EINVAL;
pm_vt_switch_unregister(fb_info->dev); pm_vt_switch_unregister(fb_info->dev);
unlink_framebuffer(fb_info); __unlink_framebuffer(fb_info);
if (fb_info->pixmap.addr && if (fb_info->pixmap.addr &&
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr); kfree(fb_info->pixmap.addr);
fb_destroy_modelist(&fb_info->modelist); fb_destroy_modelist(&fb_info->modelist);
registered_fb[i] = NULL; registered_fb[fb_info->node] = NULL;
num_registered_fb--; num_registered_fb--;
fb_cleanup_device(fb_info); fb_cleanup_device(fb_info);
event.info = fb_info; event.info = fb_info;
...@@ -1730,7 +1742,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) ...@@ -1730,7 +1742,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
return 0; return 0;
} }
int unlink_framebuffer(struct fb_info *fb_info) static int __unlink_framebuffer(struct fb_info *fb_info)
{ {
int i; int i;
...@@ -1742,6 +1754,20 @@ int unlink_framebuffer(struct fb_info *fb_info) ...@@ -1742,6 +1754,20 @@ int unlink_framebuffer(struct fb_info *fb_info)
device_destroy(fb_class, MKDEV(FB_MAJOR, i)); device_destroy(fb_class, MKDEV(FB_MAJOR, i));
fb_info->dev = NULL; fb_info->dev = NULL;
} }
return 0;
}
int unlink_framebuffer(struct fb_info *fb_info)
{
int ret;
ret = __unlink_framebuffer(fb_info);
if (ret)
return ret;
unbind_console(fb_info);
return 0; return 0;
} }
EXPORT_SYMBOL(unlink_framebuffer); EXPORT_SYMBOL(unlink_framebuffer);
......
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