Fix following ooops which can happen with plymouthd+vesafb and an i915+modesetting(intel_fb) loading while plymouthd is still running on vesa and exiting: [drm] Initialized i915 1.6.0 20080730 for 0000:00:02.0 on minor 0 BUG: unable to handle kernel NULL pointer dereference at (null) IP: [<ffffffff813b8270>] __mutex_lock_slowpath+0xc0/0x190 PGD 13f0b6067 PUD 13d8e0067 PMD 0 Oops: 0002 [#1] SMP last sysfs file: /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input6/capabilities/sw CPU 0 Pid: 58, comm: plymouthd Not tainted 2.6.33.4-desktop-1mnb #1 DG41RQ/ RIP: 0010:[<ffffffff813b8270>] [<ffffffff813b8270>] __mutex_lock_slowpath+0xc0/0x190 RSP: 0018:ffff88013d903e28 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff88013f19000c RCX: 0000000000000003 RDX: ffff88013d903e28 RSI: ffff88013f0bdcc0 RDI: ffff88013f19000c RBP: ffff88013d903e88 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: ffff88013f190008 R13: ffff88013f190010 R14: ffff88013d8d4350 R15: ffff88013d903fd8 FS: 00007f92b4f00700(0000) GS:ffff880005400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000013d8c3000 CR4: 00000000000406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process plymouthd (pid: 58, threadinfo ffff88013d902000, task ffff88013d8d4350) Stack: ffff88013f190010 0000000000000000 ffff88013d903e88 ffffffff8110ad51 <0> 000000003d903e68 ffffffff00000001 ffff88013d895040 ffff88013f190008 <0> ffff88013f190008 ffff88013f1a2650 ffff88013f759840 ffff88013dfe0200 Call Trace: [<ffffffff8110ad51>] ? free_pages_and_swap_cache+0xb1/0xe0 [<ffffffff813b818b>] mutex_lock+0x2b/0x50 [<ffffffff81223539>] fb_release+0x29/0x70 [<ffffffff8112a3b5>] __fput+0xf5/0x210 [<ffffffff8112a4f5>] fput+0x25/0x30 [<ffffffff811267ad>] filp_close+0x5d/0x90 [<ffffffff8112687c>] sys_close+0x9c/0xe0 [<ffffffff8100a042>] system_call_fastpath+0x16/0x1b Code: 44 00 00 49 8d 5c 24 04 4d 8d 6c 24 08 48 89 df e8 26 15 00 00 49 8b 44 24 10 48 8d 55 a0 49 89 54 24 10 4c 89 6d a0 48 89 45 a8 <48> 89 10 c7 45 cc ff ff ff ff 4c 89 75 b0 8b 45 cc 41 87 04 24 RIP [<ffffffff813b8270>] __mutex_lock_slowpath+0xc0/0x190 RSP <ffff88013d903e28> CR2: 0000000000000000 ---[ end trace c34d2c746663a02c ]--- This happens because while loading intel_fb (modesetting) it unregisters vesafb, calling unregister_framebuffer, and this calls fb_destroy hook which calls vesafb_destroy. vesafb_destroy frees struct fb_info of vesafb, and if there is still an application with /dev/fbX of vesafb opened, when it closes the device the struct fb_info is already gone and can crash like this case. The solution here is to keep track of opening and close of framebuffer and only free allocated struct fb_info when possible. Can't get any better solution than this for now... Signed-off-by: Herton Ronaldo Krzesinski <herton@mandriva.com.br> --- drivers/video/efifb.c | 24 +++++++++++++++++++++++- drivers/video/fbmem.c | 12 ++++++++++-- drivers/video/offb.c | 24 +++++++++++++++++++++++- drivers/video/vesafb.c | 24 +++++++++++++++++++++++- include/linux/fb.h | 6 ++++++ 5 files changed, 85 insertions(+), 5 deletions(-) diff -Naurp linux-2.6.33.orig/drivers/video/efifb.c linux-2.6.33/drivers/video/efifb.c --- linux-2.6.33.orig/drivers/video/efifb.c 2010-02-24 15:52:17.000000000 -0300 +++ linux-2.6.33/drivers/video/efifb.c 2010-05-20 14:55:04.641685804 -0300 @@ -161,16 +161,38 @@ static int efifb_setcolreg(unsigned regn return 0; } +static int efifb_cnt; +static bool efifb_dead; + +static int efifb_open(struct fb_info *info, int user) +{ + efifb_cnt++; + return 0; +} + +static int efifb_release(struct fb_info *info, int user) +{ + int rc = 0; + + if (--efifb_cnt == 0 && efifb_dead) + rc = FBINFO_MUST_FREE; + return rc; +} + static void efifb_destroy(struct fb_info *info) { + efifb_dead = true; if (info->screen_base) iounmap(info->screen_base); release_mem_region(info->aperture_base, info->aperture_size); - framebuffer_release(info); + if (!efifb_cnt) + framebuffer_release(info); } static struct fb_ops efifb_ops = { .owner = THIS_MODULE, + .fb_open = efifb_open, + .fb_release = efifb_release, .fb_destroy = efifb_destroy, .fb_setcolreg = efifb_setcolreg, .fb_fillrect = cfb_fillrect, diff -Naurp linux-2.6.33.orig/drivers/video/fbmem.c linux-2.6.33/drivers/video/fbmem.c --- linux-2.6.33.orig/drivers/video/fbmem.c 2010-02-24 15:52:17.000000000 -0300 +++ linux-2.6.33/drivers/video/fbmem.c 2010-05-20 15:20:49.942935447 -0300 @@ -1412,12 +1412,20 @@ __acquires(&info->lock) __releases(&info->lock) { struct fb_info * const info = file->private_data; + int rc_release = 0; mutex_lock(&info->lock); - if (info->fbops->fb_release) - info->fbops->fb_release(info,1); + if (info->fbops->fb_release && info->fbops->fb_destroy) + rc_release = info->fbops->fb_release(info, 1); + else if (info->fbops->fb_release) + info->fbops->fb_release(info, 1); module_put(info->fbops->owner); mutex_unlock(&info->lock); + /* If framebuffer is gone (fb_destroy) while we had it opened (see + * unregister_framebuffer case inside register_framebuffer), fb driver + * must tells us to free it */ + if (rc_release == FBINFO_MUST_FREE) + framebuffer_release(info); return 0; } diff -Naurp linux-2.6.33.orig/drivers/video/offb.c linux-2.6.33/drivers/video/offb.c --- linux-2.6.33.orig/drivers/video/offb.c 2010-02-24 15:52:17.000000000 -0300 +++ linux-2.6.33/drivers/video/offb.c 2010-05-20 14:49:41.493810063 -0300 @@ -282,16 +282,38 @@ static int offb_set_par(struct fb_info * return 0; } +static int offb_cnt; +static bool offb_dead; + +static int offb_open(struct fb_info *info, int user) +{ + offb_cnt++; + return 0; +} + +static int offb_release(struct fb_info *info, int user) +{ + int rc = 0; + + if (--offb_cnt == 0 && offb_dead) + rc = FBINFO_MUST_FREE; + return rc; +} + static void offb_destroy(struct fb_info *info) { + offb_dead = true; if (info->screen_base) iounmap(info->screen_base); release_mem_region(info->aperture_base, info->aperture_size); - framebuffer_release(info); + if (!offb_cnt) + framebuffer_release(info); } static struct fb_ops offb_ops = { .owner = THIS_MODULE, + .fb_open = offb_open, + .fb_release = offb_release, .fb_destroy = offb_destroy, .fb_setcolreg = offb_setcolreg, .fb_set_par = offb_set_par, diff -Naurp linux-2.6.33.orig/drivers/video/vesafb.c linux-2.6.33/drivers/video/vesafb.c --- linux-2.6.33.orig/drivers/video/vesafb.c 2010-02-24 15:52:17.000000000 -0300 +++ linux-2.6.33/drivers/video/vesafb.c 2010-05-20 14:48:01.854685448 -0300 @@ -174,16 +174,38 @@ static int vesafb_setcolreg(unsigned reg return err; } +static int vesafb_cnt; +static bool vesafb_dead; + +static int vesafb_open(struct fb_info *info, int user) +{ + vesafb_cnt++; + return 0; +} + +static int vesafb_release(struct fb_info *info, int user) +{ + int rc = 0; + + if (--vesafb_cnt == 0 && vesafb_dead) + rc = FBINFO_MUST_FREE; + return rc; +} + static void vesafb_destroy(struct fb_info *info) { + vesafb_dead = true; if (info->screen_base) iounmap(info->screen_base); release_mem_region(info->aperture_base, info->aperture_size); - framebuffer_release(info); + if (!vesafb_cnt) + framebuffer_release(info); } static struct fb_ops vesafb_ops = { .owner = THIS_MODULE, + .fb_open = vesafb_open, + .fb_release = vesafb_release, .fb_destroy = vesafb_destroy, .fb_setcolreg = vesafb_setcolreg, .fb_pan_display = vesafb_pan_display, diff -Naurp linux-2.6.33.orig/include/linux/fb.h linux-2.6.33/include/linux/fb.h --- linux-2.6.33.orig/include/linux/fb.h 2010-02-24 15:52:17.000000000 -0300 +++ linux-2.6.33/include/linux/fb.h 2010-05-20 14:32:08.591060001 -0300 @@ -811,6 +811,12 @@ struct fb_tile_ops { */ #define FBINFO_BE_MATH 0x100000 +/* + * Return code passed from fb_release call indicating we should free allocated + * framebuffer as the framebuffer is gone + */ +#define FBINFO_MUST_FREE 1 + struct fb_info { int node; int flags;