Commit 97d0c931 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-4.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver fixes from Greg KH:
 "Here are a few bug fixes for the tty core that resolve reported
  issues, and some serial driver fixes as well (including the
  much-reported imx driver problem)

  All of these have been in linux-next with no reported problems"

* tag 'tty-4.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  drivers/tty: require read access for controlling terminal
  serial: 8250: add uart_config entry for PORT_RT2880
  tty: fix data race on tty_buffer.commit
  tty: fix data race in tty_buffer_flush
  tty: fix data race in flush_to_ldisc
  tty: fix stall caused by missing memory barrier in drivers/tty/n_tty.c
  serial: atmel: fix error path of probe function
  tty: don't leak cdev in tty_cdev_add()
  Revert "serial: imx: remove unbalanced clk_prepare"
parents 91dbc047 0c556271
...@@ -343,8 +343,7 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) ...@@ -343,8 +343,7 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
spin_lock_irqsave(&tty->ctrl_lock, flags); spin_lock_irqsave(&tty->ctrl_lock, flags);
tty->ctrl_status |= TIOCPKT_FLUSHREAD; tty->ctrl_status |= TIOCPKT_FLUSHREAD;
spin_unlock_irqrestore(&tty->ctrl_lock, flags); spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (waitqueue_active(&tty->link->read_wait)) wake_up_interruptible(&tty->link->read_wait);
wake_up_interruptible(&tty->link->read_wait);
} }
} }
...@@ -1382,8 +1381,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) ...@@ -1382,8 +1381,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
put_tty_queue(c, ldata); put_tty_queue(c, ldata);
smp_store_release(&ldata->canon_head, ldata->read_head); smp_store_release(&ldata->canon_head, ldata->read_head);
kill_fasync(&tty->fasync, SIGIO, POLL_IN); kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait)) wake_up_interruptible_poll(&tty->read_wait, POLLIN);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
return 0; return 0;
} }
} }
...@@ -1667,8 +1665,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, ...@@ -1667,8 +1665,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
if ((read_cnt(ldata) >= ldata->minimum_to_wake) || L_EXTPROC(tty)) { if ((read_cnt(ldata) >= ldata->minimum_to_wake) || L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN); kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait)) wake_up_interruptible_poll(&tty->read_wait, POLLIN);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
} }
} }
...@@ -1887,10 +1884,8 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) ...@@ -1887,10 +1884,8 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
} }
/* The termios change make the tty ready for I/O */ /* The termios change make the tty ready for I/O */
if (waitqueue_active(&tty->write_wait)) wake_up_interruptible(&tty->write_wait);
wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
} }
/** /**
......
...@@ -261,6 +261,14 @@ configured less than Maximum supported fifo bytes */ ...@@ -261,6 +261,14 @@ configured less than Maximum supported fifo bytes */
UART_FCR7_64BYTE, UART_FCR7_64BYTE,
.flags = UART_CAP_FIFO, .flags = UART_CAP_FIFO,
}, },
[PORT_RT2880] = {
.name = "Palmchip BK-3103",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO,
},
}; };
/* Uart divisor latch read */ /* Uart divisor latch read */
......
...@@ -2786,7 +2786,7 @@ static int atmel_serial_probe(struct platform_device *pdev) ...@@ -2786,7 +2786,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
ret = atmel_init_gpios(port, &pdev->dev); ret = atmel_init_gpios(port, &pdev->dev);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize GPIOs."); dev_err(&pdev->dev, "Failed to initialize GPIOs.");
goto err; goto err_clear_bit;
} }
ret = atmel_init_port(port, pdev); ret = atmel_init_port(port, pdev);
......
...@@ -1631,12 +1631,12 @@ imx_console_write(struct console *co, const char *s, unsigned int count) ...@@ -1631,12 +1631,12 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
int locked = 1; int locked = 1;
int retval; int retval;
retval = clk_prepare_enable(sport->clk_per); retval = clk_enable(sport->clk_per);
if (retval) if (retval)
return; return;
retval = clk_prepare_enable(sport->clk_ipg); retval = clk_enable(sport->clk_ipg);
if (retval) { if (retval) {
clk_disable_unprepare(sport->clk_per); clk_disable(sport->clk_per);
return; return;
} }
...@@ -1675,8 +1675,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count) ...@@ -1675,8 +1675,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
if (locked) if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags); spin_unlock_irqrestore(&sport->port.lock, flags);
clk_disable_unprepare(sport->clk_ipg); clk_disable(sport->clk_ipg);
clk_disable_unprepare(sport->clk_per); clk_disable(sport->clk_per);
} }
/* /*
...@@ -1777,7 +1777,15 @@ imx_console_setup(struct console *co, char *options) ...@@ -1777,7 +1777,15 @@ imx_console_setup(struct console *co, char *options)
retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
clk_disable_unprepare(sport->clk_ipg); clk_disable(sport->clk_ipg);
if (retval) {
clk_unprepare(sport->clk_ipg);
goto error_console;
}
retval = clk_prepare(sport->clk_per);
if (retval)
clk_disable_unprepare(sport->clk_ipg);
error_console: error_console:
return retval; return retval;
......
...@@ -242,7 +242,10 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) ...@@ -242,7 +242,10 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
atomic_inc(&buf->priority); atomic_inc(&buf->priority);
mutex_lock(&buf->lock); mutex_lock(&buf->lock);
while ((next = buf->head->next) != NULL) { /* paired w/ release in __tty_buffer_request_room; ensures there are
* no pending memory accesses to the freed buffer
*/
while ((next = smp_load_acquire(&buf->head->next)) != NULL) {
tty_buffer_free(port, buf->head); tty_buffer_free(port, buf->head);
buf->head = next; buf->head = next;
} }
...@@ -290,7 +293,10 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, ...@@ -290,7 +293,10 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size,
if (n != NULL) { if (n != NULL) {
n->flags = flags; n->flags = flags;
buf->tail = n; buf->tail = n;
b->commit = b->used; /* paired w/ acquire in flush_to_ldisc(); ensures
* flush_to_ldisc() sees buffer data.
*/
smp_store_release(&b->commit, b->used);
/* paired w/ acquire in flush_to_ldisc(); ensures the /* paired w/ acquire in flush_to_ldisc(); ensures the
* latest commit value can be read before the head is * latest commit value can be read before the head is
* advanced to the next buffer * advanced to the next buffer
...@@ -393,7 +399,10 @@ void tty_schedule_flip(struct tty_port *port) ...@@ -393,7 +399,10 @@ void tty_schedule_flip(struct tty_port *port)
{ {
struct tty_bufhead *buf = &port->buf; struct tty_bufhead *buf = &port->buf;
buf->tail->commit = buf->tail->used; /* paired w/ acquire in flush_to_ldisc(); ensures
* flush_to_ldisc() sees buffer data.
*/
smp_store_release(&buf->tail->commit, buf->tail->used);
schedule_work(&buf->work); schedule_work(&buf->work);
} }
EXPORT_SYMBOL(tty_schedule_flip); EXPORT_SYMBOL(tty_schedule_flip);
...@@ -467,7 +476,7 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -467,7 +476,7 @@ static void flush_to_ldisc(struct work_struct *work)
struct tty_struct *tty; struct tty_struct *tty;
struct tty_ldisc *disc; struct tty_ldisc *disc;
tty = port->itty; tty = READ_ONCE(port->itty);
if (tty == NULL) if (tty == NULL)
return; return;
...@@ -491,7 +500,10 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -491,7 +500,10 @@ static void flush_to_ldisc(struct work_struct *work)
* is advancing to the next buffer * is advancing to the next buffer
*/ */
next = smp_load_acquire(&head->next); next = smp_load_acquire(&head->next);
count = head->commit - head->read; /* paired w/ release in __tty_buffer_request_room() or in
* tty_buffer_flush(); ensures we see the committed buffer data
*/
count = smp_load_acquire(&head->commit) - head->read;
if (!count) { if (!count) {
if (next == NULL) { if (next == NULL) {
check_other_closed(tty); check_other_closed(tty);
......
...@@ -2128,8 +2128,24 @@ static int tty_open(struct inode *inode, struct file *filp) ...@@ -2128,8 +2128,24 @@ static int tty_open(struct inode *inode, struct file *filp)
if (!noctty && if (!noctty &&
current->signal->leader && current->signal->leader &&
!current->signal->tty && !current->signal->tty &&
tty->session == NULL) tty->session == NULL) {
__proc_set_tty(tty); /*
* Don't let a process that only has write access to the tty
* obtain the privileges associated with having a tty as
* controlling terminal (being able to reopen it with full
* access through /dev/tty, being able to perform pushback).
* Many distributions set the group of all ttys to "tty" and
* grant write-only access to all terminals for setgid tty
* binaries, which should not imply full privileges on all ttys.
*
* This could theoretically break old code that performs open()
* on a write-only file descriptor. In that case, it might be
* necessary to also permit this if
* inode_permission(inode, MAY_READ) == 0.
*/
if (filp->f_mode & FMODE_READ)
__proc_set_tty(tty);
}
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
tty_unlock(tty); tty_unlock(tty);
...@@ -2418,7 +2434,7 @@ static int fionbio(struct file *file, int __user *p) ...@@ -2418,7 +2434,7 @@ static int fionbio(struct file *file, int __user *p)
* Takes ->siglock() when updating signal->tty * Takes ->siglock() when updating signal->tty
*/ */
static int tiocsctty(struct tty_struct *tty, int arg) static int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
{ {
int ret = 0; int ret = 0;
...@@ -2452,6 +2468,13 @@ static int tiocsctty(struct tty_struct *tty, int arg) ...@@ -2452,6 +2468,13 @@ static int tiocsctty(struct tty_struct *tty, int arg)
goto unlock; goto unlock;
} }
} }
/* See the comment in tty_open(). */
if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) {
ret = -EPERM;
goto unlock;
}
proc_set_tty(tty); proc_set_tty(tty);
unlock: unlock:
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
...@@ -2844,7 +2867,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -2844,7 +2867,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
no_tty(); no_tty();
return 0; return 0;
case TIOCSCTTY: case TIOCSCTTY:
return tiocsctty(tty, arg); return tiocsctty(tty, file, arg);
case TIOCGPGRP: case TIOCGPGRP:
return tiocgpgrp(tty, real_tty, p); return tiocgpgrp(tty, real_tty, p);
case TIOCSPGRP: case TIOCSPGRP:
...@@ -3151,13 +3174,18 @@ struct class *tty_class; ...@@ -3151,13 +3174,18 @@ struct class *tty_class;
static int tty_cdev_add(struct tty_driver *driver, dev_t dev, static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
unsigned int index, unsigned int count) unsigned int index, unsigned int count)
{ {
int err;
/* init here, since reused cdevs cause crashes */ /* init here, since reused cdevs cause crashes */
driver->cdevs[index] = cdev_alloc(); driver->cdevs[index] = cdev_alloc();
if (!driver->cdevs[index]) if (!driver->cdevs[index])
return -ENOMEM; return -ENOMEM;
cdev_init(driver->cdevs[index], &tty_fops); driver->cdevs[index]->ops = &tty_fops;
driver->cdevs[index]->owner = driver->owner; driver->cdevs[index]->owner = driver->owner;
return cdev_add(driver->cdevs[index], dev, count); err = cdev_add(driver->cdevs[index], dev, count);
if (err)
kobject_put(&driver->cdevs[index]->kobj);
return err;
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment