Skip to content

Commit 2a40f8b

Browse files
tdzgregkh
authored andcommitted
fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info
[ Upstream commit 9ded47a ] Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an instance as part of initializing deferred I/O and remove it only after the final mapping has been closed. If the fb_info and the contained deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info to invalidate the mapping. Any access will then result in a SIGBUS signal. Fixes a long-standing problem, where a device hot-unplug happens while user space still has an active mapping of the graphics memory. The hot- unplug frees the instance of struct fb_info. Accessing the memory will operate on undefined state. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Fixes: 60b59be ("fbdev: mm: Deferred IO support") Cc: Helge Deller <deller@gmx.de> Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org # v2.6.22+ Signed-off-by: Helge Deller <deller@gmx.de> [ replaced `kzalloc_obj` with `kzalloc`, and dropped `mutex_destroy(&fbdefio->lock)` ] Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent a2c817c commit 2a40f8b

2 files changed

Lines changed: 145 additions & 38 deletions

File tree

drivers/video/fbdev/core/fb_defio.c

Lines changed: 142 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,75 @@
2323
#include <linux/rmap.h>
2424
#include <linux/pagemap.h>
2525

26+
/*
27+
* struct fb_deferred_io_state
28+
*/
29+
30+
struct fb_deferred_io_state {
31+
struct kref ref;
32+
33+
struct mutex lock; /* mutex that protects the pageref list */
34+
/* fields protected by lock */
35+
struct fb_info *info;
36+
};
37+
38+
static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void)
39+
{
40+
struct fb_deferred_io_state *fbdefio_state;
41+
42+
fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL);
43+
if (!fbdefio_state)
44+
return NULL;
45+
46+
kref_init(&fbdefio_state->ref);
47+
mutex_init(&fbdefio_state->lock);
48+
49+
return fbdefio_state;
50+
}
51+
52+
static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state)
53+
{
54+
mutex_destroy(&fbdefio_state->lock);
55+
56+
kfree(fbdefio_state);
57+
}
58+
59+
static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state)
60+
{
61+
kref_get(&fbdefio_state->ref);
62+
}
63+
64+
static void __fb_deferred_io_state_release(struct kref *ref)
65+
{
66+
struct fb_deferred_io_state *fbdefio_state =
67+
container_of(ref, struct fb_deferred_io_state, ref);
68+
69+
fb_deferred_io_state_release(fbdefio_state);
70+
}
71+
72+
static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state)
73+
{
74+
kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release);
75+
}
76+
77+
/*
78+
* struct vm_operations_struct
79+
*/
80+
81+
static void fb_deferred_io_vm_open(struct vm_area_struct *vma)
82+
{
83+
struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
84+
85+
fb_deferred_io_state_get(fbdefio_state);
86+
}
87+
88+
static void fb_deferred_io_vm_close(struct vm_area_struct *vma)
89+
{
90+
struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data;
91+
92+
fb_deferred_io_state_put(fbdefio_state);
93+
}
94+
2695
static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
2796
{
2897
void *screen_base = (void __force *) info->screen_base;
@@ -93,17 +162,31 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
93162
/* this is to find and return the vmalloc-ed fb pages */
94163
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
95164
{
165+
struct fb_info *info;
96166
unsigned long offset;
97167
struct page *page;
98-
struct fb_info *info = vmf->vma->vm_private_data;
168+
vm_fault_t ret;
169+
struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
170+
171+
mutex_lock(&fbdefio_state->lock);
172+
173+
info = fbdefio_state->info;
174+
if (!info) {
175+
ret = VM_FAULT_SIGBUS; /* our device is gone */
176+
goto err_mutex_unlock;
177+
}
99178

100179
offset = vmf->pgoff << PAGE_SHIFT;
101-
if (offset >= info->fix.smem_len)
102-
return VM_FAULT_SIGBUS;
180+
if (offset >= info->fix.smem_len) {
181+
ret = VM_FAULT_SIGBUS;
182+
goto err_mutex_unlock;
183+
}
103184

104185
page = fb_deferred_io_page(info, offset);
105-
if (!page)
106-
return VM_FAULT_SIGBUS;
186+
if (!page) {
187+
ret = VM_FAULT_SIGBUS;
188+
goto err_mutex_unlock;
189+
}
107190

108191
get_page(page);
109192

@@ -115,8 +198,15 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
115198
BUG_ON(!page->mapping);
116199
page->index = vmf->pgoff; /* for page_mkclean() */
117200

201+
mutex_unlock(&fbdefio_state->lock);
202+
118203
vmf->page = page;
204+
119205
return 0;
206+
207+
err_mutex_unlock:
208+
mutex_unlock(&fbdefio_state->lock);
209+
return ret;
120210
}
121211

122212
int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
@@ -143,15 +233,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
143233
* Adds a page to the dirty list. Call this from struct
144234
* vm_operations_struct.page_mkwrite.
145235
*/
146-
static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
147-
struct page *page)
236+
static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state,
237+
unsigned long offset, struct page *page)
148238
{
149-
struct fb_deferred_io *fbdefio = info->fbdefio;
239+
struct fb_info *info;
240+
struct fb_deferred_io *fbdefio;
150241
struct fb_deferred_io_pageref *pageref;
151242
vm_fault_t ret;
152243

153244
/* protect against the workqueue changing the page list */
154-
mutex_lock(&fbdefio->lock);
245+
mutex_lock(&fbdefio_state->lock);
246+
247+
info = fbdefio_state->info;
248+
if (!info) {
249+
ret = VM_FAULT_SIGBUS; /* our device is gone */
250+
goto err_mutex_unlock;
251+
}
252+
253+
fbdefio = info->fbdefio;
155254

156255
pageref = fb_deferred_io_pageref_get(info, offset, page);
157256
if (WARN_ON_ONCE(!pageref)) {
@@ -169,50 +268,38 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long
169268
*/
170269
lock_page(pageref->page);
171270

172-
mutex_unlock(&fbdefio->lock);
271+
mutex_unlock(&fbdefio_state->lock);
173272

174273
/* come back after delay to process the deferred IO */
175274
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
176275
return VM_FAULT_LOCKED;
177276

178277
err_mutex_unlock:
179-
mutex_unlock(&fbdefio->lock);
278+
mutex_unlock(&fbdefio_state->lock);
180279
return ret;
181280
}
182281

183-
/*
184-
* fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
185-
* @fb_info: The fbdev info structure
186-
* @vmf: The VM fault
187-
*
188-
* This is a callback we get when userspace first tries to
189-
* write to the page. We schedule a workqueue. That workqueue
190-
* will eventually mkclean the touched pages and execute the
191-
* deferred framebuffer IO. Then if userspace touches a page
192-
* again, we repeat the same scheme.
193-
*
194-
* Returns:
195-
* VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
196-
*/
197-
static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
282+
static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state,
283+
struct vm_fault *vmf)
198284
{
199285
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
200286
struct page *page = vmf->page;
201287

202288
file_update_time(vmf->vma->vm_file);
203289

204-
return fb_deferred_io_track_page(info, offset, page);
290+
return fb_deferred_io_track_page(fbdefio_state, offset, page);
205291
}
206292

