Commit ee7d30aab7663d796c13f3d7e8e80068f922a334
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 */ |
include/linux/fb.h
... | ... | @@ -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); |