Sophie

Sophie

distrib > Mandriva > 2010.2 > x86_64 > by-pkgid > 65051ff50e52f21645389ab283fda8ae > files > 15

kernel-rt-2.6.33.5-1.rt23.1mdv2010.1.src.rpm

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;