Skip to contentSkip to conversation

tty: Correct tty buffer flush.

PATCH In review Ilya Zykov 4 participants 6 messages Dec 4, 2012 – Jan 19, 2013 1 message not in the archive TTY layer and serial drivers

lore
Share & tools
lore

Ilya Zykov

author
#0 lore raw

PATCH 1/1 tty: Correct tty buffer flush.

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(tty->buf.head->read = tty->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.

---
 drivers/tty/tty_buffer.c |   96 +++++++++++++---------------------------------
 1 files changed, 27 insertions(+), 69 deletions(-)
drivers/tty/tty_buffer.c +20 -44
index 91e326f..4f02f9c 100644
@@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
 {
 	struct tty_buffer *thead;
 
-	while ((thead = tty->buf.head) != NULL) {
-		tty->buf.head = thead->next;
-		tty_buffer_free(tty, thead);
+	if (tty->buf.head == NULL)
+		return;
+	while ((thead = tty->buf.head->next) != NULL) {
+		tty_buffer_free(tty, tty->buf.head);
+		tty->buf.head = thead;
 	}
-	tty->buf.tail = NULL;
+	WARN_ON(tty->buf.head != tty->buf.tail);
+	tty->buf.head->read = tty->buf.head->commit;
 }
 
 /**
@@ -185,19 +188,25 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
 	/* Should possibly check if this fails for the largest buffer we
 	   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 tty->buf.lock
+ *
+ *	Locking: Takes tty->buf.lock
  */
-static int __tty_buffer_request_room(struct tty_struct *tty, size_t size)
+int tty_buffer_request_room(struct tty_struct *tty, size_t size)
 {
 	struct tty_buffer *b, *n;
 	int left;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tty->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 */
@@ -219,29 +228,8 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size)
 			size = left;
 	}
 
-	return size;
-}
-
-
-/**
- *	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: Takes tty->buf.lock
- */
-int tty_buffer_request_room(struct tty_struct *tty, size_t size)
-{
-	unsigned long flags;
-	int length;
-
-	spin_lock_irqsave(&tty->buf.lock, flags);
-	length = __tty_buffer_request_room(tty, size);
 	spin_unlock_irqrestore(&tty->buf.lock, flags);
-	return length;
+	return size;
 }
 EXPORT_SYMBOL_GPL(tty_buffer_request_room);
 
@@ -264,22 +252,14 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
 	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(&tty->buf.lock, flags);
-		space = __tty_buffer_request_room(tty, goal);
-		tb = tty->buf.tail;
+		int space = tty_buffer_request_room(tty, goal);
+		struct tty_buffer *tb = tty->buf.tail;
 		/* If there is no space then tb may be NULL */
-		if (unlikely(space == 0)) {
-			spin_unlock_irqrestore(&tty->buf.lock, flags);
+		if (unlikely(space == 0))
 			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(&tty->buf.lock, flags);
 		copied += space;
 		chars += space;
 		/* There is a small chance that we need to split the data over
@@ -309,22 +289,14 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
 	int copied = 0;
 	do {
 		int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
-		int space;
-		unsigned long __flags;
-		struct tty_buffer *tb;
-
Acked-by on v1 1/1
#1 lore raw
Quoted 7 lines · On Tue, 04 Dec 2012 17:10:57 +0400 Ilya Zykov <[email protected]> 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(tty->buf.head->read = tty->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.

Quoted 6 lines

Also revert:
commit c56a00a165712fd73081f40044b1e64407bb1875
tty: hold lock across tty buffer finding and buffer filling
In order to delete the unneeded locks any more.

author
#2 lore raw

PATCH 1/1 tty: Correct tty buffer flush.

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.

---
 drivers/tty/tty_buffer.c |  105 +++++++++++++---------------------------------
 1 files changed, 29 insertions(+), 76 deletions(-)
drivers/tty/tty_buffer.c +29 -76
index 45d9161..8a3333d 100644
@@ -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;
 }
 
 /**
@@ -193,20 +196,26 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size)
 	/* Should possibly check if this fails for the largest buffer we
 	   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 tty->port->buf.lock
  */
-static int __tty_buffer_request_room(struct tty_port *port, size_t size)
+int tty_buffer_request_room(struct tty_struct *tty, size_t size)
 {
-	struct tty_bufhead *buf = &port->buf;
+	struct tty_bufhead *buf = &tty->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 */
@@ -218,7 +227,7 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size)
 
 	if (left < size) {
 		/* This is the slow path - looking for new buffers to use */
-		if ((n = tty_buffer_find(port, size)) != NULL) {
+		if ((n = tty_buffer_find(tty->port, size)) != NULL) {
 			if (b != NULL) {
 				b->next = n;
 				b->commit = b->used;
@@ -229,31 +238,9 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size)
 			size = left;
 	}
 
+	spin_unlock_irqrestore(&buf->lock, flags);
 	return size;
 }
-
-
-/**
- *	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: Takes port->buf.lock
- */
-int tty_buffer_request_room(struct tty_struct *tty, size_t size)
-{
-	struct tty_port *port = tty->port;
-	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);
 
 /**
@@ -272,26 +259,17 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room);
 int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
 		const unsigned char *chars, char flag, size_t size)
 {
-	struct tty_bufhead *buf = &tty->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(tty->port, goal);
-		tb = buf->tail;
+		int space = tty_buffer_request_room(tty, goal);
+		struct tty_buffer *tb = tty->port->buf.tail;
 		/* If there is no space then tb may be NULL */
-		if (unlikely(space == 0)) {
-			spin_unlock_irqrestore(&buf->lock, flags);
+		if (unlikely(space == 0))
 			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
@@ -318,26 +296,17 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);
 int tty_insert_flip_string_flags(struct tty_struct *tty,
 		const unsigned char *chars, const char *flags, size_t size)
 {
-	struct tty_bufhead *buf = &tty->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(tty->port, goal);
-		tb = buf->tail;
+		int space = tty_buffer_request_room(tty, goal);
+		struct tty_buffer *tb = tty->port->buf.tail;
 		/* If there is no space then tb may be NULL */
-		if (unlikely(space == 0)) {
-			spin_unlock_irqrestore(&buf->lock, __flags);
+		if (unlikely(space == 0))
 			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;
@@ -393,21 +362,13 @@ EXPORT_SYMBOL(tty_schedule_flip);
 int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
 		size_t size)
 {
-	struct tty_bufhead *buf = &tty->port->buf;
-	int space;
-	unsigned long flags;
-	struct tty_buffer *tb;
-
-	spin_lock_irqsave(&buf->lock, flags);
-	space = __tty_buffer_request_room(tty->port, size);
-
-	tb = buf->tail;
+	int space = tty_buffer_request_room(tty, size);
 	if (likely(space)) {
+		struct tty_buffer *tb = tty->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);
@@ -431,21 +392,13 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
 int tty_prepare_flip_string_flags(struct tty_struct *tty,
 			unsigned char **chars, char **flags, size_t size)
 {
-	struct tty_bufhead *buf = &tty->port->buf;
-	int space;
-	unsigned long __flags;
-	struct tty_buffer *tb;
-
-	spin_lock_irqsave(&buf->lock, __flags);
-	space = __tty_buffer_request_room(tty->port, size);
-
-	tb = buf->tail;
+	int space = tty_buffer_request_room(tty, size);
 	if (likely(space)) {
+		struct tty_buffer *tb = tty->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);
#3 lore raw
Quoted 11 lines · Ilya Zykov

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.

This patch doesn't apply to my tty-next branch, can you redo it against linux-next so that I can apply it?

thanks,

greg k-h

author
#4 lore raw

PATCH 1/1 tty: Correct tty buffer flush.

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.

---
 drivers/tty/tty_buffer.c |   92 +++++++++++-----------------------------------
 1 files changed, 22 insertions(+), 70 deletions(-)
drivers/tty/tty_buffer.c +22 -70
index d6969f6..61ec4dd 100644
@@ -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);

A message that is not in the archive (sent off-list or not yet received): CA+icZUXG-srV6i9PaMgdhRG2gCpU4T+VaxhZj_=E4XjAgh8REQ@mail.gmail.com

#6 lore raw
Quoted 76 lines · Sedat Dilek
Quoted 7 lines · Ilya Zykov

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.

Quoted 7 lines

Also revert:
commit c56a00a165712fd73081f40044b1e64407bb1875
tty: hold lock across tty buffer finding and buffer filling
In order to delete the unneeded locks any more.

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
Quoted 7 lines

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 -

Quoted 194 lines

- Sedat -

Quoted 192 lines
---
 drivers/tty/tty_buffer.c |   92 +++++++++++-----------------------------------
 1 files changed, 22 insertions(+), 70 deletions(-)
drivers/tty/tty_buffer.c +22 -70
index d6969f6..61ec4dd 100644
@@ -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);