cf9048c
From 92a9c19a89af2ca219fbb040a0059f414a4b7223 Mon Sep 17 00:00:00 2001
cf9048c
From: Kay Sievers <kay.sievers@vrfy.org>
cf9048c
Date: Sat, 28 Jan 2012 19:57:46 +0000
cf9048c
Subject: [PATCH] udlfb: remove sysfs framebuffer device with USB
cf9048c
 .disconnect()
cf9048c
cf9048c
The USB graphics card driver delays the unregistering of the framebuffer
cf9048c
device to a workqueue, which breaks the userspace visible remove uevent
cf9048c
sequence. Recent userspace tools started to support USB graphics card
cf9048c
hotplug out-of-the-box and rely on proper events sent by the kernel.
cf9048c
cf9048c
The framebuffer device is a direct child of the USB interface which is
cf9048c
removed immediately after the USB .disconnect() callback. But the fb device
cf9048c
in /sys stays around until its final cleanup, at a time where all the parent
cf9048c
devices have been removed already.
cf9048c
cf9048c
To work around that, we remove the sysfs fb device directly in the USB
cf9048c
.disconnect() callback and leave only the cleanup of the internal fb
cf9048c
data to the delayed work.
cf9048c
cf9048c
Before:
cf9048c
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
cf9048c
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
cf9048c
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb0 (graphics)
cf9048c
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
cf9048c
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
cf9048c
 remove   /2-1.2:1.0/graphics/fb0 (graphics)
cf9048c
cf9048c
After:
cf9048c
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
cf9048c
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
cf9048c
 add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics)
cf9048c
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/graphics/fb1 (graphics)
cf9048c
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
cf9048c
 remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
cf9048c
cf9048c
Cc: stable@vger.kernel.org
cf9048c
Tested-by: Bernie Thompson <bernie@plugable.com>
cf9048c
Acked-by: Bernie Thompson <bernie@plugable.com>
cf9048c
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
cf9048c
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
cf9048c
---
cf9048c
 drivers/video/fbmem.c |   18 +++++++++++++++++-
cf9048c
 drivers/video/udlfb.c |    2 +-
cf9048c
 include/linux/fb.h    |    1 +
cf9048c
 3 files changed, 19 insertions(+), 2 deletions(-)
cf9048c
cf9048c
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
cf9048c
index ac9141b..c6ce416 100644
cf9048c
--- a/drivers/video/fbmem.c
cf9048c
+++ b/drivers/video/fbmem.c
cf9048c
@@ -1665,6 +1665,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
cf9048c
 	if (ret)
cf9048c
 		return -EINVAL;
cf9048c
 
cf9048c
+	unlink_framebuffer(fb_info);
cf9048c
 	if (fb_info->pixmap.addr &&
cf9048c
 	    (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
cf9048c
 		kfree(fb_info->pixmap.addr);
cf9048c
@@ -1672,7 +1673,6 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
cf9048c
 	registered_fb[i] = NULL;
cf9048c
 	num_registered_fb--;
cf9048c
 	fb_cleanup_device(fb_info);
cf9048c
-	device_destroy(fb_class, MKDEV(FB_MAJOR, i));
cf9048c
 	event.info = fb_info;
cf9048c
 	fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
cf9048c
 
cf9048c
@@ -1681,6 +1681,22 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
cf9048c
 	return 0;
cf9048c
 }
cf9048c
 
cf9048c
+int unlink_framebuffer(struct fb_info *fb_info)
cf9048c
+{
cf9048c
+	int i;
cf9048c
+
cf9048c
+	i = fb_info->node;
cf9048c
+	if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
cf9048c
+		return -EINVAL;
cf9048c
+
cf9048c
+	if (fb_info->dev) {
cf9048c
+		device_destroy(fb_class, MKDEV(FB_MAJOR, i));
cf9048c
+		fb_info->dev = NULL;
cf9048c
+	}
cf9048c
+	return 0;
cf9048c
+}
cf9048c
+EXPORT_SYMBOL(unlink_framebuffer);
cf9048c
+
cf9048c
 void remove_conflicting_framebuffers(struct apertures_struct *a,
cf9048c
 				     const char *name, bool primary)
cf9048c
 {
cf9048c
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c
cf9048c
index a197731..a40c05e 100644
cf9048c
--- a/drivers/video/udlfb.c
cf9048c
+++ b/drivers/video/udlfb.c
cf9048c
@@ -1739,7 +1739,7 @@ static void dlfb_usb_disconnect(struct usb_interface *interface)
cf9048c
 	for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
cf9048c
 		device_remove_file(info->dev, &fb_device_attrs[i]);
cf9048c
 	device_remove_bin_file(info->dev, &edid_attr);
cf9048c
-
cf9048c
+	unlink_framebuffer(info);
cf9048c
 	usb_set_intfdata(interface, NULL);
cf9048c
 
cf9048c
 	/* if clients still have us open, will be freed on last close */
cf9048c
diff --git a/include/linux/fb.h b/include/linux/fb.h
cf9048c
index c18122f..a395b8c 100644
cf9048c
--- a/include/linux/fb.h
cf9048c
+++ b/include/linux/fb.h
cf9048c
@@ -1003,6 +1003,7 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
cf9048c
 /* drivers/video/fbmem.c */
cf9048c
 extern int register_framebuffer(struct fb_info *fb_info);
cf9048c
 extern int unregister_framebuffer(struct fb_info *fb_info);
cf9048c
+extern int unlink_framebuffer(struct fb_info *fb_info);
cf9048c
 extern void remove_conflicting_framebuffers(struct apertures_struct *a,
cf9048c
 				const char *name, bool primary);
cf9048c
 extern int fb_prepare_logo(struct fb_info *fb_info, int rotate);
cf9048c
-- 
cf9048c
1.7.6.5
cf9048c