[PATCH] fb: fix lost console when the user unplugs a USB adapter
Mikulas Patocka
mpatocka at redhat.com
Tue Jul 3 17:18:57 UTC 2018
On Tue, 3 Jul 2018, Bartlomiej Zolnierkiewicz wrote:
>
> Hi,
>
> On Sunday, June 03, 2018 11:46:29 AM Mikulas Patocka wrote:
> > 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: Mikulas Patocka <mpatocka at redhat.com>
> > Cc: stable at vger.kernel.org
>
> After this change unbind_console() will be called twice in the standard
> framebuffer unregister path:
>
> - first time, directly by do_unregister_framebuffer()
>
> - second time, indirectly by do_unregister_framebuffer()->unlink_framebuffer()
>
> This doesn't look correctly.
unbind_console calls the FB_EVENT_FB_UNBIND notifier, FB_EVENT_FB_UNBIND
goes to the function fbcon_fb_unbind and fbcon_fb_unbind checks if the
console is bound to the framebuffer for which unbind is requested. So a
double call won't cause any trouble.
> Also why can't udlfb just use unregister_framebuffer() like all other
> drivers (it uses unlink_framebuffer() and it is the only user of this
> helper)?
It uses unregister_framebuffer() - but - unregister_framebuffer() may only
be called when the open count of the framebuffer is zero. So, the udlfb
driver waits until the open count drops to zero and then calls
unregister_framebuffer().
But the console subsystem keeps the framebuffer open - which means that if
user use unplugs the USB adapter, the open count won't drop to zero
(because the console is bound to it) - which means that
unregister_framebuffer() will not be called.
You must unbind the console before calling unregister_framebuffer(). The
PCI framebuffer drivers don't have this problem because the user is not
expected to just unplug the PCI card while it is being used by the
console.
Mikulas
> > ---
> > drivers/video/fbdev/core/fbmem.c | 21 +++++++++++++++++----
> > 1 file changed, 17 insertions(+), 4 deletions(-)
> >
> > Index: linux-4.16.12/drivers/video/fbdev/core/fbmem.c
> > ===================================================================
> > --- linux-4.16.12.orig/drivers/video/fbdev/core/fbmem.c 2018-05-26 06:13:20.000000000 +0200
> > +++ linux-4.16.12/drivers/video/fbdev/core/fbmem.c 2018-05-26 06:13:20.000000000 +0200
> > @@ -1805,12 +1805,12 @@ static int do_register_framebuffer(struc
> > 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;
> > - 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)
> > return -EINVAL;
> >
> > @@ -1825,6 +1825,16 @@ static int do_unregister_framebuffer(str
> > unlock_fb_info(fb_info);
> > console_unlock();
> >
> > + return ret;
> > +}
> > +
> > +static int do_unregister_framebuffer(struct fb_info *fb_info)
> > +{
> > + struct fb_event event;
> > + int ret;
> > +
> > + ret = unbind_console(fb_info);
> > +
> > if (ret)
> > return -EINVAL;
> >
> > @@ -1835,7 +1845,7 @@ static int do_unregister_framebuffer(str
> > (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
> > kfree(fb_info->pixmap.addr);
> > fb_destroy_modelist(&fb_info->modelist);
> > - registered_fb[i] = NULL;
> > + registered_fb[fb_info->node] = NULL;
> > num_registered_fb--;
> > fb_cleanup_device(fb_info);
> > event.info = fb_info;
> > @@ -1860,6 +1870,9 @@ int unlink_framebuffer(struct fb_info *f
> > device_destroy(fb_class, MKDEV(FB_MAJOR, i));
> > fb_info->dev = NULL;
> > }
> > +
> > + unbind_console(fb_info);
> > +
> > return 0;
> > }
> > EXPORT_SYMBOL(unlink_framebuffer);
>
> Best regards,
> --
> Bartlomiej Zolnierkiewicz
> Samsung R&D Institute Poland
> Samsung Electronics
>
More information about the dri-devel
mailing list