Commit ee7d30aab7663d796c13f3d7e8e80068f922a334

Authored by Kay Sievers
Committed by Greg Kroah-Hartman
1 parent 3b6df5f6f4

udlfb: remove sysfs framebuffer device with USB .disconnect()

commit ce880cb860f36694d2cdebfac9e6ae18176fe4c4 upstream.

The USB graphics card driver delays the unregistering of the framebuffer
device to a workqueue, which breaks the userspace visible remove uevent
sequence. Recent userspace tools started to support USB graphics card
hotplug out-of-the-box and rely on proper events sent by the kernel.

The framebuffer device is a direct child of the USB interface which is
removed immediately after the USB .disconnect() callback. But the fb device
in /sys stays around until its final cleanup, at a time where all the parent
devices have been removed already.

To work around that, we remove the sysfs fb device directly in the USB
.disconnect() callback and leave only the cleanup of the internal fb
data to the delayed work.

Before:
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb0 (graphics)
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
 remove   /2-1.2:1.0/graphics/fb0 (graphics)

After:
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics)
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics)
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)

Tested-by: Bernie Thompson <bernie@plugable.com>
Acked-by: Bernie Thompson <bernie@plugable.com>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 3 changed files with 19 additions and 2 deletions Side-by-side Diff

drivers/video/fbmem.c
... ... @@ -1651,6 +1651,7 @@
1651 1651 if (ret)
1652 1652 return -EINVAL;
1653 1653  
  1654 + unlink_framebuffer(fb_info);
1654 1655 if (fb_info->pixmap.addr &&
1655 1656 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1656 1657 kfree(fb_info->pixmap.addr);
... ... @@ -1658,7 +1659,6 @@
1658 1659 registered_fb[i] = NULL;
1659 1660 num_registered_fb--;
1660 1661 fb_cleanup_device(fb_info);
1661   - device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1662 1662 event.info = fb_info;
1663 1663 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1664 1664  
... ... @@ -1666,6 +1666,22 @@
1666 1666 put_fb_info(fb_info);
1667 1667 return 0;
1668 1668 }
  1669 +
  1670 +int unlink_framebuffer(struct fb_info *fb_info)
  1671 +{
  1672 + int i;
  1673 +
  1674 + i = fb_info->node;
  1675 + if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
  1676 + return -EINVAL;
  1677 +
  1678 + if (fb_info->dev) {
  1679 + device_destroy(fb_class, MKDEV(FB_MAJOR, i));
  1680 + fb_info->dev = NULL;
  1681 + }
  1682 + return 0;
  1683 +}
  1684 +EXPORT_SYMBOL(unlink_framebuffer);
1669 1685  
1670 1686 void remove_conflicting_framebuffers(struct apertures_struct *a,
1671 1687 const char *name, bool primary)
drivers/video/udlfb.c
... ... @@ -1739,7 +1739,7 @@
1739 1739 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
1740 1740 device_remove_file(info->dev, &fb_device_attrs[i]);
1741 1741 device_remove_bin_file(info->dev, &edid_attr);
1742   -
  1742 + unlink_framebuffer(info);
1743 1743 usb_set_intfdata(interface, NULL);
1744 1744  
1745 1745 /* if clients still have us open, will be freed on last close */
... ... @@ -997,6 +997,7 @@
997 997 /* drivers/video/fbmem.c */
998 998 extern int register_framebuffer(struct fb_info *fb_info);
999 999 extern int unregister_framebuffer(struct fb_info *fb_info);
  1000 +extern int unlink_framebuffer(struct fb_info *fb_info);
1000 1001 extern void remove_conflicting_framebuffers(struct apertures_struct *a,
1001 1002 const char *name, bool primary);
1002 1003 extern int fb_prepare_logo(struct fb_info *fb_info, int rotate);