207-
/* vm_ops->page_mkwrite handler */
208293
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
209294
{
210-
struct fb_info *info = vmf->vma->vm_private_data;
295+
struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data;
211296

212-
return fb_deferred_io_page_mkwrite(info, vmf);
297+
return fb_deferred_io_page_mkwrite(fbdefio_state, vmf);
213298
}
214299

215300
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
301+
.open = fb_deferred_io_vm_open,
302+
.close = fb_deferred_io_vm_close,
216303
.fault = fb_deferred_io_fault,
217304
.page_mkwrite = fb_deferred_io_mkwrite,
218305
};
@@ -227,7 +314,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
227314
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
228315
if (!(info->flags & FBINFO_VIRTFB))
229316
vm_flags_set(vma, VM_IO);
230-
vma->vm_private_data = info;
317+
vma->vm_private_data = info->fbdefio_state;
318+
319+
fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */
320+
231321
return 0;
232322
}
233323
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
@@ -238,9 +328,10 @@ static void fb_deferred_io_work(struct work_struct *work)
238328
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
239329
struct fb_deferred_io_pageref *pageref, *next;
240330
struct fb_deferred_io *fbdefio = info->fbdefio;
331+
struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
241332

242333
/* here we mkclean the pages, then do all deferred IO */
243-
mutex_lock(&fbdefio->lock);
334+
mutex_lock(&fbdefio_state->lock);
244335
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
245336
struct page *cur = pageref->page;
246337
lock_page(cur);
@@ -255,12 +346,13 @@ static void fb_deferred_io_work(struct work_struct *work)
255346
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
256347
fb_deferred_io_pageref_put(pageref, info);
257348

