2005-03-28 17:30:47

by Alexey Dobriyan

[permalink] [raw]
Subject: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

Steps to reproduce for me:
* Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
* Start rebooting
* Start moving serial mouse (I have Genius NetMouse Pro)
* Right after gpm is shut down I see the oops
* The system continues to reboot

Doing a "# service gpm stop" produce several pages of messages and then hang
the system.

CONFIG_PREEMPT_BKL=n kernel survives gpm shutdown in both cases.
============================================================================
Unable to handle kernel NULL pointer dereference at virtual address 00000888
printing eip:
c02011fe
*pde = 1ae8c067
*pte = 00000000
Oops: 0000 [#1]
PREEMPT
Modules linked in: ipt_REJECT ipt_state ip_conntrack iptable_filter ip_tables binfmt_misc uhci_hcd snd_intel8x0 snd_ac97_codec snd_pcm snd_timer snd soundcore snd_page_alloc floppy
CPU: 0
EIP: 0060:[<c02011fe>] Not tainted VLI
EFLAGS: 00010006 (2.6.12-rc1-bk2)
EIP is at serio_interrupt+0x3c/0x92
eax: ddbc0000 ebx: 00000874 ecx: 00000000 edx: 0000003e
esi: ddbc0000 edi: 0000003e ebp: 00000000 esp: c145def4
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c145d000 task=def82020)
Stack: 00000000 00000000 00000292 00000001 de024180 c173c128 00000001 c020273e
00000000 c173c000 c173c00c 00000001 00000246 c01dbf0c 00000001 c173c528
c173c128 c173c0d4 00000287 c173c0d8 defeb680 c01207fc 00000000 def82a00
Call Trace:
[<c020273e>] serport_ldisc_receive+0x31/0x3d
[<c01dbf0c>] flush_to_ldisc+0xae/0x11b
[<c01207fc>] worker_thread+0x1d1/0x284
[<c01dbe5e>] flush_to_ldisc+0x0/0x11b
[<c010ed10>] default_wake_function+0x0/0xc
[<c010ed4f>] __wake_up_common+0x33/0x5a
[<c010ed10>] default_wake_function+0x0/0xc
[<c012062b>] worker_thread+0x0/0x284
[<c0124470>] kthread+0x7c/0xa4
[<c01243f4>] kthread+0x0/0xa4
[<c0100c31>] kernel_thread_helper+0x5/0xb
Code: 00 00 00 00 9c 8f 44 24 08 fa b8 01 00 00 00 e8 b8 da f0 ff 8b 5e 68 85 db 74 40 89 f8 89 e9 0f b6 d0 8b 44 24 20 89 04 24 89 f0 <ff> 53 14 89 44 24 04 ff 74 24 08 9d b8 01 00 00 00 e8 bd da f0
<6>note: events/0[3] exited with preempt_count 1

"ff 53 14" is "call *0x14(%ebx)". 0x14 is the offset of
struct serio_interrupt::interrupt().

irqreturn_t serio_interrupt(struct serio *serio,
unsigned char data, unsigned int dfl, struct pt_regs *regs)
{
unsigned long flags;
irqreturn_t ret = IRQ_NONE;

spin_lock_irqsave(&serio->lock, flags);

if (likely(serio->drv)) {
=> ret = serio->drv->interrupt(serio, data, dfl, regs); <=
} else if (!dfl && serio->registered) {
serio_rescan(serio);
ret = IRQ_HANDLED;
}

spin_unlock_irqrestore(&serio->lock, flags);

return ret;
}


Attachments:
(No filename) (2.59 kB)
config_serio_interrupt_oops (5.86 kB)
dmesg_serio_interrupt_oops (9.31 kB)
Download all attachments

2005-03-29 06:31:56

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Monday 28 March 2005 12:26, Alexey Dobriyan wrote:
> Steps to reproduce for me:
> * Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
> * Start rebooting
> * Start moving serial mouse (I have Genius NetMouse Pro)
> * Right after gpm is shut down I see the oops
> * The system continues to reboot
>

