summary refs log tree commit diff
path: root/arch
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-09-10 08:17:04 -0400
committerRichard Weinberger <richard@nod.at>2012-03-25 00:29:54 +0100
commit0fcd719934cd3521ae4a977f454e75e2be60b7ff (patch)
treef6679910030fa0361ab52603987b72bc5e7ef314 /arch
parent5eaa3411a80fe50b3a0333389e0e259c580869e9 (diff)
downloadlinux-0fcd719934cd3521ae4a977f454e75e2be60b7ff.tar.gz
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/um/drivers/chan.h2
-rw-r--r--arch/um/drivers/chan_kern.c15
-rw-r--r--arch/um/drivers/line.c16
3 files changed, 15 insertions, 18 deletions
diff --git a/arch/um/drivers/chan.h b/arch/um/drivers/chan.h
index 5078ec701c61..d3974a91aa48 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 73d7bc018ab2..4744b5840fa9 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 08f63930da8b..9299b8a2d171 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;