]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.3.8/udlfb-fix-hcd_buffer_free-panic-on-unplug-replug.patch
5.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.3.8 / udlfb-fix-hcd_buffer_free-panic-on-unplug-replug.patch
1 From 8d21547d3c9c3bc653261f26d554cfabc4a083de Mon Sep 17 00:00:00 2001
2 From: Bernie Thompson <bernie@plugable.com>
3 Date: Thu, 1 Mar 2012 17:35:48 -0800
4 Subject: udlfb: fix hcd_buffer_free panic on unplug/replug
5
6 From: Bernie Thompson <bernie@plugable.com>
7
8 commit 8d21547d3c9c3bc653261f26d554cfabc4a083de upstream.
9
10 Fix race conditions with unplug/replug behavior, in particular
11 take care not to hold up USB probe/disconnect for long-running
12 framebuffer operations and rely on usb to handle teardown.
13
14 Fix for kernel panic reported with new F17 multiseat support.
15
16 Reported-by: Kay Sievers <kay.sievers@vrfy.org>
17 Signed-off-by: Bernie Thompson <bernie@plugable.com>
18 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
19
20 ---
21 drivers/video/udlfb.c | 146 +++++++++++++++++++++++++++-----------------------
22 include/video/udlfb.h | 1
23 2 files changed, 81 insertions(+), 66 deletions(-)
24
25 --- a/drivers/video/udlfb.c
26 +++ b/drivers/video/udlfb.c
27 @@ -918,10 +918,6 @@ static void dlfb_free(struct kref *kref)
28 {
29 struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref);
30
31 - /* this function will wait for all in-flight urbs to complete */
32 - if (dev->urbs.count > 0)
33 - dlfb_free_urb_list(dev);
34 -
35 if (dev->backing_buffer)
36 vfree(dev->backing_buffer);
37
38 @@ -940,35 +936,42 @@ static void dlfb_release_urb_work(struct
39 up(&unode->dev->urbs.limit_sem);
40 }
41
42 -static void dlfb_free_framebuffer_work(struct work_struct *work)
43 +static void dlfb_free_framebuffer(struct dlfb_data *dev)
44 {
45 - struct dlfb_data *dev = container_of(work, struct dlfb_data,
46 - free_framebuffer_work.work);
47 struct fb_info *info = dev->info;
48 - int node = info->node;
49
50 - unregister_framebuffer(info);
51 + if (info) {
52 + int node = info->node;
53
54 - if (info->cmap.len != 0)
55 - fb_dealloc_cmap(&info->cmap);
56 - if (info->monspecs.modedb)
57 - fb_destroy_modedb(info->monspecs.modedb);
58 - if (info->screen_base)
59 - vfree(info->screen_base);
60 + unregister_framebuffer(info);
61
62 - fb_destroy_modelist(&info->modelist);
63 + if (info->cmap.len != 0)
64 + fb_dealloc_cmap(&info->cmap);
65 + if (info->monspecs.modedb)
66 + fb_destroy_modedb(info->monspecs.modedb);
67 + if (info->screen_base)
68 + vfree(info->screen_base);
69 +
70 + fb_destroy_modelist(&info->modelist);
71
72 - dev->info = 0;
73 + dev->info = NULL;
74
75 - /* Assume info structure is freed after this point */
76 - framebuffer_release(info);
77 + /* Assume info structure is freed after this point */
78 + framebuffer_release(info);
79
80 - pr_warn("fb_info for /dev/fb%d has been freed\n", node);
81 + pr_warn("fb_info for /dev/fb%d has been freed\n", node);
82 + }
83
84 /* ref taken in probe() as part of registering framebfufer */
85 kref_put(&dev->kref, dlfb_free);
86 }
87
88 +static void dlfb_free_framebuffer_work(struct work_struct *work)
89 +{
90 + struct dlfb_data *dev = container_of(work, struct dlfb_data,
91 + free_framebuffer_work.work);
92 + dlfb_free_framebuffer(dev);
93 +}
94 /*
95 * Assumes caller is holding info->lock mutex (for open and release at least)
96 */
97 @@ -1570,14 +1573,15 @@ success:
98 kfree(buf);
99 return true;
100 }
101 +
102 +static void dlfb_init_framebuffer_work(struct work_struct *work);
103 +
104 static int dlfb_usb_probe(struct usb_interface *interface,
105 const struct usb_device_id *id)
106 {
107 struct usb_device *usbdev;
108 struct dlfb_data *dev = 0;
109 - struct fb_info *info = 0;
110 int retval = -ENOMEM;
111 - int i;
112
113 /* usb initialization */
114
115 @@ -1589,9 +1593,7 @@ static int dlfb_usb_probe(struct usb_int
116 goto error;
117 }
118
119 - /* we need to wait for both usb and fbdev to spin down on disconnect */
120 kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */
121 - kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
122
123 dev->udev = usbdev;
124 dev->gdev = &usbdev->dev; /* our generic struct device * */
125 @@ -1619,10 +1621,39 @@ static int dlfb_usb_probe(struct usb_int
126 goto error;
127 }
128
129 + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
130 +
131 /* We don't register a new USB class. Our client interface is fbdev */
132
133 + /* Workitem keep things fast & simple during USB enumeration */
134 + INIT_DELAYED_WORK(&dev->init_framebuffer_work,
135 + dlfb_init_framebuffer_work);
136 + schedule_delayed_work(&dev->init_framebuffer_work, 0);
137 +
138 + return 0;
139 +
140 +error:
141 + if (dev) {
142 +
143 + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */
144 + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */
145 +
146 + /* dev has been deallocated. Do not dereference */
147 + }
148 +
149 + return retval;
150 +}
151 +
152 +static void dlfb_init_framebuffer_work(struct work_struct *work)
153 +{
154 + struct dlfb_data *dev = container_of(work, struct dlfb_data,
155 + init_framebuffer_work.work);
156 + struct fb_info *info;
157 + int retval;
158 + int i;
159 +
160 /* allocates framebuffer driver structure, not framebuffer memory */
161 - info = framebuffer_alloc(0, &interface->dev);
162 + info = framebuffer_alloc(0, dev->gdev);
163 if (!info) {
164 retval = -ENOMEM;
165 pr_err("framebuffer_alloc failed\n");
166 @@ -1668,15 +1699,13 @@ static int dlfb_usb_probe(struct usb_int
167 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) {
168 retval = device_create_file(info->dev, &fb_device_attrs[i]);
169 if (retval) {
170 - pr_err("device_create_file failed %d\n", retval);
171 - goto err_del_attrs;
172 + pr_warn("device_create_file failed %d\n", retval);
173 }
174 }
175
176 retval = device_create_bin_file(info->dev, &edid_attr);
177 if (retval) {
178 - pr_err("device_create_bin_file failed %d\n", retval);
179 - goto err_del_attrs;
180 + pr_warn("device_create_bin_file failed %d\n", retval);
181 }
182
183 pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution."
184 @@ -1684,38 +1713,10 @@ static int dlfb_usb_probe(struct usb_int
185 info->var.xres, info->var.yres,
186 ((dev->backing_buffer) ?
187 info->fix.smem_len * 2 : info->fix.smem_len) >> 10);
188 - return 0;
189 -
190 -err_del_attrs:
191 - for (i -= 1; i >= 0; i--)
192 - device_remove_file(info->dev, &fb_device_attrs[i]);
193 + return;
194
195 error:
196 - if (dev) {
197 -
198 - if (info) {
199 - if (info->cmap.len != 0)
200 - fb_dealloc_cmap(&info->cmap);
201 - if (info->monspecs.modedb)
202 - fb_destroy_modedb(info->monspecs.modedb);
203 - if (info->screen_base)
204 - vfree(info->screen_base);
205 -
206 - fb_destroy_modelist(&info->modelist);
207 -
208 - framebuffer_release(info);
209 - }
210 -
211 - if (dev->backing_buffer)
212 - vfree(dev->backing_buffer);
213 -
214 - kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */
215 - kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */
216 -
217 - /* dev has been deallocated. Do not dereference */
218 - }
219 -
220 - return retval;
221 + dlfb_free_framebuffer(dev);
222 }
223
224 static void dlfb_usb_disconnect(struct usb_interface *interface)
225 @@ -1735,12 +1736,24 @@ static void dlfb_usb_disconnect(struct u
226 /* When non-active we'll update virtual framebuffer, but no new urbs */
227 atomic_set(&dev->usb_active, 0);
228
229 - /* remove udlfb's sysfs interfaces */
230 - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
231 - device_remove_file(info->dev, &fb_device_attrs[i]);
232 - device_remove_bin_file(info->dev, &edid_attr);
233 - unlink_framebuffer(info);
234 + /* this function will wait for all in-flight urbs to complete */
235 + dlfb_free_urb_list(dev);
236 +
237 + if (info) {
238 +
239 + /* remove udlfb's sysfs interfaces */
240 + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
241 + device_remove_file(info->dev, &fb_device_attrs[i]);
242 + device_remove_bin_file(info->dev, &edid_attr);
243 +
244 + /* it's safe to uncomment next line if your kernel
245 + doesn't yet have this function exported */
246 + unlink_framebuffer(info);
247 + }
248 +
249 usb_set_intfdata(interface, NULL);
250 + dev->udev = NULL;
251 + dev->gdev = NULL;
252
253 /* if clients still have us open, will be freed on last close */
254 if (dev->fb_count == 0)
255 @@ -1806,12 +1819,12 @@ static void dlfb_free_urb_list(struct dl
256 int ret;
257 unsigned long flags;
258
259 - pr_notice("Waiting for completes and freeing all render urbs\n");
260 + pr_notice("Freeing all render urbs\n");
261
262 /* keep waiting and freeing, until we've got 'em all */
263 while (count--) {
264
265 - /* Getting interrupted means a leak, but ok at shutdown*/
266 + /* Getting interrupted means a leak, but ok at disconnect */
267 ret = down_interruptible(&dev->urbs.limit_sem);
268 if (ret)
269 break;
270 @@ -1833,6 +1846,7 @@ static void dlfb_free_urb_list(struct dl
271 kfree(node);
272 }
273
274 + dev->urbs.count = 0;
275 }
276
277 static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size)
278 --- a/include/video/udlfb.h
279 +++ b/include/video/udlfb.h
280 @@ -41,6 +41,7 @@ struct dlfb_data {
281 char *backing_buffer;
282 int fb_count;
283 bool virtualized; /* true when physical usb device not present */
284 + struct delayed_work init_framebuffer_work;
285 struct delayed_work free_framebuffer_work;
286 atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */
287 atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */