On Sat, Jan 19, 2013 at 4:12 PM, Sedat Dilek <sedat.dilek@xxxxxxxxx> wrote: > On Sat, Jan 19, 2013 at 3:16 PM, Ilya Zykov <ilya@xxxxxxx> wrote: >> The root of problem is carelessly zeroing pointer(in function __tty_buffer_flush()), >> when another thread can use it. It can be cause of "NULL pointer dereference". >> Main idea of the patch, this is never free last (struct tty_buffer) in the active buffer. >> Only flush the data for ldisc(buf->head->read = buf->head->commit). >> At that moment driver can collect(write) data in buffer without conflict. >> It is repeat behavior of flush_to_ldisc(), only without feeding data to ldisc. >> >> Also revert: >> commit c56a00a165712fd73081f40044b1e64407bb1875 >> tty: hold lock across tty buffer finding and buffer filling >> In order to delete the unneeded locks any more. >> >> Signed-off-by: Ilya Zykov <ilya@xxxxxxx> >> > > I have tested your patch on top of Linux-Next (next-20130118) plus my > own patchset (see file attachments)! > > [ TESTCASE ] > > # grep CONFIG_PM_DEBUG /boot/config-$(uname -r) > CONFIG_PM_DEBUG=y > > # echo "freezer" > /sys/power/pm_test > > # cat /sys/power/pm_test > none core processors platform devices [freezer] > > # echo mem > /sys/power/state && sleep 1 <--- 1st S/R: OK > > # echo mem > /sys/power/state && sleep 1 <--- 2nd S/R: OK, but caused > a call-trace (see above)! > > [ /TESTCASE ] > > Unfortunately, I get now a different call-trace on the 2nd freezer/pm-test... > > +[ 368.891613] PM: Preparing system for mem sleep > +[ 373.886722] Freezing user space processes ... (elapsed 0.01 seconds) done. > +[ 373.902741] Freezing remaining freezable tasks ... > +[ 393.904587] Freezing of tasks failed after 20.01 seconds (1 tasks > refusing to freeze, wq_busy=0): > +[ 393.904714] jbd2/loop0-8 D 0000000000000003 0 304 2 0x00000000 > +[ 393.904723] ffff880117525b68 0000000000000046 00000000ffffffff > ffff8801195d9400 > +[ 393.904731] ffff880117d74560 ffff880117525fd8 ffff880117525fd8 > ffff880117525fd8 > +[ 393.904738] ffff88004212c560 ffff880117d74560 ffff880117525b68 > ffff88011fad4738 > +[ 393.904745] Call Trace: > +[ 393.904764] [<ffffffff811c6830>] ? __wait_on_buffer+0x30/0x30 > +[ 393.904772] [<ffffffff816b5079>] schedule+0x29/0x70 > +[ 393.904778] [<ffffffff816b514f>] io_schedule+0x8f/0xd0 > +[ 393.904785] [<ffffffff811c683e>] sleep_on_buffer+0xe/0x20 > +[ 393.904794] [<ffffffff816b394f>] __wait_on_bit+0x5f/0x90 > +[ 393.904801] [<ffffffff811c6830>] ? __wait_on_buffer+0x30/0x30 > +[ 393.904809] [<ffffffff816b39fc>] out_of_line_wait_on_bit+0x7c/0x90 > +[ 393.904817] [<ffffffff8107eb00>] ? autoremove_wake_function+0x40/0x40 > +[ 393.904824] [<ffffffff811c682e>] __wait_on_buffer+0x2e/0x30 > +[ 393.904836] [<ffffffff8128a14f>] > jbd2_journal_commit_transaction+0xdef/0x1960 > +[ 393.904844] [<ffffffff816b620e>] ? _raw_spin_lock_irqsave+0x2e/0x40 > +[ 393.904853] [<ffffffff81069fbf>] ? try_to_del_timer_sync+0x4f/0x70 > +[ 393.904859] [<ffffffff8128e938>] kjournald2+0xb8/0x240 > +[ 393.904865] [<ffffffff8107eac0>] ? add_wait_queue+0x60/0x60 > +[ 393.904871] [<ffffffff8128e880>] ? commit_timeout+0x10/0x10 > +[ 393.904877] [<ffffffff8107ded0>] kthread+0xc0/0xd0 > +[ 393.904883] [<ffffffff8107de10>] ? flush_kthread_worker+0xb0/0xb0 > +[ 393.904889] [<ffffffff816bea2c>] ret_from_fork+0x7c/0xb0 > +[ 393.904894] [<ffffffff8107de10>] ? flush_kthread_worker+0xb0/0xb0 > +[ 393.904972] > +[ 393.904975] Restarting kernel threads ... done. > +[ 393.905163] Restarting tasks ... done. > +[ 393.914283] video LNXVIDEO:00: Restoring backlight state > > If this ring one or more bells to you let me know! > > Please, have a look at the file attachments! > Thanks. > > Hope this helps us to track the problem! > I turned on... CONFIG_EXT4_DEBUG=y CONFIG_JBD2_DEBUG=y ...to see more light in the dark. - Sedat - > - Sedat - > >> --- >> drivers/tty/tty_buffer.c | 92 +++++++++++----------------------------------- >> 1 files changed, 22 insertions(+), 70 deletions(-) >> >> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c >> index d6969f6..61ec4dd 100644 >> --- a/drivers/tty/tty_buffer.c >> +++ b/drivers/tty/tty_buffer.c >> @@ -119,11 +119,14 @@ static void __tty_buffer_flush(struct tty_port *port) >> struct tty_bufhead *buf = &port->buf; >> struct tty_buffer *thead; >> >> - while ((thead = buf->head) != NULL) { >> - buf->head = thead->next; >> - tty_buffer_free(port, thead); >> + if (unlikely(buf->head == NULL)) >> + return; >> + while ((thead = buf->head->next) != NULL) { >> + tty_buffer_free(port, buf->head); >> + buf->head = thead; >> } >> - buf->tail = NULL; >> + WARN_ON(buf->head != buf->tail); >> + buf->head->read = buf->head->commit; >> } >> >> /** >> @@ -194,19 +197,22 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) >> have queued and recycle that ? */ >> } >> /** >> - * __tty_buffer_request_room - grow tty buffer if needed >> + * tty_buffer_request_room - grow tty buffer if needed >> * @tty: tty structure >> * @size: size desired >> * >> * Make at least size bytes of linear space available for the tty >> * buffer. If we fail return the size we managed to find. >> - * Locking: Caller must hold port->buf.lock >> + * >> + * Locking: Takes port->buf.lock >> */ >> -static int __tty_buffer_request_room(struct tty_port *port, size_t size) >> +int tty_buffer_request_room(struct tty_port *port, size_t size) >> { >> struct tty_bufhead *buf = &port->buf; >> struct tty_buffer *b, *n; >> int left; >> + unsigned long flags; >> + spin_lock_irqsave(&buf->lock, flags); >> /* OPTIMISATION: We could keep a per tty "zero" sized buffer to >> remove this conditional if its worth it. This would be invisible >> to the callers */ >> @@ -228,31 +234,9 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size) >> } else >> size = left; >> } >> - >> + spin_unlock_irqrestore(&buf->lock, flags); >> return size; >> } >> - >> - >> -/** >> - * tty_buffer_request_room - grow tty buffer if needed >> - * @port: tty port structure >> - * @size: size desired >> - * >> - * Make at least size bytes of linear space available for the tty >> - * buffer. If we fail return the size we managed to find. >> - * >> - * Locking: Takes port->buf.lock >> - */ >> -int tty_buffer_request_room(struct tty_port *port, size_t size) >> -{ >> - unsigned long flags; >> - int length; >> - >> - spin_lock_irqsave(&port->buf.lock, flags); >> - length = __tty_buffer_request_room(port, size); >> - spin_unlock_irqrestore(&port->buf.lock, flags); >> - return length; >> -} >> EXPORT_SYMBOL_GPL(tty_buffer_request_room); >> >> /** >> @@ -271,26 +255,18 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); >> int tty_insert_flip_string_fixed_flag(struct tty_port *port, >> const unsigned char *chars, char flag, size_t size) >> { >> - struct tty_bufhead *buf = &port->buf; >> int copied = 0; >> do { >> int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); >> - int space; >> - unsigned long flags; >> - struct tty_buffer *tb; >> - >> - spin_lock_irqsave(&buf->lock, flags); >> - space = __tty_buffer_request_room(port, goal); >> - tb = buf->tail; >> + int space = tty_buffer_request_room(port, goal); >> + struct tty_buffer *tb = port->buf.tail; >> /* If there is no space then tb may be NULL */ >> if (unlikely(space == 0)) { >> - spin_unlock_irqrestore(&buf->lock, flags); >> break; >> } >> memcpy(tb->char_buf_ptr + tb->used, chars, space); >> memset(tb->flag_buf_ptr + tb->used, flag, space); >> tb->used += space; >> - spin_unlock_irqrestore(&buf->lock, flags); >> copied += space; >> chars += space; >> /* There is a small chance that we need to split the data over >> @@ -317,26 +293,18 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); >> int tty_insert_flip_string_flags(struct tty_port *port, >> const unsigned char *chars, const char *flags, size_t size) >> { >> - struct tty_bufhead *buf = &port->buf; >> int copied = 0; >> do { >> int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); >> - int space; >> - unsigned long __flags; >> - struct tty_buffer *tb; >> - >> - spin_lock_irqsave(&buf->lock, __flags); >> - space = __tty_buffer_request_room(port, goal); >> - tb = buf->tail; >> + int space = tty_buffer_request_room(port, goal); >> + struct tty_buffer *tb = port->buf.tail; >> /* If there is no space then tb may be NULL */ >> if (unlikely(space == 0)) { >> - spin_unlock_irqrestore(&buf->lock, __flags); >> break; >> } >> memcpy(tb->char_buf_ptr + tb->used, chars, space); >> memcpy(tb->flag_buf_ptr + tb->used, flags, space); >> tb->used += space; >> - spin_unlock_irqrestore(&buf->lock, __flags); >> copied += space; >> chars += space; >> flags += space; >> @@ -392,21 +360,13 @@ EXPORT_SYMBOL(tty_schedule_flip); >> int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, >> size_t size) >> { >> - struct tty_bufhead *buf = &port->buf; >> - int space; >> - unsigned long flags; >> - struct tty_buffer *tb; >> - >> - spin_lock_irqsave(&buf->lock, flags); >> - space = __tty_buffer_request_room(port, size); >> - >> - tb = buf->tail; >> + int space = tty_buffer_request_room(port, size); >> if (likely(space)) { >> + struct tty_buffer *tb = port->buf.tail; >> *chars = tb->char_buf_ptr + tb->used; >> memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); >> tb->used += space; >> } >> - spin_unlock_irqrestore(&buf->lock, flags); >> return space; >> } >> EXPORT_SYMBOL_GPL(tty_prepare_flip_string); >> @@ -430,21 +390,13 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); >> int tty_prepare_flip_string_flags(struct tty_port *port, >> unsigned char **chars, char **flags, size_t size) >> { >> - struct tty_bufhead *buf = &port->buf; >> - int space; >> - unsigned long __flags; >> - struct tty_buffer *tb; >> - >> - spin_lock_irqsave(&buf->lock, __flags); >> - space = __tty_buffer_request_room(port, size); >> - >> - tb = buf->tail; >> + int space = tty_buffer_request_room(port, size); >> if (likely(space)) { >> + struct tty_buffer *tb = port->buf.tail; >> *chars = tb->char_buf_ptr + tb->used; >> *flags = tb->flag_buf_ptr + tb->used; >> tb->used += space; >> } >> - spin_unlock_irqrestore(&buf->lock, __flags); >> return space; >> } >> EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html