Could you try the patch below, please? Thanks!

--
Dmitry

===================================================================

Input: serport - fix an Oops when closing port - should not call
serio_interrupt when serio port is being unregistered.

Signed-off-by: Dmitry Torokhov <[email protected]>


serport.c | 40 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 38 insertions(+), 2 deletions(-)

Index: dtor/drivers/input/serio/serport.c
===================================================================
--- dtor.orig/drivers/input/serio/serport.c
+++ dtor/drivers/input/serio/serport.c
@@ -27,11 +27,14 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_MOUSE);

#define SERPORT_BUSY 1
+#define SERPORT_ACTIVE 2
+#define SERPORT_DEAD 3

struct serport {
struct tty_struct *tty;
wait_queue_head_t wait;
struct serio *serio;
+ spinlock_t lock;
unsigned long flags;
};

@@ -49,10 +52,31 @@ static void serport_serio_close(struct s
{
struct serport *serport = serio->port_data;

- serport->serio->id.type = 0;
+ set_bit(SERPORT_DEAD, &serport->flags);
wake_up_interruptible(&serport->wait);
}

+static int serport_serio_start(struct serio *serio)
+{
+ struct serport *serport = serio->port_data;
+
+ spin_lock(&serport->lock);
+ set_bit(SERPORT_ACTIVE, &serport->flags);
+ spin_unlock(&serport->lock);
+
+ return 0;
+}
+
+static void serport_serio_stop(struct serio *serio)
+{
+ struct serport *serport = serio->port_data;
+
+ spin_lock(&serport->lock);
+ clear_bit(SERPORT_ACTIVE, &serport->flags);
+ serport->serio = NULL;
+ spin_unlock(&serport->lock);
+}
+
/*
* serport_ldisc_open() is the routine that is called upon setting our line
* discipline on a tty. It prepares the serio struct.
@@ -79,6 +103,7 @@ static int serport_ldisc_open(struct tty
serport->serio = serio;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
serport->tty = tty;
+ spin_lock_init(&serport->lock);
tty->disc_data = serport;

memset(serio, 0, sizeof(struct serio));
@@ -87,6 +112,8 @@ static int serport_ldisc_open(struct tty
serio->id.type = SERIO_RS232;
serio->write = serport_serio_write;
serio->close = serport_serio_close;
+ serio->start = serport_serio_start;
+ serio->stop = serport_serio_stop;
serio->port_data = serport;

init_waitqueue_head(&serport->wait);
@@ -117,8 +144,17 @@ static void serport_ldisc_receive(struct
{
struct serport *serport = (struct serport*) tty->disc_data;
int i;
+
+ spin_lock(&serport->lock);
+
+ if (!test_bit(SERPORT_ACTIVE, &serport->flags))
+ goto out;
+
for (i = 0; i < count; i++)
serio_interrupt(serport->serio, cp[i], 0, NULL);
+
+out:
+ spin_unlock(&serport->lock);
}

/*
@@ -148,7 +184,7 @@ static ssize_t serport_ldisc_read(struct

serio_register_port(serport->serio);
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
- wait_event_interruptible(serport->wait, !serport->serio->id.type);
+ wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
serio_unregister_port(serport->serio);

clear_bit(SERPORT_BUSY, &serport->flags);

2005-03-29 17:28:11

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Tuesday 29 March 2005 10:27, Dmitry Torokhov wrote:
> On Monday 28 March 2005 12:26, Alexey Dobriyan wrote:
> > Steps to reproduce for me:
> > * Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
> > * Start rebooting
> > * Start moving serial mouse (I have Genius NetMouse Pro)
> > * Right after gpm is shut down I see the oops
> > * The system continues to reboot
>
> Could you try the patch below, please? Thanks!

> Input: serport - fix an Oops when closing port - should not call
> serio_interrupt when serio port is being unregistered.

Doesn't work, sorry. Even worse: rebooting now also produces many pages of
oopsen, then hang the system. I'm willing to test any new patches.

2005-03-29 19:09:09

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Tue, 29 Mar 2005 21:28:20 +0400, Alexey Dobriyan <[email protected]> wrote:
> On Tuesday 29 March 2005 10:27, Dmitry Torokhov wrote:
> > On Monday 28 March 2005 12:26, Alexey Dobriyan wrote:
> > > Steps to reproduce for me:
> > > * Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
> > > * Start rebooting
> > > * Start moving serial mouse (I have Genius NetMouse Pro)
> > > * Right after gpm is shut down I see the oops
> > > * The system continues to reboot
> >
> > Could you try the patch below, please? Thanks!
>
> > Input: serport - fix an Oops when closing port - should not call
> > serio_interrupt when serio port is being unregistered.
>
> Doesn't work, sorry. Even worse: rebooting now also produces many pages of
> oopsen, then hang the system. I'm willing to test any new patches.
>

Does it oops at the same place with this patch or at some other place?
Btw, what happen if you try to kill inputattach or GPM or both without
rebooting?

--
Dmitry

2005-03-29 19:49:46

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Tuesday 29 March 2005 23:02, Dmitry Torokhov wrote:
> On Tue, 29 Mar 2005 21:28:20 +0400, Alexey Dobriyan <[email protected]> wrote:
> > On Tuesday 29 March 2005 10:27, Dmitry Torokhov wrote:
> > > On Monday 28 March 2005 12:26, Alexey Dobriyan wrote:
> > > > Steps to reproduce for me:
> > > > * Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
> > > > * Start rebooting
> > > > * Start moving serial mouse (I have Genius NetMouse Pro)
> > > > * Right after gpm is shut down I see the oops
> > > > * The system continues to reboot
> > >
> > > Could you try the patch below, please? Thanks!
> >
> > > Input: serport - fix an Oops when closing port - should not call
> > > serio_interrupt when serio port is being unregistered.
> >
> > Doesn't work, sorry. Even worse: rebooting now also produces many pages of
> > oopsen, then hang the system. I'm willing to test any new patches.
>
> Does it oops at the same place with this patch or at some other place?

I manage to find this in the logs (nothing more :-( ):
============================================================================
Unable to handle kernel NULL pointer dereference at virtual address 00000068
printing eip:
c0202947
*pde = 00000000
Oops: 0000 [#1]
PREEMPT
Modules linked in: ipt_REJECT ipt_state ip_conntrack iptable_filter ip_tables binfmt_misc uhci_hcd snd_intel8x0 snd_ac97_codec snd_pcm snd_timer snd soundcore snd_page_alloc floppy
CPU: 0
EIP: 0060:[<c0202947>] Not tainted VLI
EFLAGS: 00010282 (2.6.12-rc1-bk2-serio)
============================================================================
According to vmlinux, c0202947 is at:

c020293e <serport_ldisc_write_wakeup>:
c020293e: 8b 80 78 09 00 00 mov 0x978(%eax),%eax
c0202944: 8b 40 0c mov 0xc(%eax),%eax
c0202947: 8b 50 68 mov 0x68(%eax),%edx <<<<====
c020294a: 85 d2 test %edx,%edx
c020294c: 74 07 je c0202955 <serport_ldisc_write_wakeup+0x17>
c020294e: 8b 52 10 mov 0x10(%edx),%edx
c0202951: 85 d2 test %edx,%edx
c0202953: 75 01 jne c0202956 <serport_ldisc_write_wakeup+0x18>
c0202955: c3 ret
c0202956: ff d2 call *%edx

2005-03-29 20:44:20

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Tue, 29 Mar 2005 23:49:55 +0400, Alexey Dobriyan <[email protected]> wrote:
> On Tuesday 29 March 2005 23:02, Dmitry Torokhov wrote:
> > On Tue, 29 Mar 2005 21:28:20 +0400, Alexey Dobriyan <[email protected]> wrote:
> > > On Tuesday 29 March 2005 10:27, Dmitry Torokhov wrote:
> > > > On Monday 28 March 2005 12:26, Alexey Dobriyan wrote:
> > > > > Steps to reproduce for me:
> > > > > * Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
> > > > > * Start rebooting
> > > > > * Start moving serial mouse (I have Genius NetMouse Pro)
> > > > > * Right after gpm is shut down I see the oops
> > > > > * The system continues to reboot
> > > >
> > > > Could you try the patch below, please? Thanks!
> > >
> > > > Input: serport - fix an Oops when closing port - should not call
> > > > serio_interrupt when serio port is being unregistered.
> > >
> > > Doesn't work, sorry. Even worse: rebooting now also produces many pages of
> > > oopsen, then hang the system. I'm willing to test any new patches.
> >
> > Does it oops at the same place with this patch or at some other place?
>
> I manage to find this in the logs (nothing more :-( ):
> ============================================================================
> Unable to handle kernel NULL pointer dereference at virtual address 00000068
> printing eip:
> c0202947
> *pde = 00000000
> Oops: 0000 [#1]
> PREEMPT
> Modules linked in: ipt_REJECT ipt_state ip_conntrack iptable_filter ip_tables binfmt_misc uhci_hcd snd_intel8x0 snd_ac97_codec snd_pcm snd_timer snd soundcore snd_page_alloc floppy
> CPU: 0
> EIP: 0060:[<c0202947>] Not tainted VLI
> EFLAGS: 00010282 (2.6.12-rc1-bk2-serio)
> ============================================================================
> According to vmlinux, c0202947 is at:
>
> c020293e <serport_ldisc_write_wakeup>:

Ok, I have seen this OOPS before - it is a bit different scenario and
I am trying to look into it.

--
Dmitry

2005-03-30 06:30:53

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Tuesday 29 March 2005 14:49, Alexey Dobriyan wrote:
> On Tuesday 29 March 2005 23:02, Dmitry Torokhov wrote:
> > On Tue, 29 Mar 2005 21:28:20 +0400, Alexey Dobriyan <[email protected]> wrote:
> > > On Tuesday 29 March 2005 10:27, Dmitry Torokhov wrote:
> > > > On Monday 28 March 2005 12:26, Alexey Dobriyan wrote:
> > > > > Steps to reproduce for me:
> > > > > * Boot CONFIG_PREEMPT_BKL=y kernel (.config, dmesg are attached)
> > > > > * Start rebooting
> > > > > * Start moving serial mouse (I have Genius NetMouse Pro)
> > > > > * Right after gpm is shut down I see the oops
> > > > > * The system continues to reboot
> > > >
> > > > Could you try the patch below, please? Thanks!
> > >
> > > > Input: serport - fix an Oops when closing port - should not call
> > > > serio_interrupt when serio port is being unregistered.
> > >
> > > Doesn't work, sorry. Even worse: rebooting now also produces many pages of
> > > oopsen, then hang the system. I'm willing to test any new patches.
> >
> > Does it oops at the same place with this patch or at some other place?
>
> I manage to find this in the logs (nothing more :-( ):
> ============================================================================
> Unable to handle kernel NULL pointer dereference at virtual address 00000068
> printing eip:
> c0202947
> *pde = 00000000
> Oops: 0000 [#1]
> PREEMPT
> Modules linked in: ipt_REJECT ipt_state ip_conntrack iptable_filter ip_tables binfmt_misc uhci_hcd snd_intel8x0 snd_ac97_codec snd_pcm snd_timer snd soundcore snd_page_alloc floppy
> CPU: 0
> EIP: 0060:[<c0202947>] Not tainted VLI
> EFLAGS: 00010282 (2.6.12-rc1-bk2-serio)
> ============================================================================
> According to vmlinux, c0202947 is at:
>
> c020293e <serport_ldisc_write_wakeup>:

Could you please try this one instead? Thanks!

--
Dmitry


serport.c | 98 +++++++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 68 insertions(+), 30 deletions(-)

Index: dtor/drivers/input/serio/serport.c
===================================================================
--- dtor.orig/drivers/input/serio/serport.c
+++ dtor/drivers/input/serio/serport.c
@@ -27,11 +27,15 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_MOUSE);

#define SERPORT_BUSY 1
+#define SERPORT_ACTIVE 2
+#define SERPORT_DEAD 3

struct serport {
struct tty_struct *tty;
wait_queue_head_t wait;
struct serio *serio;
+ struct serio_device_id id;
+ spinlock_t lock;
unsigned long flags;
};

@@ -45,11 +49,29 @@ static int serport_serio_write(struct se
return -(serport->tty->driver->write(serport->tty, &data, 1) != 1);
}

+static int serport_serio_open(struct serio *serio)
+{
+ struct serport *serport = serio->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serport->lock, flags);
+ set_bit(SERPORT_ACTIVE, &serport->flags);
+ spin_unlock_irqrestore(&serport->lock, flags);
+
+ return 0;
+}
+
+
static void serport_serio_close(struct serio *serio)
{
struct serport *serport = serio->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serport->lock, flags);
+ clear_bit(SERPORT_ACTIVE, &serport->flags);
+ set_bit(SERPORT_DEAD, &serport->flags);
+ spin_unlock_irqrestore(&serport->lock, flags);

- serport->serio->id.type = 0;
wake_up_interruptible(&serport->wait);
}

@@ -61,36 +83,21 @@ static void serport_serio_close(struct s
static int serport_ldisc_open(struct tty_struct *tty)
{
struct serport *serport;
- struct serio *serio;
- char name[64];

if (!capable(CAP_SYS_ADMIN))
return -EPERM;

- serport = kmalloc(sizeof(struct serport), GFP_KERNEL);
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
- if (unlikely(!serport || !serio)) {
- kfree(serport);
- kfree(serio);
+ serport = kcalloc(1, sizeof(struct serport), GFP_KERNEL);
+ if (!serport)
return -ENOMEM;
- }

- memset(serport, 0, sizeof(struct serport));
- serport->serio = serio;
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
serport->tty = tty;
- tty->disc_data = serport;
-
- memset(serio, 0, sizeof(struct serio));
- strlcpy(serio->name, "Serial port", sizeof(serio->name));
- snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
- serio->id.type = SERIO_RS232;
- serio->write = serport_serio_write;
- serio->close = serport_serio_close;
- serio->port_data = serport;
-
+ spin_lock_init(&serport->lock);
init_waitqueue_head(&serport->wait);

+ tty->disc_data = serport;
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
return 0;
}

@@ -100,7 +107,8 @@ static int serport_ldisc_open(struct tty

static void serport_ldisc_close(struct tty_struct *tty)
{
- struct serport *serport = (struct serport*) tty->disc_data;
+ struct serport *serport = (struct serport *) tty->disc_data;
+
kfree(serport);
}

@@ -116,9 +124,19 @@ static void serport_ldisc_close(struct t
static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
struct serport *serport = (struct serport*) tty->disc_data;
+ unsigned long flags;
int i;
+
+ spin_lock_irqsave(&serport->lock, flags);
+
+ if (!test_bit(SERPORT_ACTIVE, &serport->flags))
+ goto out;
+
for (i = 0; i < count; i++)
serio_interrupt(serport->serio, cp[i], 0, NULL);
+
+out:
+ spin_unlock_irqrestore(&serport->lock, flags);
}

/*
@@ -141,16 +159,33 @@ static int serport_ldisc_room(struct tty
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
{
struct serport *serport = (struct serport*) tty->disc_data;
+ struct serio *serio;
char name[64];

if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
return -EBUSY;

+ serport->serio = serio = kcalloc(1, sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ strlcpy(serio->name, "Serial port", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
+ serio->id = serport->id;
+ serio->id.type = SERIO_RS232;
+ serio->write = serport_serio_write;
+ serio->open = serport_serio_open;
+ serio->close = serport_serio_close;
+ serio->port_data = serport;
+
serio_register_port(serport->serio);
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
- wait_event_interruptible(serport->wait, !serport->serio->id.type);
+
+ wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
serio_unregister_port(serport->serio);
+ serport->serio = NULL;

+ clear_bit(SERPORT_DEAD, &serport->flags);
clear_bit(SERPORT_BUSY, &serport->flags);

return 0;
@@ -163,16 +198,15 @@ static ssize_t serport_ldisc_read(struct
static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg)
{
struct serport *serport = (struct serport*) tty->disc_data;
- struct serio *serio = serport->serio;
unsigned long type;

if (cmd == SPIOCSTYPE) {
if (get_user(type, (unsigned long __user *) arg))
return -EFAULT;

- serio->id.proto = type & 0x000000ff;
- serio->id.id = (type & 0x0000ff00) >> 8;
- serio->id.extra = (type & 0x00ff0000) >> 16;
+ serport->id.proto = type & 0x000000ff;
+ serport->id.id = (type & 0x0000ff00) >> 8;
+ serport->id.extra = (type & 0x00ff0000) >> 16;

return 0;
}
@@ -182,9 +216,13 @@ static int serport_ldisc_ioctl(struct tt

static void serport_ldisc_write_wakeup(struct tty_struct * tty)
{
- struct serport *sp = (struct serport *) tty->disc_data;
+ struct serport *serport = (struct serport *) tty->disc_data;
+ unsigned long flags;

- serio_drv_write_wakeup(sp->serio);
+ spin_lock_irqsave(&serport->lock, flags);
+ if (!test_bit(SERPORT_ACTIVE, &serport->flags))
+ serio_drv_write_wakeup(serport->serio);
+ spin_unlock_irqrestore(&serport->lock, flags);
}

/*

2005-03-30 09:14:34

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Wednesday 30 March 2005 10:30, Dmitry Torokhov wrote:
> On Tuesday 29 March 2005 14:49, Alexey Dobriyan wrote:
> > According to vmlinux, c0202947 is at:
> >
> > c020293e <serport_ldisc_write_wakeup>:
>
> Could you please try this one instead? Thanks!

Still dies in serport_ldisc_write_wakeup (doesn't matter how to trigger)
via:

Unable to handle kernel NULL pointer dereference at virtual address 00000068
EIP: c020294f
tty_wakeup
uart_close
wait_for_completion
release_dev

If you want me to be more specific, wait until I figure out how to print
only parts of oops (to fit them on console).
============================================================================
c0202930 <serport_ldisc_write_wakeup>:
c0202930: push %esi
c0202931: push %ebx
c0202932: mov 0x978(%eax),%ebx
c0202938: pushf
c0202939: pop %esi
c020293a: cli
c020293b: mov $0x1,%eax
c0202940: call c010ecaf <add_preempt_count>
c0202945: mov 0x14(%ebx),%eax
c0202948: test $0x4,%al
c020294a: jne c0202956 <serport_ldisc_write_wakeup+0x26>
c020294c: mov 0xc(%ebx),%eax
c020294f: ==>> mov 0x68(%eax),%edx <<==
c0202952: test %edx,%edx
c0202954: jne c0202973 <serport_ldisc_write_wakeup+0x43>
c0202956: push %esi
c0202957: popf
c0202958: mov $0x1,%eax
c020295d: call c010ece1 <sub_preempt_count>
c0202962: mov $0xfffff000,%eax
c0202967: and %esp,%eax
c0202969: mov 0x8(%eax),%eax
c020296c: test $0x8,%al
c020296e: jne c0202984 <serport_ldisc_write_wakeup+0x54>
c0202970: pop %ebx
c0202971: pop %esi
c0202972: ret
c0202973: mov 0x10(%edx),%edx
c0202976: test %edx,%edx
c0202978: je c0202956 <serport_ldisc_write_wakeup+0x26>
c020297a: lea 0x0(%esi),%esi
c0202980: call *%edx
c0202982: jmp c0202956 <serport_ldisc_write_wakeup+0x26>
c0202984: pop %ebx
c0202985: pop %esi
c0202986: jmp c029ad16 <preempt_schedule>

2005-03-30 18:38:38

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: 2.6.12-rc1-bk2+PREEMPT_BKL: Oops at serio_interrupt

On Wednesday 30 March 2005 04:14, Alexey Dobriyan wrote:
> On Wednesday 30 March 2005 10:30, Dmitry Torokhov wrote:
> > On Tuesday 29 March 2005 14:49, Alexey Dobriyan wrote:
> > > According to vmlinux, c0202947 is at:
> > >
> > > c020293e <serport_ldisc_write_wakeup>:
> >
> > Could you please try this one instead? Thanks!
>
> Still dies in serport_ldisc_write_wakeup (doesn't matter how to trigger)
> via:
>
> Unable to handle kernel NULL pointer dereference at virtual address 00000068
> EIP: c020294f
> tty_wakeup
> uart_close
> wait_for_completion
> release_dev
>

Doh! What a silly typo:

> + if (!test_bit(SERPORT_ACTIVE, &serport->flags))
> + serio_drv_write_wakeup(serport->serio);

should be "if (test_bit(...))". Oh well, 3rd time is a charm.

--
Dmitry

serport.c | 98 +++++++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 68 insertions(+), 30 deletions(-)

Index: dtor/drivers/input/serio/serport.c
===================================================================
--- dtor.orig/drivers/input/serio/serport.c
+++ dtor/drivers/input/serio/serport.c
@@ -27,11 +27,15 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_MOUSE);

#define SERPORT_BUSY 1
+#define SERPORT_ACTIVE 2
+#define SERPORT_DEAD 3

struct serport {
struct tty_struct *tty;
wait_queue_head_t wait;
struct serio *serio;
+ struct serio_device_id id;
+ spinlock_t lock;
unsigned long flags;
};

@@ -45,11 +49,29 @@ static int serport_serio_write(struct se
return -(serport->tty->driver->write(serport->tty, &data, 1) != 1);
}

+static int serport_serio_open(struct serio *serio)
+{
+ struct serport *serport = serio->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serport->lock, flags);
+ set_bit(SERPORT_ACTIVE, &serport->flags);
+ spin_unlock_irqrestore(&serport->lock, flags);
+
+ return 0;
+}
+
+
static void serport_serio_close(struct serio *serio)
{
struct serport *serport = serio->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serport->lock, flags);
+ clear_bit(SERPORT_ACTIVE, &serport->flags);
+ set_bit(SERPORT_DEAD, &serport->flags);
+ spin_unlock_irqrestore(&serport->lock, flags);

- serport->serio->id.type = 0;
wake_up_interruptible(&serport->wait);
}

@@ -61,36 +83,21 @@ static void serport_serio_close(struct s
static int serport_ldisc_open(struct tty_struct *tty)
{
struct serport *serport;
- struct serio *serio;
- char name[64];

if (!capable(CAP_SYS_ADMIN))
return -EPERM;

- serport = kmalloc(sizeof(struct serport), GFP_KERNEL);
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
- if (unlikely(!serport || !serio)) {
- kfree(serport);
- kfree(serio);
+ serport = kcalloc(1, sizeof(struct serport), GFP_KERNEL);
+ if (!serport)
return -ENOMEM;
- }

- memset(serport, 0, sizeof(struct serport));
- serport->serio = serio;
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
serport->tty = tty;
- tty->disc_data = serport;
-
- memset(serio, 0, sizeof(struct serio));
- strlcpy(serio->name, "Serial port", sizeof(serio->name));
- snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
- serio->id.type = SERIO_RS232;
- serio->write = serport_serio_write;
- serio->close = serport_serio_close;
- serio->port_data = serport;
-
+ spin_lock_init(&serport->lock);
init_waitqueue_head(&serport->wait);

+ tty->disc_data = serport;
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
return 0;
}

@@ -100,7 +107,8 @@ static int serport_ldisc_open(struct tty

static void serport_ldisc_close(struct tty_struct *tty)
{
- struct serport *serport = (struct serport*) tty->disc_data;
+ struct serport *serport = (struct serport *) tty->disc_data;
+
kfree(serport);
}

@@ -116,9 +124,19 @@ static void serport_ldisc_close(struct t
static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
struct serport *serport = (struct serport*) tty->disc_data;
+ unsigned long flags;
int i;
+
+ spin_lock_irqsave(&serport->lock, flags);
+
+ if (!test_bit(SERPORT_ACTIVE, &serport->flags))
+ goto out;
+
for (i = 0; i < count; i++)
serio_interrupt(serport->serio, cp[i], 0, NULL);
+
+out:
+ spin_unlock_irqrestore(&serport->lock, flags);
}

/*
@@ -141,16 +159,33 @@ static int serport_ldisc_room(struct tty
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
{
struct serport *serport = (struct serport*) tty->disc_data;
+ struct serio *serio;
char name[64];

if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
return -EBUSY;

+ serport->serio = serio = kcalloc(1, sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ strlcpy(serio->name, "Serial port", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
+ serio->id = serport->id;
+ serio->id.type = SERIO_RS232;
+ serio->write = serport_serio_write;
+ serio->open = serport_serio_open;
+ serio->close = serport_serio_close;
+ serio->port_data = serport;
+
serio_register_port(serport->serio);
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
- wait_event_interruptible(serport->wait, !serport->serio->id.type);
+
+ wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
serio_unregister_port(serport->serio);
+ serport->serio = NULL;

+ clear_bit(SERPORT_DEAD, &serport->flags);
clear_bit(SERPORT_BUSY, &serport->flags);

return 0;
@@ -163,16 +198,15 @@ static ssize_t serport_ldisc_read(struct
static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg)
{
struct serport *serport = (struct serport*) tty->disc_data;
- struct serio *serio = serport->serio;
unsigned long type;

if (cmd == SPIOCSTYPE) {
if (get_user(type, (unsigned long __user *) arg))
return -EFAULT;

- serio->id.proto = type & 0x000000ff;
- serio->id.id = (type & 0x0000ff00) >> 8;
- serio->id.extra = (type & 0x00ff0000) >> 16;
+ serport->id.proto = type & 0x000000ff;
+ serport->id.id = (type & 0x0000ff00) >> 8;
+ serport->id.extra = (type & 0x00ff0000) >> 16;

return 0;
}
@@ -182,9 +216,13 @@ static int serport_ldisc_ioctl(struct tt

static void serport_ldisc_write_wakeup(struct tty_struct * tty)
{
- struct serport *sp = (struct serport *) tty->disc_data;
+ struct serport *serport = (struct serport *) tty->disc_data;
+ unsigned long flags;

- serio_drv_write_wakeup(sp->serio);
+ spin_lock_irqsave(&serport->lock, flags);
+ if (test_bit(SERPORT_ACTIVE, &serport->flags))
+ serio_drv_write_wakeup(serport->serio);
+ spin_unlock_irqrestore(&serport->lock, flags);
}

/*