From c0b79a90b1556a7e51d7a49a655eb60306f6258d Mon Sep 17 00:00:00 2001
From: Yong Zhang <yong.zhang0@gmail.com>
Date: Thu, 22 Sep 2011 16:58:46 +0800
Subject: um: irq: Remove IRQF_DISABLED

Since commit [e58aa3d2: genirq: Run irq handlers with interrupts disabled],
We run all interrupt handlers with interrupts disabled
and we even check and yell when an interrupt handler
returns with interrupts enabled (see commit [b738a50a:
genirq: Warn when handler enables interrupts]).

So now this flag is a NOOP and can be removed.

Signed-off-by: Yong Zhang <yong.zhang0@gmail.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index c1cf2206b84..ac9bcfbbeee 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -347,8 +347,8 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
 	int err;
 
 	/*
-	 * Interrupts are disabled here because we registered the interrupt with
-	 * IRQF_DISABLED (see line_setup_irq).
+	 * Interrupts are disabled here because genirq keep irqs disabled when
+	 * calling the action handler.
 	 */
 
 	spin_lock(&line->lock);
@@ -371,7 +371,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
 int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
 {
 	const struct line_driver *driver = line->driver;
-	int err = 0, flags = IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM;
+	int err = 0, flags = IRQF_SHARED | IRQF_SAMPLE_RANDOM;
 
 	if (input)
 		err = um_request_irq(driver->read_irq, fd, IRQ_READ,
@@ -807,7 +807,7 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty,
 				   .stack	= stack });
 
 	if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt,