258-
mutex_unlock(&fbdefio->lock);
349+
mutex_unlock(&fbdefio_state->lock);
259350
}
260351

261352
int fb_deferred_io_init(struct fb_info *info)
262353
{
263354
struct fb_deferred_io *fbdefio = info->fbdefio;
355+
struct fb_deferred_io_state *fbdefio_state;
264356
struct fb_deferred_io_pageref *pagerefs;
265357
unsigned long npagerefs, i;
266358
int ret;
@@ -270,7 +362,11 @@ int fb_deferred_io_init(struct fb_info *info)
270362
if (WARN_ON(!info->fix.smem_len))
271363
return -EINVAL;
272364

273-
mutex_init(&fbdefio->lock);
365+
fbdefio_state = fb_deferred_io_state_alloc();
366+
if (!fbdefio_state)
367+
return -ENOMEM;
368+
fbdefio_state->info = info;
369+
274370
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
275371
INIT_LIST_HEAD(&fbdefio->pagereflist);
276372
if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -289,10 +385,12 @@ int fb_deferred_io_init(struct fb_info *info)
289385
info->npagerefs = npagerefs;
290386
info->pagerefs = pagerefs;
291387

388+
info->fbdefio_state = fbdefio_state;
389+
292390
return 0;
293391

294392
err:
295-
mutex_destroy(&fbdefio->lock);
393+
fb_deferred_io_state_release(fbdefio_state);
296394
return ret;
297395
}
298396
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
@@ -333,11 +431,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
333431

334432
void fb_deferred_io_cleanup(struct fb_info *info)
335433
{
336-
struct fb_deferred_io *fbdefio = info->fbdefio;
434+
struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state;
337435

338436
fb_deferred_io_lastclose(info);
339437

438+
info->fbdefio_state = NULL;
439+
440+
mutex_lock(&fbdefio_state->lock);
441+
fbdefio_state->info = NULL;
442+
mutex_unlock(&fbdefio_state->lock);
443+
444+
fb_deferred_io_state_put(fbdefio_state);
445+
340446
kvfree(info->pagerefs);
341-
mutex_destroy(&fbdefio->lock);
342447
}
343448
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);

include/linux/fb.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,12 @@ struct fb_deferred_io {
214214
unsigned long delay;
215215
bool sort_pagereflist; /* sort pagelist by offset */
216216
int open_count; /* number of opened files; protected by fb_info lock */
217-
struct mutex lock; /* mutex that protects the pageref list */
218217
struct list_head pagereflist; /* list of pagerefs for touched pages */
219218
/* callback */
220219
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
221220
};
221+
222+
struct fb_deferred_io_state;
222223
#endif
223224

224225
/*
@@ -476,6 +477,7 @@ struct fb_info {
476477
unsigned long npagerefs;
477478
struct fb_deferred_io_pageref *pagerefs;
478479
struct fb_deferred_io *fbdefio;
480+
struct fb_deferred_io_state *fbdefio_state;
479481
#endif
480482

481483
const struct fb_ops *fbops;

0 commit comments

Comments
 (0)