Skip to content

Commit fe9ae05

Browse files
tiwaiThomas Zimmermann
authored andcommitted
fbdev: Fix incorrect page mapping clearance at fb_deferred_io_release()
The recent fix for the deferred I/O by the commit 3efc61d ("fbdev: Fix invalid page access after closing deferred I/O devices") caused a regression when the same fb device is opened/closed while it's being used. It resulted in a frozen screen even if something is redrawn there after the close. The breakage is because the patch was made under a wrong assumption of a single open; in the current code, fb_deferred_io_release() cleans up the page mapping of the pageref list and it calls cancel_delayed_work_sync() unconditionally, where both are no correct behavior for multiple opens. This patch adds a refcount for the opens of the device, and applies the cleanup only when all files get closed. As both fb_deferred_io_open() and _close() are called always in the fb_info lock (mutex), it's safe to use the normal int for the refcounting. Also, a useless BUG_ON() is dropped. Fixes: 3efc61d ("fbdev: Fix invalid page access after closing deferred I/O devices") Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> Reviewed-by: Patrik Jakobsson <[email protected]> Signed-off-by: Thomas Zimmermann <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 9a9a8fe commit fe9ae05

File tree

2 files changed

+14
-4
lines changed

2 files changed

+14
-4
lines changed

drivers/video/fbdev/core/fb_defio.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,17 +309,18 @@ void fb_deferred_io_open(struct fb_info *info,
309309
struct inode *inode,
310310
struct file *file)
311311
{
312+
struct fb_deferred_io *fbdefio = info->fbdefio;
313+
312314
file->f_mapping->a_ops = &fb_deferred_io_aops;
315+
fbdefio->open_count++;
313316
}
314317
EXPORT_SYMBOL_GPL(fb_deferred_io_open);
315318

316-
void fb_deferred_io_release(struct fb_info *info)
319+
static void fb_deferred_io_lastclose(struct fb_info *info)
317320
{
318-
struct fb_deferred_io *fbdefio = info->fbdefio;
319321
struct page *page;
320322
int i;
321323

322-
BUG_ON(!fbdefio);
323324
cancel_delayed_work_sync(&info->deferred_work);
324325

325326
/* clear out the mapping that we setup */
@@ -328,13 +329,21 @@ void fb_deferred_io_release(struct fb_info *info)
328329
page->mapping = NULL;
329330
}
330331
}
332+
333+
void fb_deferred_io_release(struct fb_info *info)
334+
{
335+
struct fb_deferred_io *fbdefio = info->fbdefio;
336+
337+
if (!--fbdefio->open_count)
338+
fb_deferred_io_lastclose(info);
339+
}
331340
EXPORT_SYMBOL_GPL(fb_deferred_io_release);
332341

333342
void fb_deferred_io_cleanup(struct fb_info *info)
334343
{
335344
struct fb_deferred_io *fbdefio = info->fbdefio;
336345

337-
fb_deferred_io_release(info);
346+
fb_deferred_io_lastclose(info);
338347

339348
kvfree(info->pagerefs);
340349
mutex_destroy(&fbdefio->lock);

include/linux/fb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ struct fb_deferred_io {
212212
/* delay between mkwrite and deferred handler */
213213
unsigned long delay;
214214
bool sort_pagereflist; /* sort pagelist by offset */
215+
int open_count; /* number of opened files; protected by fb_info lock */
215216
struct mutex lock; /* mutex that protects the pageref list */
216217
struct list_head pagereflist; /* list of pagerefs for touched pages */
217218
/* callback */

0 commit comments

Comments
 (0)