-			   IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
+			   IRQF_SHARED | IRQF_SAMPLE_RANDOM,
 			   "winch", winch) < 0) {
 		printk(KERN_ERR "register_winch_irq - failed to register "
 		       "IRQ\n");
-- 
cgit v1.2.3-70-g09d2


From da645f3be912a377ada97268e36360b0a4389ab0 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Thu, 8 Sep 2011 20:34:52 -0400
Subject: um: switch line_remove() to setup_one_line()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index ac9bcfbbeee..08c5fba6db8 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -640,14 +640,11 @@ int line_id(char **str, int *start_out, int *end_out)
 
 int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
 {
-	int err;
-	char config[sizeof("conxxxx=none\0")];
-
-	sprintf(config, "%d=none", n);
-	err = line_setup(lines, num, config, error_out);
-	if (err >= 0)
-		err = 0;
-	return err;
+	if (n >= num) {
+		*error_out = "Device number out of range";
+		return -EINVAL;
+	}
+	return setup_one_line(lines, n, "none", INIT_ONE, error_out);
 }
 
 struct tty_driver *register_lines(struct line_driver *line_driver,
-- 
cgit v1.2.3-70-g09d2


From fe9a6b002988372406baf5aeefc046677782365e Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Thu, 8 Sep 2011 20:44:06 -0400
Subject: um: switch line_config() to setup_one_line()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 08c5fba6db8..b0022cf4efb 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -572,22 +572,32 @@ int line_config(struct line *lines, unsigned int num, char *str,
 {
 	struct line *line;
 	char *new;
-	int n;
+	char *end;
+	int n, err;
 
 	if (*str == '=') {
 		*error_out = "Can't configure all devices from mconsole";
 		return -EINVAL;
 	}
 
-	new = kstrdup(str, GFP_KERNEL);
+	n = simple_strtoul(str, &end, 0);
+	if (*end++ != '=') {
+		*error_out = "Couldn't parse device number";
+		return -EINVAL;
+	}
+	if (n >= num) {
+		*error_out = "Device number out of range";
+		return -EINVAL;
+	}
+
+	new = kstrdup(end, GFP_KERNEL);
 	if (new == NULL) {
 		*error_out = "Failed to allocate memory";
 		return -ENOMEM;
 	}
-	n = line_setup(lines, num, new, error_out);
-	if (n < 0)
-		return n;
-
+	err = setup_one_line(lines, n, new, INIT_ONE, error_out);
+	if (err)
+		return err;
 	line = &lines[n];
 	return parse_chan_pair(line->init_str, line, n, opts, error_out);
 }
-- 
cgit v1.2.3-70-g09d2


From 43574c1afea4f798592c03cf4d4ecea4fd0a8416 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 17:25:00 -0400
Subject: um: get rid of the init_prio mess

make line_setup() act on a separate array of conf strings + default conf,
have lines array initialized explicitly by that data, bury LINE_INIT()
macro from hell.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c          | 69 +++++++++++++++++------------------------
 arch/um/drivers/line.h          | 13 ++------
 arch/um/drivers/ssl.c           | 28 +++++++++++------
 arch/um/drivers/stdio_console.c | 32 ++++++++++++-------
 4 files changed, 68 insertions(+), 74 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index b0022cf4efb..3eea99e98a1 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -489,7 +489,7 @@ void close_lines(struct line *lines, int nlines)
 		close_chan(&lines[i].chan_list, 0);
 }
 
-static int setup_one_line(struct line *lines, int n, char *init, int init_prio,
+static int setup_one_line(struct line *lines, int n, char *init,
 			  char **error_out)
 {
 	struct line *line = &lines[n];
@@ -502,14 +502,11 @@ static int setup_one_line(struct line *lines, int n, char *init, int init_prio,
 		goto out;
 	}
 
-	if (line->init_pri <= init_prio) {
-		line->init_pri = init_prio;
-		if (!strcmp(init, "none"))
-			line->valid = 0;
-		else {
-			line->init_str = init;
-			line->valid = 1;
-		}
+	if (!strcmp(init, "none"))
+		line->valid = 0;
+	else {
+		line->init_str = init;
+		line->valid = 1;
 	}
 	err = 0;
 out:
@@ -524,47 +521,37 @@ out:
  * @error_out is an error string in the case of failure;
  */
 
-int line_setup(struct line *lines, unsigned int num, char *init,
-	       char **error_out)
+int line_setup(char **conf, unsigned int num, char **def,
+	       char *init, char *name)
 {
-	int i, n, err;
-	char *end;
+	char *error;
 
 	if (*init == '=') {
 		/*
 		 * We said con=/ssl= instead of con#=, so we are configuring all
 		 * consoles at once.
 		 */
-		n = -1;
-	}
-	else {
-		n = simple_strtoul(init, &end, 0);
+		*def = init + 1;
+	} else {
+		char *end;
+		unsigned n = simple_strtoul(init, &end, 0);
+
 		if (*end != '=') {
-			*error_out = "Couldn't parse device number";
-			return -EINVAL;
+			error = "Couldn't parse device number";
+			goto out;
 		}
-		init = end;
-	}
-	init++;
-
-	if (n >= (signed int) num) {
-		*error_out = "Device number out of range";
-		return -EINVAL;
-	}
-	else if (n >= 0) {
-		err = setup_one_line(lines, n, init, INIT_ONE, error_out);
-		if (err)
-			return err;
-	}
-	else {
-		for(i = 0; i < num; i++) {
-			err = setup_one_line(lines, i, init, INIT_ALL,
-					     error_out);
-			if (err)
-				return err;
+		if (n >= num) {
+			error = "Device number out of range";
+			goto out;
 		}
+		conf[n] = end + 1;
 	}
-	return n == -1 ? num : n;
+	return 0;
+
+out:
+	printk(KERN_ERR "Failed to set up %s with "
+	       "configuration string \"%s\" : %s\n", name, init, error);
+	return -EINVAL;
 }
 
 int line_config(struct line *lines, unsigned int num, char *str,
@@ -595,7 +582,7 @@ int line_config(struct line *lines, unsigned int num, char *str,
 		*error_out = "Failed to allocate memory";
 		return -ENOMEM;
 	}
-	err = setup_one_line(lines, n, new, INIT_ONE, error_out);
+	err = setup_one_line(lines, n, new, error_out);
 	if (err)
 		return err;
 	line = &lines[n];
@@ -654,7 +641,7 @@ int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
 		*error_out = "Device number out of range";
 		return -EINVAL;
 	}
-	return setup_one_line(lines, n, "none", INIT_ONE, error_out);
+	return setup_one_line(lines, n, "none", error_out);
 }
 
 struct tty_driver *register_lines(struct line_driver *line_driver,
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index 63df3ca02ac..0c4dadf5e03 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -37,7 +37,6 @@ struct line {
 	int valid;
 
 	char *init_str;
-	int init_pri;
 	struct list_head chan_list;
 
 	/*This lock is actually, mostly, local to*/
@@ -58,18 +57,10 @@ struct line {
 	int have_irq;
 };
 
-#define LINE_INIT(str, d) \
-	{ .count_lock =	__SPIN_LOCK_UNLOCKED((str).count_lock), \
-	  .init_str =	str,	\
-	  .init_pri =	INIT_STATIC, \
-	  .valid =	1, \
-	  .lock =	__SPIN_LOCK_UNLOCKED((str).lock), \
-	  .driver =	d }
-
 extern void line_close(struct tty_struct *tty, struct file * filp);
 extern int line_open(struct line *lines, struct tty_struct *tty);
-extern int line_setup(struct line *lines, unsigned int sizeof_lines,
-		      char *init, char **error_out);
+extern int line_setup(char **conf, unsigned nlines, char **def,
+		      char *init, char *name);
 extern int line_write(struct tty_struct *tty, const unsigned char *buf,
 		      int len);
 extern int line_put_char(struct tty_struct *tty, unsigned char ch);
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index 9d8c20af6f8..445288ff065 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -71,8 +71,9 @@ static struct line_driver driver = {
 /* The array is initialized by line_init, at initcall time.  The
  * elements are locked individually as needed.
  */
-static struct line serial_lines[NR_PORTS] =
-	{ [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN, &driver) };
+static char *conf[NR_PORTS];
+static char *def_conf = CONFIG_SSL_CHAN;
+static struct line serial_lines[NR_PORTS];
 
 static int ssl_config(char *str, char **error_out)
 {
@@ -186,9 +187,23 @@ static struct console ssl_cons = {
 static int ssl_init(void)
 {
 	char *new_title;
+	int i;
 
 	printk(KERN_INFO "Initializing software serial port version %d\n",
 	       ssl_version);
+
+	for (i = 0; i < NR_PORTS; i++) {
+		char *s = conf[i];
+		if (!s)
+			s = def_conf;
+		if (s && strcmp(s, "none") != 0) {
+			serial_lines[i].init_str = s;
+			serial_lines[i].valid = 1;
+		}
+		spin_lock_init(&serial_lines[i].lock);
+		spin_lock_init(&serial_lines[i].count_lock);
+		serial_lines[i].driver = &driver;
+	}
 	ssl_driver = register_lines(&driver, &ssl_ops, serial_lines,
 				    ARRAY_SIZE(serial_lines));
 
@@ -214,14 +229,7 @@ __uml_exitcall(ssl_exit);
 
 static int ssl_chan_setup(char *str)
 {
-	char *error;
-	int ret;
-
-	ret = line_setup(serial_lines, ARRAY_SIZE(serial_lines), str, &error);
-	if(ret < 0)
-		printk(KERN_ERR "Failed to set up serial line with "
-		       "configuration string \"%s\" : %s\n", str, error);
-
+	line_setup(conf, NR_PORTS, &def_conf, str, "serial line");
 	return 1;
 }
 
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index 088776f0190..6d244f47096 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -76,9 +76,9 @@ static struct line_driver driver = {
 /* The array is initialized by line_init, at initcall time.  The
  * elements are locked individually as needed.
  */
-static struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver),
-				     [ 1 ... MAX_TTYS - 1 ] =
-				     LINE_INIT(CONFIG_CON_CHAN, &driver) };
+static char *vt_conf[MAX_TTYS];
+static char *def_conf;
+static struct line vts[MAX_TTYS];
 
 static int con_config(char *str, char **error_out)
 {
@@ -160,7 +160,22 @@ static struct console stdiocons = {
 static int stdio_init(void)
 {
 	char *new_title;
-
+	int i;
+
+	for (i = 0; i < MAX_TTYS; i++) {
+		char *s = vt_conf[i];
+		if (!s)
+			s = def_conf;
+		if (!s)
+			s = i ? CONFIG_CON_CHAN : CONFIG_CON_ZERO_CHAN;
+		if (s && strcmp(s, "none") != 0) {
+			vts[i].init_str = s;
+			vts[i].valid = 1;
+		}
+		spin_lock_init(&vts[i].lock);
+		spin_lock_init(&vts[i].count_lock);
+		vts[i].driver = &driver;
+	}
 	console_driver = register_lines(&driver, &console_ops, vts,
 					ARRAY_SIZE(vts));
 	if (console_driver == NULL)
@@ -189,14 +204,7 @@ __uml_exitcall(console_exit);
 
 static int console_chan_setup(char *str)
 {
-	char *error;
-	int ret;
-
-	ret = line_setup(vts, ARRAY_SIZE(vts), str, &error);
-	if(ret < 0)
-		printk(KERN_ERR "Failed to set up console with "
-		       "configuration string \"%s\" : %s\n", str, error);
-
+	line_setup(vt_conf, MAX_TTYS, &def_conf, str, "console");
 	return 1;
 }
 __setup("con", console_chan_setup);
-- 
cgit v1.2.3-70-g09d2


From d8c215adbf3901aa7d00a0f17f08d77be689f838 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 17:36:37 -0400
Subject: um: convert count_lock to mutex, fix a race in line_open()

If two processes are opening the same line, the second to get
into line_open() will decide that it doesn't need to do anything
(correctly) or wait for anything.  The latter, unfortunately,
is incorrect - the first opener might not be through yet.  We
need to have exclusion covering the entire line_init(), including
the blocking parts.  Moreover, the next patch will need to
widen the exclusion on mconsole side of things, also including
the blocking bits, so let's just convert that sucker to mutex...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c          | 26 +++++++++-----------------
 arch/um/drivers/line.h          |  2 +-
 arch/um/drivers/ssl.c           |  2 +-
 arch/um/drivers/stdio_console.c |  2 +-
 4 files changed, 12 insertions(+), 20 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 3eea99e98a1..dc7e216df6a 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -409,7 +409,7 @@ int line_open(struct line *lines, struct tty_struct *tty)
 	struct line *line = &lines[tty->index];
 	int err = -ENODEV;
 
-	spin_lock(&line->count_lock);
+	mutex_lock(&line->count_lock);
 	if (!line->valid)
 		goto out_unlock;
 
@@ -421,10 +421,9 @@ int line_open(struct line *lines, struct tty_struct *tty)
 	tty->driver_data = line;
 	line->tty = tty;
 
-	spin_unlock(&line->count_lock);
 	err = enable_chan(line);
 	if (err) /* line_close() will be called by our caller */
-		return err;
+		goto out_unlock;
 
 	INIT_DELAYED_WORK(&line->task, line_timer_cb);
 
@@ -435,11 +434,8 @@ int line_open(struct line *lines, struct tty_struct *tty)
 
 	chan_window_size(&line->chan_list, &tty->winsize.ws_row,
 			 &tty->winsize.ws_col);
-
-	return 0;
-
 out_unlock:
-	spin_unlock(&line->count_lock);
+	mutex_unlock(&line->count_lock);
 	return err;
 }
 
@@ -459,7 +455,7 @@ void line_close(struct tty_struct *tty, struct file * filp)
 	/* We ignore the error anyway! */
 	flush_buffer(line);
 
-	spin_lock(&line->count_lock);
+	mutex_lock(&line->count_lock);
 	BUG_ON(!line->valid);
 
 	if (--line->count)
@@ -468,17 +464,13 @@ void line_close(struct tty_struct *tty, struct file * filp)
 	line->tty = NULL;
 	tty->driver_data = NULL;
 
-	spin_unlock(&line->count_lock);
-
 	if (line->sigio) {
 		unregister_winch(tty);
 		line->sigio = 0;
 	}
 
-	return;
-
 out_unlock:
-	spin_unlock(&line->count_lock);
+	mutex_unlock(&line->count_lock);
 }
 
 void close_lines(struct line *lines, int nlines)
@@ -495,7 +487,7 @@ static int setup_one_line(struct line *lines, int n, char *init,
 	struct line *line = &lines[n];
 	int err = -EINVAL;
 
-	spin_lock(&line->count_lock);
+	mutex_lock(&line->count_lock);
 
 	if (line->count) {
 		*error_out = "Device is already open";
@@ -510,7 +502,7 @@ static int setup_one_line(struct line *lines, int n, char *init,
 	}
 	err = 0;
 out:
-	spin_unlock(&line->count_lock);
+	mutex_unlock(&line->count_lock);
 	return err;
 }
 
@@ -609,13 +601,13 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
 
 	line = &lines[dev];
 
-	spin_lock(&line->count_lock);
+	mutex_lock(&line->count_lock);
 	if (!line->valid)
 		CONFIG_CHUNK(str, size, n, "none", 1);
 	else if (line->tty == NULL)
 		CONFIG_CHUNK(str, size, n, line->init_str, 1);
 	else n = chan_config_string(&line->chan_list, str, size, error_out);
-	spin_unlock(&line->count_lock);
+	mutex_unlock(&line->count_lock);
 
 	return n;
 }
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index 0c4dadf5e03..471f477b271 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -32,7 +32,7 @@ struct line_driver {
 
 struct line {
 	struct tty_struct *tty;
-	spinlock_t count_lock;
+	struct mutex count_lock;
 	unsigned long count;
 	int valid;
 
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index 445288ff065..23cffd6d85a 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -201,7 +201,7 @@ static int ssl_init(void)
 			serial_lines[i].valid = 1;
 		}
 		spin_lock_init(&serial_lines[i].lock);
-		spin_lock_init(&serial_lines[i].count_lock);
+		mutex_init(&serial_lines[i].count_lock);
 		serial_lines[i].driver = &driver;
 	}
 	ssl_driver = register_lines(&driver, &ssl_ops, serial_lines,
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index 6d244f47096..f8d4325b28b 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -173,7 +173,7 @@ static int stdio_init(void)
 			vts[i].valid = 1;
 		}
 		spin_lock_init(&vts[i].lock);
-		spin_lock_init(&vts[i].count_lock);
+		mutex_init(&vts[i].count_lock);
 		vts[i].driver = &driver;
 	}
 	console_driver = register_lines(&driver, &console_ops, vts,
-- 
cgit v1.2.3-70-g09d2


From 31efcebb7d7196adcee73027f513d7c0bf572b47 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 19:14:02 -0400
Subject: um: fix races between line_open() and line_config()

Pull parse_chan_pair() call into setup_one_line(), under the mutex.
We really don't want open() to succeed before parse_chan_pair() had
been done (or after it has failed, BTW).  We also want "remove con<n>"
to free irqs, etc., same as "config con<n>=none".

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/chan_kern.c |  3 +++
 arch/um/drivers/line.c      | 54 ++++++++++++++++++++++-----------------------
 2 files changed, 29 insertions(+), 28 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 420e2c80079..3a95498155a 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -551,6 +551,9 @@ int parse_chan_pair(char *str, struct line *line, int device,
 		INIT_LIST_HEAD(chans);
 	}
 
+	if (!str)
+		return 0;
+
 	out = strchr(str, ',');
 	if (out != NULL) {
 		in = str;
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index dc7e216df6a..1a8d6591c20 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -482,7 +482,7 @@ void close_lines(struct line *lines, int nlines)
 }
 
 static int setup_one_line(struct line *lines, int n, char *init,
-			  char **error_out)
+			  const struct chan_opts *opts, char **error_out)
 {
 	struct line *line = &lines[n];
 	int err = -EINVAL;
@@ -494,13 +494,28 @@ static int setup_one_line(struct line *lines, int n, char *init,
 		goto out;
 	}
 
-	if (!strcmp(init, "none"))
-		line->valid = 0;
-	else {
-		line->init_str = init;
+	if (!strcmp(init, "none")) {
+		if (line->valid) {
+			line->valid = 0;
+			kfree(line->init_str);
+			parse_chan_pair(NULL, line, n, opts, error_out);
+			err = 0;
+		}
+	} else {
+		char *new = kstrdup(init, GFP_KERNEL);
+		if (!new) {
+			*error_out = "Failed to allocate memory";
+			return -ENOMEM;
+		}
+		line->init_str = new;
 		line->valid = 1;
+		err = parse_chan_pair(new, line, n, opts, error_out);
+		if (err) {
+			line->init_str = NULL;
+			line->valid = 0;
+			kfree(new);
+		}
 	}
-	err = 0;
 out:
 	mutex_unlock(&line->count_lock);
 	return err;
@@ -549,10 +564,8 @@ out:
 int line_config(struct line *lines, unsigned int num, char *str,
 		const struct chan_opts *opts, char **error_out)
 {
-	struct line *line;
-	char *new;
 	char *end;
-	int n, err;
+	int n;
 
 	if (*str == '=') {
 		*error_out = "Can't configure all devices from mconsole";
@@ -569,16 +582,7 @@ int line_config(struct line *lines, unsigned int num, char *str,
 		return -EINVAL;
 	}
 
-	new = kstrdup(end, GFP_KERNEL);
-	if (new == NULL) {
-		*error_out = "Failed to allocate memory";
-		return -ENOMEM;
-	}
-	err = setup_one_line(lines, n, new, error_out);
-	if (err)
-		return err;
-	line = &lines[n];
-	return parse_chan_pair(line->init_str, line, n, opts, error_out);
+	return setup_one_line(lines, n, end, opts, error_out);
 }
 
 int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
@@ -633,7 +637,7 @@ int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
 		*error_out = "Device number out of range";
 		return -EINVAL;
 	}
-	return setup_one_line(lines, n, "none", error_out);
+	return setup_one_line(lines, n, "none", NULL, error_out);
 }
 
 struct tty_driver *register_lines(struct line_driver *line_driver,
@@ -688,15 +692,9 @@ void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
 		if (line->init_str == NULL)
 			continue;
 
-		line->init_str = kstrdup(line->init_str, GFP_KERNEL);
-		if (line->init_str == NULL)
-			printk(KERN_ERR "lines_init - kstrdup returned NULL\n");
-
-		if (parse_chan_pair(line->init_str, line, i, opts, &error)) {
-			printk(KERN_ERR "parse_chan_pair failed for "
+		if (setup_one_line(lines, i, line->init_str, opts, &error))
+			printk(KERN_ERR "setup_one_line failed for "
 			       "device %d : %s\n", i, error);
-			line->valid = 0;
-		}
 	}
 }
 
-- 
cgit v1.2.3-70-g09d2


From cfe6b7c79daa0efa27f474f1fe2a88fd7af5cc47 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 19:45:42 -0400
Subject: um: switch line.c tty drivers to dynamic device creation

Current code doesn't update the symlinks in /sys/dev/char when we add/remove
tty lines.  Fixing that allows to stop messing with ->valid before the driver
registration, which is a Good Thing(tm) - we shouldn't have it set before we
really have the things set up and ready for line_open().

We need tty_driver available to call tty_{un,}register_device(), so we just
stash a reference to it into struct line_driver.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c          | 37 +++++++++++++++++++++++--------------
 arch/um/drivers/line.h          |  9 +++++----
 arch/um/drivers/ssl.c           | 17 ++++++-----------
 arch/um/drivers/stdio_console.c | 19 ++++++-------------
 4 files changed, 40 insertions(+), 42 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 1a8d6591c20..015209a9881 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -485,6 +485,7 @@ static int setup_one_line(struct line *lines, int n, char *init,
 			  const struct chan_opts *opts, char **error_out)
 {
 	struct line *line = &lines[n];
+	struct tty_driver *driver = line->driver->driver;
 	int err = -EINVAL;
 
 	mutex_lock(&line->count_lock);
@@ -498,6 +499,7 @@ static int setup_one_line(struct line *lines, int n, char *init,
 		if (line->valid) {
 			line->valid = 0;
 			kfree(line->init_str);
+			tty_unregister_device(driver, n);
 			parse_chan_pair(NULL, line, n, opts, error_out);
 			err = 0;
 		}
@@ -507,9 +509,19 @@ static int setup_one_line(struct line *lines, int n, char *init,
 			*error_out = "Failed to allocate memory";
 			return -ENOMEM;
 		}
+		if (line->valid)
+			tty_unregister_device(driver, n);
 		line->init_str = new;
 		line->valid = 1;
 		err = parse_chan_pair(new, line, n, opts, error_out);
+		if (!err) {
+			struct device *d = tty_register_device(driver, n, NULL);
+			if (IS_ERR(d)) {
+				*error_out = "Failed to register device";
+				err = PTR_ERR(d);
+				parse_chan_pair(NULL, line, n, opts, error_out);
+			}
+		}
 		if (err) {
 			line->init_str = NULL;
 			line->valid = 0;
@@ -640,15 +652,15 @@ int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
 	return setup_one_line(lines, n, "none", NULL, error_out);
 }
 
-struct tty_driver *register_lines(struct line_driver *line_driver,
-				  const struct tty_operations *ops,
-				  struct line *lines, int nlines)
+int register_lines(struct line_driver *line_driver,
+		   const struct tty_operations *ops,
+		   struct line *lines, int nlines)
 {
-	int i;
 	struct tty_driver *driver = alloc_tty_driver(nlines);
+	int err;
 
 	if (!driver)
-		return NULL;
+		return -ENOMEM;
 
 	driver->driver_name = line_driver->name;
 	driver->name = line_driver->device_name;
@@ -656,24 +668,21 @@ struct tty_driver *register_lines(struct line_driver *line_driver,
 	driver->minor_start = line_driver->minor_start;
 	driver->type = line_driver->type;
 	driver->subtype = line_driver->subtype;
-	driver->flags = TTY_DRIVER_REAL_RAW;
+	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 	driver->init_termios = tty_std_termios;
 	tty_set_operations(driver, ops);
 
-	if (tty_register_driver(driver)) {
+	err = tty_register_driver(driver);
+	if (err) {
 		printk(KERN_ERR "register_lines : can't register %s driver\n",
 		       line_driver->name);
 		put_tty_driver(driver);
-		return NULL;
-	}
-
-	for(i = 0; i < nlines; i++) {
-		if (!lines[i].valid)
-			tty_unregister_device(driver, i);
+		return err;
 	}
 
+	line_driver->driver = driver;
 	mconsole_register_dev(&line_driver->mc);
-	return driver;
+	return 0;
 }
 
 static DEFINE_SPINLOCK(winch_handler_lock);
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index 471f477b271..e3f86065e04 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -15,7 +15,7 @@
 #include "chan_user.h"
 #include "mconsole_kern.h"
 
-/* There's only one modifiable field in this - .mc.list */
+/* There's only two modifiable fields in this - .mc.list and .driver */
 struct line_driver {
 	const char *name;
 	const char *device_name;
@@ -28,6 +28,7 @@ struct line_driver {
 	const int write_irq;
 	const char *write_irq_name;
 	struct mc_device mc;
+	struct tty_driver *driver;
 };
 
 struct line {
@@ -78,9 +79,9 @@ extern char *add_xterm_umid(char *base);
 extern int line_setup_irq(int fd, int input, int output, struct line *line,
 			  void *data);
 extern void line_close_chan(struct line *line);
-extern struct tty_driver *register_lines(struct line_driver *line_driver,
-					 const struct tty_operations *driver,
-					 struct line *lines, int nlines);
+extern int register_lines(struct line_driver *line_driver,
+			  const struct tty_operations *driver,
+			  struct line *lines, int nlines);
 extern void lines_init(struct line *lines, int nlines, struct chan_opts *opts);
 extern void close_lines(struct line *lines, int nlines);
 
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index 23cffd6d85a..6398a47d035 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -20,12 +20,6 @@
 
 static const int ssl_version = 1;
 
-/* Referenced only by tty_driver below - presumably it's locked correctly
- * by the tty driver.
- */
-
-static struct tty_driver *ssl_driver;
-
 #define NR_PORTS 64
 
 static void ssl_announce(char *dev_name, int dev)
@@ -164,7 +158,7 @@ static void ssl_console_write(struct console *c, const char *string,
 static struct tty_driver *ssl_console_device(struct console *c, int *index)
 {
 	*index = c->index;
-	return ssl_driver;
+	return driver.driver;
 }
 
 static int ssl_console_setup(struct console *co, char *options)
@@ -187,6 +181,7 @@ static struct console ssl_cons = {
 static int ssl_init(void)
 {
 	char *new_title;
+	int err;
 	int i;
 
 	printk(KERN_INFO "Initializing software serial port version %d\n",
@@ -196,16 +191,16 @@ static int ssl_init(void)
 		char *s = conf[i];
 		if (!s)
 			s = def_conf;
-		if (s && strcmp(s, "none") != 0) {
+		if (s && strcmp(s, "none") != 0)
 			serial_lines[i].init_str = s;
-			serial_lines[i].valid = 1;
-		}
 		spin_lock_init(&serial_lines[i].lock);
 		mutex_init(&serial_lines[i].count_lock);
 		serial_lines[i].driver = &driver;
 	}
-	ssl_driver = register_lines(&driver, &ssl_ops, serial_lines,
+	err = register_lines(&driver, &ssl_ops, serial_lines,
 				    ARRAY_SIZE(serial_lines));
+	if (err)
+		return err;
 
 	new_title = add_xterm_umid(opts.xterm_title);
 	if (new_title != NULL)
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index f8d4325b28b..32bd040138f 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -27,12 +27,6 @@
 
 #define MAX_TTYS (16)
 
-/* Referenced only by tty_driver below - presumably it's locked correctly
- * by the tty driver.
- */
-
-static struct tty_driver *console_driver;
-
 static void stdio_announce(char *dev_name, int dev)
 {
 	printk(KERN_INFO "Virtual console %d assigned device '%s'\n", dev,
@@ -137,7 +131,7 @@ static void uml_console_write(struct console *console, const char *string,
 static struct tty_driver *uml_console_device(struct console *c, int *index)
 {
 	*index = c->index;
-	return console_driver;
+	return driver.driver;
 }
 
 static int uml_console_setup(struct console *co, char *options)
@@ -160,6 +154,7 @@ static struct console stdiocons = {
 static int stdio_init(void)
 {
 	char *new_title;
+	int err;
 	int i;
 
 	for (i = 0; i < MAX_TTYS; i++) {
@@ -168,18 +163,16 @@ static int stdio_init(void)
 			s = def_conf;
 		if (!s)
 			s = i ? CONFIG_CON_CHAN : CONFIG_CON_ZERO_CHAN;
-		if (s && strcmp(s, "none") != 0) {
+		if (s && strcmp(s, "none") != 0)
 			vts[i].init_str = s;
-			vts[i].valid = 1;
-		}
 		spin_lock_init(&vts[i].lock);
 		mutex_init(&vts[i].count_lock);
 		vts[i].driver = &driver;
 	}
-	console_driver = register_lines(&driver, &console_ops, vts,
+	err = register_lines(&driver, &console_ops, vts,
 					ARRAY_SIZE(vts));
-	if (console_driver == NULL)
-		return -1;
+	if (err)
+		return err;
 	printk(KERN_INFO "Initialized stdio console driver\n");
 
 	new_title = add_xterm_umid(opts.xterm_title);
-- 
cgit v1.2.3-70-g09d2


From 04292b2cf8f02a33cfc1054c0c51aa8c77731813 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 20:07:05 -0400
Subject: um: get rid of lines_init()

move config-independent parts of initialization into
register_lines(), call setup_one_line() after it instead
of abusing ->init_str.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c          | 31 ++++++++++---------------------
 arch/um/drivers/line.h          |  3 ++-
 arch/um/drivers/ssl.c           | 20 +++++++++-----------
 arch/um/drivers/stdio_console.c | 25 ++++++++++++-------------
 4 files changed, 33 insertions(+), 46 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 015209a9881..002d4a968ae 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -481,8 +481,8 @@ void close_lines(struct line *lines, int nlines)
 		close_chan(&lines[i].chan_list, 0);
 }
 
-static int setup_one_line(struct line *lines, int n, char *init,
-			  const struct chan_opts *opts, char **error_out)
+int setup_one_line(struct line *lines, int n, char *init,
+		   const struct chan_opts *opts, char **error_out)
 {
 	struct line *line = &lines[n];
 	struct tty_driver *driver = line->driver->driver;
@@ -658,6 +658,7 @@ int register_lines(struct line_driver *line_driver,
 {
 	struct tty_driver *driver = alloc_tty_driver(nlines);
 	int err;
+	int i;
 
 	if (!driver)
 		return -ENOMEM;
@@ -670,6 +671,13 @@ int register_lines(struct line_driver *line_driver,
 	driver->subtype = line_driver->subtype;
 	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 	driver->init_termios = tty_std_termios;
+	
+	for (i = 0; i < nlines; i++) {
+		spin_lock_init(&lines[i].lock);
+		mutex_init(&lines[i].count_lock);
+		lines[i].driver = line_driver;
+		INIT_LIST_HEAD(&lines[i].chan_list);
+	}
 	tty_set_operations(driver, ops);
 
 	err = tty_register_driver(driver);
@@ -688,25 +696,6 @@ int register_lines(struct line_driver *line_driver,
 static DEFINE_SPINLOCK(winch_handler_lock);
 static LIST_HEAD(winch_handlers);
 
-void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
-{
-	struct line *line;
-	char *error;
-	int i;
-
-	for(i = 0; i < nlines; i++) {
-		line = &lines[i];
-		INIT_LIST_HEAD(&line->chan_list);
-
-		if (line->init_str == NULL)
-			continue;
-
-		if (setup_one_line(lines, i, line->init_str, opts, &error))
-			printk(KERN_ERR "setup_one_line failed for "
-			       "device %d : %s\n", i, error);
-	}
-}
-
 struct winch {
 	struct list_head list;
 	int fd;
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index e3f86065e04..95991994a93 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -82,7 +82,8 @@ extern void line_close_chan(struct line *line);
 extern int register_lines(struct line_driver *line_driver,
 			  const struct tty_operations *driver,
 			  struct line *lines, int nlines);
-extern void lines_init(struct line *lines, int nlines, struct chan_opts *opts);
+extern int setup_one_line(struct line *lines, int n, char *init,
+			  const struct chan_opts *opts, char **error_out);
 extern void close_lines(struct line *lines, int nlines);
 
 extern int line_config(struct line *lines, unsigned int sizeof_lines,
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index 6398a47d035..d0b5ccf2379 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -187,16 +187,6 @@ static int ssl_init(void)
 	printk(KERN_INFO "Initializing software serial port version %d\n",
 	       ssl_version);
 
-	for (i = 0; i < NR_PORTS; i++) {
-		char *s = conf[i];
-		if (!s)
-			s = def_conf;
-		if (s && strcmp(s, "none") != 0)
-			serial_lines[i].init_str = s;
-		spin_lock_init(&serial_lines[i].lock);
-		mutex_init(&serial_lines[i].count_lock);
-		serial_lines[i].driver = &driver;
-	}
 	err = register_lines(&driver, &ssl_ops, serial_lines,
 				    ARRAY_SIZE(serial_lines));
 	if (err)
@@ -206,7 +196,15 @@ static int ssl_init(void)
 	if (new_title != NULL)
 		opts.xterm_title = new_title;
 
-	lines_init(serial_lines, ARRAY_SIZE(serial_lines), &opts);
+	for (i = 0; i < NR_PORTS; i++) {
+		char *error;
+		char *s = conf[i];
+		if (!s)
+			s = def_conf;
+		if (setup_one_line(serial_lines, i, s, &opts, &error))
+			printk(KERN_ERR "setup_one_line failed for "
+			       "device %d : %s\n", i, error);
+	}
 
 	ssl_init_done = 1;
 	register_console(&ssl_cons);
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index 32bd040138f..fe581209d62 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -157,29 +157,28 @@ static int stdio_init(void)
 	int err;
 	int i;
 
-	for (i = 0; i < MAX_TTYS; i++) {
-		char *s = vt_conf[i];
-		if (!s)
-			s = def_conf;
-		if (!s)
-			s = i ? CONFIG_CON_CHAN : CONFIG_CON_ZERO_CHAN;
-		if (s && strcmp(s, "none") != 0)
-			vts[i].init_str = s;
-		spin_lock_init(&vts[i].lock);
-		mutex_init(&vts[i].count_lock);
-		vts[i].driver = &driver;
-	}
 	err = register_lines(&driver, &console_ops, vts,
 					ARRAY_SIZE(vts));
 	if (err)
 		return err;
+
 	printk(KERN_INFO "Initialized stdio console driver\n");
 
 	new_title = add_xterm_umid(opts.xterm_title);
 	if(new_title != NULL)
 		opts.xterm_title = new_title;
 
-	lines_init(vts, ARRAY_SIZE(vts), &opts);
+	for (i = 0; i < MAX_TTYS; i++) {
+		char *error;
+		char *s = vt_conf[i];
+		if (!s)
+			s = def_conf;
+		if (!s)
+			s = i ? CONFIG_CON_CHAN : CONFIG_CON_ZERO_CHAN;
+		if (setup_one_line(vts, i, s, &opts, &error))
+			printk(KERN_ERR "setup_one_line failed for "
+			       "device %d : %s\n", i, error);
+	}
 
 	con_init_done = 1;
 	register_console(&stdiocons);
-- 
cgit v1.2.3-70-g09d2


From c8e2876fc8adaf9539f051fcda5d551308e8a0f8 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 20:08:48 -0400
Subject: um: finally kill ->init_str leaks

now we can do that...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 002d4a968ae..9ffade87a8c 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -509,8 +509,10 @@ int setup_one_line(struct line *lines, int n, char *init,
 			*error_out = "Failed to allocate memory";
 			return -ENOMEM;
 		}
-		if (line->valid)
+		if (line->valid) {
 			tty_unregister_device(driver, n);
+			kfree(line->init_str);
+		}
 		line->init_str = new;
 		line->valid = 1;
 		err = parse_chan_pair(new, line, n, opts, error_out);
-- 
cgit v1.2.3-70-g09d2


From bed5e39c56f3fe792e336cfa2670001d78f1d44c Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Thu, 8 Sep 2011 10:49:34 -0400
Subject: um: switch users of ->chan_list to ->chan_{in,out} (easy cases)

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/chan.h          |  16 ++--
 arch/um/drivers/chan_kern.c     | 172 +++++++++++++++-------------------------
 arch/um/drivers/line.c          |  24 +++---
 arch/um/drivers/ssl.c           |   2 +-
 arch/um/drivers/stdio_console.c |   2 +-
 5 files changed, 88 insertions(+), 128 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/chan.h b/arch/um/drivers/chan.h
index 8df0fd9024d..5078ec701c6 100644
--- a/arch/um/drivers/chan.h
+++ b/arch/um/drivers/chan.h
@@ -27,24 +27,24 @@ struct chan {
 	void *data;
 };
 
-extern void chan_interrupt(struct list_head *chans, struct delayed_work *task,
+extern void chan_interrupt(struct line *line, struct delayed_work *task,
 			   struct tty_struct *tty, int irq);
 extern int parse_chan_pair(char *str, struct line *line, int device,
 			   const struct chan_opts *opts, char **error_out);
-extern int write_chan(struct list_head *chans, const char *buf, int len,
+extern int write_chan(struct chan *chan, const char *buf, int len,
 			     int write_irq);
-extern int console_write_chan(struct list_head *chans, const char *buf, 
+extern int console_write_chan(struct chan *chan, const char *buf, 
 			      int len);
 extern int console_open_chan(struct line *line, struct console *co);
-extern void deactivate_chan(struct list_head *chans, int irq);
-extern void reactivate_chan(struct list_head *chans, int irq);
-extern void chan_enable_winch(struct list_head *chans, struct tty_struct *tty);
+extern void deactivate_chan(struct chan *chan, int irq);
+extern void reactivate_chan(struct chan *chan, int irq);
+extern void chan_enable_winch(struct chan *chan, struct tty_struct *tty);
 extern int enable_chan(struct line *line);
 extern void close_chan(struct list_head *chans, int delay_free_irq);
-extern int chan_window_size(struct list_head *chans, 
+extern int chan_window_size(struct line *line, 
 			     unsigned short *rows_out, 
 			     unsigned short *cols_out);
-extern int chan_config_string(struct list_head *chans, char *str, int size,
+extern int chan_config_string(struct line *line, char *str, int size,
 			      char **error_out);
 
 #endif
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 390920d63a2..73d7bc018ab 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -140,18 +140,10 @@ static int open_chan(struct list_head *chans)
 	return err;
 }
 
-void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
+void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
 {
-	struct list_head *ele;
-	struct chan *chan;
-
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-		if (chan->primary && chan->output && chan->ops->winch) {
-			register_winch(chan->fd, tty);
-			return;
-		}
-	}
+	if (chan && chan->primary && chan->ops->winch)
+		register_winch(chan->fd, tty);
 }
 
 int enable_chan(struct line *line)
@@ -258,72 +250,45 @@ void close_chan(struct list_head *chans, int delay_free_irq)
 	}
 }
 
-void deactivate_chan(struct list_head *chans, int irq)
+void deactivate_chan(struct chan *chan, int irq)
 {
-	struct list_head *ele;
-
-	struct chan *chan;
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-
-		if (chan->enabled && chan->input)
-			deactivate_fd(chan->fd, irq);
-	}
+	if (chan && chan->enabled)
+		deactivate_fd(chan->fd, irq);
 }
 
-void reactivate_chan(struct list_head *chans, int irq)
+void reactivate_chan(struct chan *chan, int irq)
 {
-	struct list_head *ele;
-	struct chan *chan;
-
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-
-		if (chan->enabled && chan->input)
-			reactivate_fd(chan->fd, irq);
-	}
+	if (chan && chan->enabled)
+		reactivate_fd(chan->fd, irq);
 }
 
-int write_chan(struct list_head *chans, const char *buf, int len,
+int write_chan(struct chan *chan, const char *buf, int len,
 	       int write_irq)
 {
-	struct list_head *ele;
-	struct chan *chan = NULL;
 	int n, ret = 0;
 
-	if (len == 0)
+	if (len == 0 || !chan || !chan->ops->write)
 		return 0;
 
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-		if (!chan->output || (chan->ops->write == NULL))
-			continue;
-
-		n = chan->ops->write(chan->fd, buf, len, chan->data);
-		if (chan->primary) {
-			ret = n;
-			if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
-				reactivate_fd(chan->fd, write_irq);
-		}
+	n = chan->ops->write(chan->fd, buf, len, chan->data);
+	if (chan->primary) {
+		ret = n;
+		if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
+			reactivate_fd(chan->fd, write_irq);
 	}
 	return ret;
 }
 
-int console_write_chan(struct list_head *chans, const char *buf, int len)
+int console_write_chan(struct chan *chan, const char *buf, int len)
 {
-	struct list_head *ele;
-	struct chan *chan;
 	int n, ret = 0;
 
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-		if (!chan->output || (chan->ops->console_write == NULL))
-			continue;
+	if (!chan || !chan->ops->console_write)
+		return 0;
 
-		n = chan->ops->console_write(chan->fd, buf, len);
-		if (chan->primary)
-			ret = n;
-	}
+	n = chan->ops->console_write(chan->fd, buf, len);
+	if (chan->primary)
+		ret = n;
 	return ret;
 }
 
@@ -340,20 +305,24 @@ int console_open_chan(struct line *line, struct console *co)
 	return 0;
 }
 
-int chan_window_size(struct list_head *chans, unsigned short *rows_out,
+int chan_window_size(struct line *line, unsigned short *rows_out,
 		      unsigned short *cols_out)
 {
-	struct list_head *ele;
 	struct chan *chan;
 
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-		if (chan->primary) {
-			if (chan->ops->window_size == NULL)
-				return 0;
-			return chan->ops->window_size(chan->fd, chan->data,
-						      rows_out, cols_out);
-		}
+	chan = line->chan_in;
+	if (chan && chan->primary) {
+		if (chan->ops->window_size == NULL)
+			return 0;
+		return chan->ops->window_size(chan->fd, chan->data,
+					      rows_out, cols_out);
+	}
+	chan = line->chan_out;
+	if (chan && chan->primary) {
+		if (chan->ops->window_size == NULL)
+			return 0;
+		return chan->ops->window_size(chan->fd, chan->data,
+					      rows_out, cols_out);
 	}
 	return 0;
 }
@@ -429,21 +398,15 @@ static int chan_pair_config_string(struct chan *in, struct chan *out,
 	return n;
 }
 
-int chan_config_string(struct list_head *chans, char *str, int size,
+int chan_config_string(struct line *line, char *str, int size,
 		       char **error_out)
 {
-	struct list_head *ele;
-	struct chan *chan, *in = NULL, *out = NULL;
+	struct chan *in = line->chan_in, *out = line->chan_out;
 
-	list_for_each(ele, chans) {
-		chan = list_entry(ele, struct chan, list);
-		if (!chan->primary)
-			continue;
-		if (chan->input)
-			in = chan;
-		if (chan->output)
-			out = chan;
-	}
+	if (in && !in->primary)
+		in = NULL;
+	if (out && !out->primary)
+		out = NULL;
 
 	return chan_pair_config_string(in, out, str, size, error_out);
 }
@@ -589,39 +552,36 @@ int parse_chan_pair(char *str, struct line *line, int device,
 	return 0;
 }
 
-void chan_interrupt(struct list_head *chans, struct delayed_work *task,
+void chan_interrupt(struct line *line, struct delayed_work *task,
 		    struct tty_struct *tty, int irq)
 {
-	struct list_head *ele, *next;
-	struct chan *chan;
+	struct chan *chan = line->chan_in;
 	int err;
 	char c;
 
-	list_for_each_safe(ele, next, chans) {
-		chan = list_entry(ele, struct chan, list);
-		if (!chan->input || (chan->ops->read == NULL))
-			continue;
-		do {
-			if (tty && !tty_buffer_request_room(tty, 1)) {
-				schedule_delayed_work(task, 1);
-				goto out;
-			}
-			err = chan->ops->read(chan->fd, &c, chan->data);
-			if (err > 0)
-				tty_receive_char(tty, c);
-		} while (err > 0);
-
-		if (err == 0)
-			reactivate_fd(chan->fd, irq);
-		if (err == -EIO) {
-			if (chan->primary) {
-				if (tty != NULL)
-					tty_hangup(tty);
-				close_chan(chans, 1);
-				return;
-			}
-			else close_one_chan(chan, 1);
+	if (!chan || !chan->ops->read)
+		goto out;
+
+	do {
+		if (tty && !tty_buffer_request_room(tty, 1)) {
+			schedule_delayed_work(task, 1);
+			goto out;
+		}
+		err = chan->ops->read(chan->fd, &c, chan->data);
+		if (err > 0)
+			tty_receive_char(tty, c);
+	} while (err > 0);
+
+	if (err == 0)
+		reactivate_fd(chan->fd, irq);
+	if (err == -EIO) {
+		if (chan->primary) {
+			if (tty != NULL)
+				tty_hangup(tty);
+			close_chan(&line->chan_list, 1);
+			return;
 		}
+		else close_one_chan(chan, 1);
 	}
  out:
 	if (tty)
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 9ffade87a8c..c1aa89cefae 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -21,7 +21,7 @@ static irqreturn_t line_interrupt(int irq, void *data)
 	struct line *line = chan->line;
 
 	if (line)
-		chan_interrupt(&line->chan_list, &line->task, line->tty, irq);
+		chan_interrupt(line, &line->task, line->tty, irq);
 	return IRQ_HANDLED;
 }
 
@@ -30,7 +30,7 @@ static void line_timer_cb(struct work_struct *work)
 	struct line *line = container_of(work, struct line, task.work);
 
 	if (!line->throttled)
-		chan_interrupt(&line->chan_list, &line->task, line->tty,
+		chan_interrupt(line, &line->task, line->tty,
 			       line->driver->read_irq);
 }
 
@@ -145,7 +145,7 @@ static int flush_buffer(struct line *line)
 		/* line->buffer + LINE_BUFSIZE is the end of the buffer! */
 		count = line->buffer + LINE_BUFSIZE - line->head;
 
-		n = write_chan(&line->chan_list, line->head, count,
+		n = write_chan(line->chan_out, line->head, count,
 			       line->driver->write_irq);
 		if (n < 0)
 			return n;
@@ -162,7 +162,7 @@ static int flush_buffer(struct line *line)
 	}
 
 	count = line->tail - line->head;
-	n = write_chan(&line->chan_list, line->head, count,
+	n = write_chan(line->chan_out, line->head, count,
 		       line->driver->write_irq);
 
 	if (n < 0)
@@ -206,7 +206,7 @@ int line_write(struct tty_struct *tty, const unsigned char *buf, int len)
 	if (line->head != line->tail)
 		ret = buffer_data(line, buf, len);
 	else {
-		n = write_chan(&line->chan_list, buf, len,
+		n = write_chan(line->chan_out, buf, len,
 			       line->driver->write_irq);
 		if (n < 0) {
 			ret = n;
@@ -318,7 +318,7 @@ void line_throttle(struct tty_struct *tty)
 {
 	struct line *line = tty->driver_data;
 
-	deactivate_chan(&line->chan_list, line->driver->read_irq);
+	deactivate_chan(line->chan_in, line->driver->read_irq);
 	line->throttled = 1;
 }
 
@@ -327,7 +327,7 @@ void line_unthrottle(struct tty_struct *tty)
 	struct line *line = tty->driver_data;
 
 	line->throttled = 0;
-	chan_interrupt(&line->chan_list, &line->task, tty,
+	chan_interrupt(line, &line->task, tty,
 		       line->driver->read_irq);
 
 	/*
@@ -336,7 +336,7 @@ void line_unthrottle(struct tty_struct *tty)
 	 * again and we shouldn't turn the interrupt back on.
 	 */
 	if (!line->throttled)
-		reactivate_chan(&line->chan_list, line->driver->read_irq);
+		reactivate_chan(line->chan_in, line->driver->read_irq);
 }
 
 static irqreturn_t line_write_interrupt(int irq, void *data)
@@ -428,11 +428,11 @@ int line_open(struct line *lines, struct tty_struct *tty)
 	INIT_DELAYED_WORK(&line->task, line_timer_cb);
 
 	if (!line->sigio) {
-		chan_enable_winch(&line->chan_list, tty);
+		chan_enable_winch(line->chan_out, tty);
 		line->sigio = 1;
 	}
 
-	chan_window_size(&line->chan_list, &tty->winsize.ws_row,
+	chan_window_size(line, &tty->winsize.ws_row,
 			 &tty->winsize.ws_col);
 out_unlock:
 	mutex_unlock(&line->count_lock);
@@ -624,7 +624,7 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
 		CONFIG_CHUNK(str, size, n, "none", 1);
 	else if (line->tty == NULL)
 		CONFIG_CHUNK(str, size, n, line->init_str, 1);
-	else n = chan_config_string(&line->chan_list, str, size, error_out);
+	else n = chan_config_string(line, str, size, error_out);
 	mutex_unlock(&line->count_lock);
 
 	return n;
@@ -761,7 +761,7 @@ static irqreturn_t winch_interrupt(int irq, void *data)
 	if (tty != NULL) {
 		line = tty->driver_data;
 		if (line != NULL) {
-			chan_window_size(&line->chan_list, &tty->winsize.ws_row,
+			chan_window_size(line, &tty->winsize.ws_row,
 					 &tty->winsize.ws_col);
 			kill_pgrp(tty->pgrp, SIGWINCH, 1);
 		}
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index d0b5ccf2379..e09801a1327 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -151,7 +151,7 @@ static void ssl_console_write(struct console *c, const char *string,
 	unsigned long flags;
 
 	spin_lock_irqsave(&line->lock, flags);
-	console_write_chan(&line->chan_list, string, len);
+	console_write_chan(line->chan_out, string, len);
 	spin_unlock_irqrestore(&line->lock, flags);
 }
 
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index fe581209d62..7663541c372 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -124,7 +124,7 @@ static void uml_console_write(struct console *console, const char *string,
 	unsigned long flags;
 
 	spin_lock_irqsave(&line->lock, flags);
-	console_write_chan(&line->chan_list, string, len);
+	console_write_chan(line->chan_out, string, len);
 	spin_unlock_irqrestore(&line->lock, flags);
 }
 
-- 
cgit v1.2.3-70-g09d2


From 5eaa3411a80fe50b3a0333389e0e259c580869e9 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Fri, 9 Sep 2011 20:20:52 -0400
Subject: um: line->have_irq is never checked...

looks like a half-arsed duplicate of line->enabled

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c | 1 -
 arch/um/drivers/line.h | 1 -
 2 files changed, 2 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index c1aa89cefae..08f63930da8 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -383,7 +383,6 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
 		err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
 					line_write_interrupt, flags,
 					driver->write_irq_name, data);
-	line->have_irq = 1;
 	return err;
 }
 
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index 6cb05a22e9b..0a1834719db 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -56,7 +56,6 @@ struct line {
 	int sigio;
 	struct delayed_work task;
 	const struct line_driver *driver;
-	int have_irq;
 };
 
 extern void line_close(struct tty_struct *tty, struct file * filp);
-- 
cgit v1.2.3-70-g09d2


From 0fcd719934cd3521ae4a977f454e75e2be60b7ff Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Sat, 10 Sep 2011 08:17:04 -0400
Subject: um: race fix: initialize delayed_work *before* registering IRQ

... since chan_interrupt() might schedule it if there's too much
incoming data.  Kill task argument of chan_interrupt(), while
we are at it - it's always &line->task.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/chan.h      |  2 +-
 arch/um/drivers/chan_kern.c | 15 ++++++++++++---
 arch/um/drivers/line.c      | 16 ++--------------
 3 files changed, 15 insertions(+), 18 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/chan.h b/arch/um/drivers/chan.h
index 5078ec701c6..d3974a91aa4 100644
--- a/arch/um/drivers/chan.h
+++ b/arch/um/drivers/chan.h
@@ -27,7 +27,7 @@ struct chan {
 	void *data;
 };
 
-extern void chan_interrupt(struct line *line, struct delayed_work *task,
+extern void chan_interrupt(struct line *line,
 			   struct tty_struct *tty, int irq);
 extern int parse_chan_pair(char *str, struct line *line, int device,
 			   const struct chan_opts *opts, char **error_out);
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 73d7bc018ab..4744b5840fa 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -146,12 +146,22 @@ void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
 		register_winch(chan->fd, tty);
 }
 
+static void line_timer_cb(struct work_struct *work)
+{
+	struct line *line = container_of(work, struct line, task.work);
+
+	if (!line->throttled)
+		chan_interrupt(line, line->tty, line->driver->read_irq);
+}
+
 int enable_chan(struct line *line)
 {
 	struct list_head *ele;
 	struct chan *chan;
 	int err;
 
+	INIT_DELAYED_WORK(&line->task, line_timer_cb);
+
 	list_for_each(ele, &line->chan_list) {
 		chan = list_entry(ele, struct chan, list);
 		err = open_one_chan(chan);
@@ -552,8 +562,7 @@ int parse_chan_pair(char *str, struct line *line, int device,
 	return 0;
 }
 
-void chan_interrupt(struct line *line, struct delayed_work *task,
-		    struct tty_struct *tty, int irq)
+void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
 {
 	struct chan *chan = line->chan_in;
 	int err;
@@ -564,7 +573,7 @@ void chan_interrupt(struct line *line, struct delayed_work *task,
 
 	do {
 		if (tty && !tty_buffer_request_room(tty, 1)) {
-			schedule_delayed_work(task, 1);
+			schedule_delayed_work(&line->task, 1);
 			goto out;
 		}
 		err = chan->ops->read(chan->fd, &c, chan->data);
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 08f63930da8..9299b8a2d17 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -21,19 +21,10 @@ static irqreturn_t line_interrupt(int irq, void *data)
 	struct line *line = chan->line;
 
 	if (line)
-		chan_interrupt(line, &line->task, line->tty, irq);
+		chan_interrupt(line, line->tty, irq);
 	return IRQ_HANDLED;
 }
 
-static void line_timer_cb(struct work_struct *work)
-{
-	struct line *line = container_of(work, struct line, task.work);
-
-	if (!line->throttled)
-		chan_interrupt(line, &line->task, line->tty,
-			       line->driver->read_irq);
-}
-
 /*
  * Returns the free space inside the ring buffer of this line.
  *
@@ -327,8 +318,7 @@ void line_unthrottle(struct tty_struct *tty)
 	struct line *line = tty->driver_data;
 
 	line->throttled = 0;
-	chan_interrupt(line, &line->task, tty,
-		       line->driver->read_irq);
+	chan_interrupt(line, tty, line->driver->read_irq);
 
 	/*
 	 * Maybe there is enough stuff pending that calling the interrupt
@@ -424,8 +414,6 @@ int line_open(struct line *lines, struct tty_struct *tty)
 	if (err) /* line_close() will be called by our caller */
 		goto out_unlock;
 
-	INIT_DELAYED_WORK(&line->task, line_timer_cb);
-
 	if (!line->sigio) {
 		chan_enable_winch(line->chan_out, tty);
 		line->sigio = 1;
-- 
cgit v1.2.3-70-g09d2


From 10c890c0a303070652f5374ea31a0b29350d14d9 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Sat, 10 Sep 2011 08:39:18 -0400
Subject: um: switch close_chan() to struct line

... and switch chan_interrupt() to directly calling close_one_chan(),
so we can lose delay_free_irq argument of close_chan() as well.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/chan.h      |  2 +-
 arch/um/drivers/chan_kern.c | 16 +++++++++-------
 arch/um/drivers/line.c      |  2 +-
 3 files changed, 11 insertions(+), 9 deletions(-)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/chan.h b/arch/um/drivers/chan.h
index d3974a91aa4..02b5a76e98d 100644
--- a/arch/um/drivers/chan.h
+++ b/arch/um/drivers/chan.h
@@ -40,7 +40,7 @@ extern void deactivate_chan(struct chan *chan, int irq);
 extern void reactivate_chan(struct chan *chan, int irq);
 extern void chan_enable_winch(struct chan *chan, struct tty_struct *tty);
 extern int enable_chan(struct line *line);
-extern void close_chan(struct list_head *chans, int delay_free_irq);
+extern void close_chan(struct line *line);
 extern int chan_window_size(struct line *line, 
 			     unsigned short *rows_out, 
 			     unsigned short *cols_out);
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 4744b5840fa..ca4c7ebfd0a 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -185,7 +185,7 @@ int enable_chan(struct line *line)
 	return 0;
 
  out_close:
-	close_chan(&line->chan_list, 0);
+	close_chan(line);
 	return err;
 }
 
@@ -246,7 +246,7 @@ static void close_one_chan(struct chan *chan, int delay_free_irq)
 	chan->fd = -1;
 }
 
-void close_chan(struct list_head *chans, int delay_free_irq)
+void close_chan(struct line *line)
 {
 	struct chan *chan;
 
@@ -255,8 +255,8 @@ void close_chan(struct list_head *chans, int delay_free_irq)
 	 * state.  Then, the first one opened will have the original state,
 	 * so it must be the last closed.
 	 */
-	list_for_each_entry_reverse(chan, chans, list) {
-		close_one_chan(chan, delay_free_irq);
+	list_for_each_entry_reverse(chan, &line->chan_list, list) {
+		close_one_chan(chan, 0);
 	}
 }
 
@@ -587,10 +587,12 @@ void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
 		if (chan->primary) {
 			if (tty != NULL)
 				tty_hangup(tty);
-			close_chan(&line->chan_list, 1);
-			return;
+			if (line->chan_out != chan)
+				close_one_chan(line->chan_out, 1);
 		}
-		else close_one_chan(chan, 1);
+		close_one_chan(chan, 1);
+		if (chan->primary)
+			return;
 	}
  out:
 	if (tty)
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 9299b8a2d17..4a3c853689a 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -465,7 +465,7 @@ void close_lines(struct line *lines, int nlines)
 	int i;
 
 	for(i = 0; i < nlines; i++)
-		close_chan(&lines[i].chan_list, 0);
+		close_chan(&lines[i]);
 }
 
 int setup_one_line(struct line *lines, int n, char *init,
-- 
cgit v1.2.3-70-g09d2


From 199eebbae45791af0f37184dd495f16a6cf5c34a Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Sat, 11 Feb 2012 03:05:32 -0500
Subject: um: deadlock in line_write_interrupt()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
---
 arch/um/drivers/line.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'arch/um/drivers/line.c')

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 4a3c853689a..4ab0d9c0911 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -344,6 +344,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
 	spin_lock(&line->lock);
 	err = flush_buffer(line);
 	if (err == 0) {
+		spin_unlock(&line->lock);
 		return IRQ_NONE;
 	} else if (err < 0) {
 		line->head = line->buffer;
-- 
cgit v1.2.3-70-g09d2