diff options
author | Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> | 2005-05-01 08:58:56 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-05-01 08:58:56 -0700 |
commit | b97b77cca627b2e3e6d0015e2e14b1d1c12de0c8 (patch) | |
tree | 3fa7a55e316624a72764e5c7eead52b92a500a88 /arch/um/drivers/line.c | |
parent | 80f9507886076de0cadfdf2b87701e68156829c2 (diff) |
[PATCH] uml: redo console locking
Fix some console locking problems (including scheduling in atomic) and various
reorderings and cleanup in that code. Not yet ready for 2.6.12 probably.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/um/drivers/line.c')
-rw-r--r-- | arch/um/drivers/line.c | 318 |
1 files changed, 202 insertions, 116 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 6924f273ced..d0f97127adf 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -39,19 +39,69 @@ static void line_timer_cb(void *arg) line_interrupt(line->driver->read_irq, arg, NULL); } -static int write_room(struct line *dev) +/* Returns the free space inside the ring buffer of this line. + * + * Should be called while holding line->lock (this does not modify datas). + */ +static int write_room(struct line *line) { int n; - if (dev->buffer == NULL) - return (LINE_BUFSIZE - 1); + if (line->buffer == NULL) + return LINE_BUFSIZE - 1; + + /* This is for the case where the buffer is wrapped! */ + n = line->head - line->tail; - n = dev->head - dev->tail; if (n <= 0) - n = LINE_BUFSIZE + n; - return (n - 1); + n = LINE_BUFSIZE + n; /* The other case */ + return n - 1; +} + +int line_write_room(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + unsigned long flags; + int room; + + if (tty->stopped) + return 0; + + spin_lock_irqsave(&line->lock, flags); + room = write_room(line); + spin_unlock_irqrestore(&line->lock, flags); + + /*XXX: Warning to remove */ + if (0 == room) + printk(KERN_DEBUG "%s: %s: no room left in buffer\n", + __FUNCTION__,tty->name); + return room; } +int line_chars_in_buffer(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&line->lock, flags); + + /*write_room subtracts 1 for the needed NULL, so we readd it.*/ + ret = LINE_BUFSIZE - (write_room(line) + 1); + spin_unlock_irqrestore(&line->lock, flags); + + return ret; +} + +/* + * This copies the content of buf into the circular buffer associated with + * this line. + * The return value is the number of characters actually copied, i.e. the ones + * for which there was space: this function is not supposed to ever flush out + * the circular buffer. + * + * Must be called while holding line->lock! + */ static int buffer_data(struct line *line, const char *buf, int len) { int end, room; @@ -70,48 +120,95 @@ static int buffer_data(struct line *line, const char *buf, int len) len = (len > room) ? room : len; end = line->buffer + LINE_BUFSIZE - line->tail; - if(len < end){ + + if (len < end){ memcpy(line->tail, buf, len); line->tail += len; - } - else { + } else { + /* The circular buffer is wrapping */ memcpy(line->tail, buf, end); buf += end; memcpy(line->buffer, buf, len - end); line->tail = line->buffer + len - end; } - return(len); + return len; } +/* + * Flushes the ring buffer to the output channels. That is, write_chan is + * called, passing it line->head as buffer, and an appropriate count. + * + * On exit, returns 1 when the buffer is empty, + * 0 when the buffer is not empty on exit, + * and -errno when an error occurred. + * + * Must be called while holding line->lock!*/ static int flush_buffer(struct line *line) { int n, count; if ((line->buffer == NULL) || (line->head == line->tail)) - return(1); + return 1; if (line->tail < line->head) { + /* 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, line->driver->write_irq); if (n < 0) - return(n); - if (n == count) + return n; + if (n == count) { + /* We have flushed from ->head to buffer end, now we + * must flush only from the beginning to ->tail.*/ line->head = line->buffer; - else { + } else { line->head += n; - return(0); + return 0; } } count = line->tail - line->head; n = write_chan(&line->chan_list, line->head, count, line->driver->write_irq); - if(n < 0) return(n); + + if(n < 0) + return n; line->head += n; - return(line->head == line->tail); + return line->head == line->tail; +} + +void line_flush_buffer(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + unsigned long flags; + int err; + + /*XXX: copied from line_write, verify if it is correct!*/ + if(tty->stopped) + return; + //return 0; + + spin_lock_irqsave(&line->lock, flags); + err = flush_buffer(line); + /*if (err == 1) + err = 0;*/ + spin_unlock_irqrestore(&line->lock, flags); + //return err; +} + +/* We map both ->flush_chars and ->put_char (which go in pair) onto ->flush_buffer + * and ->write. Hope it's not that bad.*/ +void line_flush_chars(struct tty_struct *tty) +{ + line_flush_buffer(tty); +} + +void line_put_char(struct tty_struct *tty, unsigned char ch) +{ + line_write(tty, &ch, sizeof(ch)); } int line_write(struct tty_struct *tty, const unsigned char *buf, int len) @@ -120,38 +217,31 @@ int line_write(struct tty_struct *tty, const unsigned char *buf, int len) unsigned long flags; int n, err, ret = 0; - if(tty->stopped) return 0; + if(tty->stopped) + return 0; - down(&line->sem); - if(line->head != line->tail){ - local_irq_save(flags); + spin_lock_irqsave(&line->lock, flags); + if (line->head != line->tail) { ret = buffer_data(line, buf, len); err = flush_buffer(line); - local_irq_restore(flags); - if(err <= 0 && (err != -EAGAIN || !ret)) + if (err <= 0 && (err != -EAGAIN || !ret)) ret = err; - } - else { + } else { n = write_chan(&line->chan_list, buf, len, line->driver->write_irq); - if(n < 0){ + if (n < 0) { ret = n; goto out_up; } len -= n; ret += n; - if(len > 0) + if (len > 0) ret += buffer_data(line, buf + n, len); } - out_up: - up(&line->sem); - return(ret); -} - -void line_put_char(struct tty_struct *tty, unsigned char ch) -{ - line_write(tty, &ch, sizeof(ch)); +out_up: + spin_unlock_irqrestore(&line->lock, flags); + return ret; } void line_set_termios(struct tty_struct *tty, struct termios * old) @@ -159,11 +249,6 @@ void line_set_termios(struct tty_struct *tty, struct termios * old) /* nothing */ } -int line_chars_in_buffer(struct tty_struct *tty) -{ - return 0; -} - static struct { int cmd; char *level; @@ -250,7 +335,7 @@ int line_ioctl(struct tty_struct *tty, struct file * file, ret = -ENOIOCTLCMD; break; } - return(ret); + return ret; } static irqreturn_t line_write_interrupt(int irq, void *data, @@ -260,18 +345,23 @@ static irqreturn_t line_write_interrupt(int irq, void *data, struct line *line = tty->driver_data; int err; + /* Interrupts are enabled here because we registered the interrupt with + * SA_INTERRUPT (see line_setup_irq).*/ + + spin_lock_irq(&line->lock); err = flush_buffer(line); - if(err == 0) - return(IRQ_NONE); - else if(err < 0){ + if (err == 0) { + return IRQ_NONE; + } else if(err < 0) { line->head = line->buffer; line->tail = line->buffer; } + spin_unlock_irq(&line->lock); if(tty == NULL) - return(IRQ_NONE); + return IRQ_NONE; - if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && + if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && (tty->ldisc.write_wakeup != NULL)) (tty->ldisc.write_wakeup)(tty); @@ -281,9 +371,9 @@ static irqreturn_t line_write_interrupt(int irq, void *data, * writes. */ - if(waitqueue_active(&tty->write_wait)) + if (waitqueue_active(&tty->write_wait)) wake_up_interruptible(&tty->write_wait); - return(IRQ_HANDLED); + return IRQ_HANDLED; } int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) @@ -292,15 +382,18 @@ int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) struct line_driver *driver = line->driver; int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM; - if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, + if (input) + err = um_request_irq(driver->read_irq, fd, IRQ_READ, line_interrupt, flags, driver->read_irq_name, tty); - if(err) return(err); - if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, + if (err) + return err; + if (output) + err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, line_write_interrupt, flags, driver->write_irq_name, tty); line->have_irq = 1; - return(err); + return err; } void line_disable(struct tty_struct *tty, int current_irq) @@ -336,7 +429,9 @@ int line_open(struct line *lines, struct tty_struct *tty, line = &lines[tty->index]; tty->driver_data = line; - down(&line->sem); + /* The IRQ which takes this lock is not yet enabled and won't be run + * before the end, so we don't need to use spin_lock_irq.*/ + spin_lock(&line->lock); if (tty->count == 1) { if (!line->valid) { err = -ENODEV; @@ -349,6 +444,7 @@ int line_open(struct line *lines, struct tty_struct *tty, err = open_chan(&line->chan_list); if(err) goto out; } + /* Here the interrupt is registered.*/ enable_chan(&line->chan_list, tty); INIT_WORK(&line->task, line_timer_cb, tty); } @@ -362,21 +458,27 @@ int line_open(struct line *lines, struct tty_struct *tty, line->count++; out: - up(&line->sem); - return(err); + spin_unlock(&line->lock); + return err; } void line_close(struct tty_struct *tty, struct file * filp) { struct line *line = tty->driver_data; - down(&line->sem); + /* XXX: I assume this should be called in process context, not with interrupt + * disabled!*/ + spin_lock_irq(&line->lock); + + /* We ignore the error anyway! */ + flush_buffer(line); + line->count--; if (tty->count == 1) { line_disable(tty, -1); tty->driver_data = NULL; } - up(&line->sem); + spin_unlock_irq(&line->lock); } void close_lines(struct line *lines, int nlines) @@ -387,31 +489,41 @@ void close_lines(struct line *lines, int nlines) close_chan(&lines[i].chan_list); } -int line_setup(struct line *lines, int num, char *init, int all_allowed) +/* Common setup code for both startup command line and mconsole initialization. + * @lines contains the the array (of size @num) to modify; + * @init is the setup string; + * @all_allowed is a boolean saying if we can setup the whole @lines + * at once. For instance, it will be usually true for startup init. (where we + * can use con=xterm) and false for mconsole.*/ + +int line_setup(struct line *lines, unsigned int num, char *init, int all_allowed) { int i, n; char *end; - if(*init == '=') n = -1; - else { + 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); if(*end != '='){ printk(KERN_ERR "line_setup failed to parse \"%s\"\n", init); - return(0); + return 0; } init = end; } init++; - if((n >= 0) && (n >= num)){ + + if (n >= (signed int) num) { printk("line_setup - %d out of range ((0 ... %d) allowed)\n", n, num - 1); - return(0); - } - else if (n >= 0){ + return 0; + } else if (n >= 0){ if (lines[n].count > 0) { printk("line_setup - device %d is open\n", n); - return(0); + return 0; } if (lines[n].init_pri <= INIT_ONE){ lines[n].init_pri = INIT_ONE; @@ -422,13 +534,11 @@ int line_setup(struct line *lines, int num, char *init, int all_allowed) lines[n].valid = 1; } } - } - else if(!all_allowed){ + } else if(!all_allowed){ printk("line_setup - can't configure all devices from " "mconsole\n"); - return(0); - } - else { + return 0; + } else { for(i = 0; i < num; i++){ if(lines[i].init_pri <= INIT_ALL){ lines[i].init_pri = INIT_ALL; @@ -440,21 +550,21 @@ int line_setup(struct line *lines, int num, char *init, int all_allowed) } } } - return(1); + return 1; } -int line_config(struct line *lines, int num, char *str) +int line_config(struct line *lines, unsigned int num, char *str) { char *new = uml_strdup(str); if(new == NULL){ printk("line_config - uml_strdup failed\n"); - return(-ENOMEM); + return -ENOMEM; } - return(!line_setup(lines, num, new, 0)); + return !line_setup(lines, num, new, 0); } -int line_get_config(char *name, struct line *lines, int num, char *str, +int line_get_config(char *name, struct line *lines, unsigned int num, char *str, int size, char **error_out) { struct line *line; @@ -464,47 +574,33 @@ int line_get_config(char *name, struct line *lines, int num, char *str, dev = simple_strtoul(name, &end, 0); if((*end != '\0') || (end == name)){ *error_out = "line_get_config failed to parse device number"; - return(0); + return 0; } if((dev < 0) || (dev >= num)){ - *error_out = "device number of of range"; - return(0); + *error_out = "device number out of range"; + return 0; } line = &lines[dev]; - down(&line->sem); + spin_lock(&line->lock); if(!line->valid) CONFIG_CHUNK(str, size, n, "none", 1); else if(line->count == 0) CONFIG_CHUNK(str, size, n, line->init_str, 1); else n = chan_config_string(&line->chan_list, str, size, error_out); - up(&line->sem); + spin_unlock(&line->lock); - return(n); + return n; } -int line_remove(struct line *lines, int num, char *str) +int line_remove(struct line *lines, unsigned int num, char *str) { char config[sizeof("conxxxx=none\0")]; sprintf(config, "%s=none", str); - return(!line_setup(lines, num, config, 0)); -} - -int line_write_room(struct tty_struct *tty) -{ - struct line *dev = tty->driver_data; - int room; - - if (tty->stopped) - return 0; - room = write_room(dev); - if (0 == room) - printk(KERN_DEBUG "%s: %s: no room left in buffer\n", - __FUNCTION__,tty->name); - return room; + return !line_setup(lines, num, config, 0); } struct tty_driver *line_register_devfs(struct lines *set, @@ -553,7 +649,7 @@ void lines_init(struct line *lines, int nlines) for(i = 0; i < nlines; i++){ line = &lines[i]; INIT_LIST_HEAD(&line->chan_list); - sema_init(&line->sem, 1); + spin_lock_init(&line->lock); if(line->init_str != NULL){ line->init_str = uml_strdup(line->init_str); if(line->init_str == NULL) @@ -587,7 +683,7 @@ irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) "errno = %d\n", -err); printk("fd %d is losing SIGWINCH support\n", winch->tty_fd); - return(IRQ_HANDLED); + return IRQ_HANDLED; } goto out; } @@ -603,7 +699,7 @@ irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) out: if(winch->fd != -1) reactivate_fd(winch->fd, WINCH_IRQ); - return(IRQ_HANDLED); + return IRQ_HANDLED; } DECLARE_MUTEX(winch_handler_sem); @@ -625,7 +721,7 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) .pid = pid, .tty = tty }); list_add(&winch->list, &winch_handlers); - if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, + if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "winch", winch) < 0) printk("register_winch_irq - failed to register IRQ\n"); @@ -656,26 +752,16 @@ char *add_xterm_umid(char *base) int len; umid = get_umid(1); - if(umid == NULL) return(base); + if(umid == NULL) + return base; len = strlen(base) + strlen(" ()") + strlen(umid) + 1; title = kmalloc(len, GFP_KERNEL); if(title == NULL){ printk("Failed to allocate buffer for xterm title\n"); - return(base); + return base; } snprintf(title, len, "%s (%s)", base, umid); - return(title); + return title; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ |