Merge tag 'tty-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH: "Here is the big set of tty/serial driver changes for 5.12-rc1. Nothing huge, just lots of good cleanups and additions: - n_tty line discipline cleanups - vt core cleanups and reworks to make the code more "modern" - stm32 driver additions - tty led support added to the tty core and led layer - minor serial driver fixups and additions All of these have been in linux-next for a while with no reported issues" * tag 'tty-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (54 commits) serial: core: Remove BUG_ON(in_interrupt()) check vt_ioctl: Remove in_interrupt() check dt-bindings: serial: imx: Switch to my personal address vt: keyboard, use new API for keyboard_tasklet serial: stm32: improve platform_get_irq condition handling in init_port serial: ifx6x60: Remove driver for deprecated platform tty: fix up iterate_tty_read() EOVERFLOW handling tty: fix up hung_up_tty_read() conversion tty: fix up hung_up_tty_write() conversion tty: teach the n_tty ICANON case about the new "cookie continuations" too tty: teach n_tty line discipline about the new "cookie continuations" tty: clean up legacy leftovers from n_tty line discipline tty: implement read_iter tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer serial: remove sirf prima/atlas driver serial: mxs-auart: Remove <asm/cacheflush.h> serial: mxs-auart: Remove serial_mxs_probe_dt() serial: fsl_lpuart: Use of_device_get_match_data() dt-bindings: serial: renesas,hscif: Add r8a779a0 support tty: serial: Drop unused efm32 serial driver ...
This commit is contained in:
@ -164,29 +164,24 @@ static void zero_buffer(struct tty_struct *tty, u8 *buffer, int size)
|
||||
memset(buffer, 0x00, size);
|
||||
}
|
||||
|
||||
static int tty_copy_to_user(struct tty_struct *tty, void __user *to,
|
||||
size_t tail, size_t n)
|
||||
static void tty_copy(struct tty_struct *tty, void *to, size_t tail, size_t n)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
size_t size = N_TTY_BUF_SIZE - tail;
|
||||
void *from = read_buf_addr(ldata, tail);
|
||||
int uncopied;
|
||||
|
||||
if (n > size) {
|
||||
tty_audit_add_data(tty, from, size);
|
||||
uncopied = copy_to_user(to, from, size);
|
||||
zero_buffer(tty, from, size - uncopied);
|
||||
if (uncopied)
|
||||
return uncopied;
|
||||
memcpy(to, from, size);
|
||||
zero_buffer(tty, from, size);
|
||||
to += size;
|
||||
n -= size;
|
||||
from = ldata->read_buf;
|
||||
}
|
||||
|
||||
tty_audit_add_data(tty, from, n);
|
||||
uncopied = copy_to_user(to, from, n);
|
||||
zero_buffer(tty, from, n - uncopied);
|
||||
return uncopied;
|
||||
memcpy(to, from, n);
|
||||
zero_buffer(tty, from, n);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1894,8 +1889,10 @@ static void n_tty_close(struct tty_struct *tty)
|
||||
if (tty->link)
|
||||
n_tty_packet_mode_flush(tty);
|
||||
|
||||
down_write(&tty->termios_rwsem);
|
||||
vfree(ldata);
|
||||
tty->disc_data = NULL;
|
||||
up_write(&tty->termios_rwsem);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1944,42 +1941,38 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
|
||||
/**
|
||||
* copy_from_read_buf - copy read data directly
|
||||
* @tty: terminal device
|
||||
* @b: user data
|
||||
* @kbp: data
|
||||
* @nr: size of data
|
||||
*
|
||||
* Helper function to speed up n_tty_read. It is only called when
|
||||
* ICANON is off; it copies characters straight from the tty queue to
|
||||
* user space directly. It can be profitably called twice; once to
|
||||
* drain the space from the tail pointer to the (physical) end of the
|
||||
* buffer, and once to drain the space from the (physical) beginning of
|
||||
* the buffer to head pointer.
|
||||
* ICANON is off; it copies characters straight from the tty queue.
|
||||
*
|
||||
* Called under the ldata->atomic_read_lock sem
|
||||
*
|
||||
* Returns true if it successfully copied data, but there is still
|
||||
* more data to be had.
|
||||
*
|
||||
* n_tty_read()/consumer path:
|
||||
* caller holds non-exclusive termios_rwsem
|
||||
* read_tail published
|
||||
*/
|
||||
|
||||
static int copy_from_read_buf(struct tty_struct *tty,
|
||||
unsigned char __user **b,
|
||||
static bool copy_from_read_buf(struct tty_struct *tty,
|
||||
unsigned char **kbp,
|
||||
size_t *nr)
|
||||
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
int retval;
|
||||
size_t n;
|
||||
bool is_eof;
|
||||
size_t head = smp_load_acquire(&ldata->commit_head);
|
||||
size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
|
||||
|
||||
retval = 0;
|
||||
n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail);
|
||||
n = min(*nr, n);
|
||||
if (n) {
|
||||
unsigned char *from = read_buf_addr(ldata, tail);
|
||||
retval = copy_to_user(*b, from, n);
|
||||
n -= retval;
|
||||
memcpy(*kbp, from, n);
|
||||
is_eof = n == 1 && *from == EOF_CHAR(tty);
|
||||
tty_audit_add_data(tty, from, n);
|
||||
zero_buffer(tty, from, n);
|
||||
@ -1987,22 +1980,25 @@ static int copy_from_read_buf(struct tty_struct *tty,
|
||||
/* Turn single EOF into zero-length read */
|
||||
if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
|
||||
(head == ldata->read_tail))
|
||||
n = 0;
|
||||
*b += n;
|
||||
return false;
|
||||
*kbp += n;
|
||||
*nr -= n;
|
||||
|
||||
/* If we have more to copy, let the caller know */
|
||||
return head != ldata->read_tail;
|
||||
}
|
||||
return retval;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* canon_copy_from_read_buf - copy read data in canonical mode
|
||||
* @tty: terminal device
|
||||
* @b: user data
|
||||
* @kbp: data
|
||||
* @nr: size of data
|
||||
*
|
||||
* Helper function for n_tty_read. It is only called when ICANON is on;
|
||||
* it copies one line of input up to and including the line-delimiting
|
||||
* character into the user-space buffer.
|
||||
* character into the result buffer.
|
||||
*
|
||||
* NB: When termios is changed from non-canonical to canonical mode and
|
||||
* the read buffer contains data, n_tty_set_termios() simulates an EOF
|
||||
@ -2017,21 +2013,22 @@ static int copy_from_read_buf(struct tty_struct *tty,
|
||||
* read_tail published
|
||||
*/
|
||||
|
||||
static int canon_copy_from_read_buf(struct tty_struct *tty,
|
||||
unsigned char __user **b,
|
||||
size_t *nr)
|
||||
static bool canon_copy_from_read_buf(struct tty_struct *tty,
|
||||
unsigned char **kbp,
|
||||
size_t *nr)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
size_t n, size, more, c;
|
||||
size_t eol;
|
||||
size_t tail;
|
||||
int ret, found = 0;
|
||||
size_t tail, canon_head;
|
||||
int found = 0;
|
||||
|
||||
/* N.B. avoid overrun if nr == 0 */
|
||||
if (!*nr)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
|
||||
canon_head = smp_load_acquire(&ldata->canon_head);
|
||||
n = min(*nr + 1, canon_head - ldata->read_tail);
|
||||
|
||||
tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
|
||||
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
|
||||
@ -2061,10 +2058,8 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
|
||||
n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n",
|
||||
__func__, eol, found, n, c, tail, more);
|
||||
|
||||
ret = tty_copy_to_user(tty, *b, tail, n);
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
*b += n;
|
||||
tty_copy(tty, *kbp, tail, n);
|
||||
*kbp += n;
|
||||
*nr -= n;
|
||||
|
||||
if (found)
|
||||
@ -2077,8 +2072,11 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
|
||||
else
|
||||
ldata->push = 0;
|
||||
tty_audit_push();
|
||||
return false;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* No EOL found - do a continuation retry if there is more data */
|
||||
return ldata->read_tail != canon_head;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2129,10 +2127,11 @@ static int job_control(struct tty_struct *tty, struct file *file)
|
||||
*/
|
||||
|
||||
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
unsigned char __user *buf, size_t nr)
|
||||
unsigned char *kbuf, size_t nr,
|
||||
void **cookie, unsigned long offset)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
unsigned char __user *b = buf;
|
||||
unsigned char *kb = kbuf;
|
||||
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
||||
int c;
|
||||
int minimum, time;
|
||||
@ -2141,6 +2140,30 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
int packet;
|
||||
size_t tail;
|
||||
|
||||
/*
|
||||
* Is this a continuation of a read started earler?
|
||||
*
|
||||
* If so, we still hold the atomic_read_lock and the
|
||||
* termios_rwsem, and can just continue to copy data.
|
||||
*/
|
||||
if (*cookie) {
|
||||
if (ldata->icanon && !L_EXTPROC(tty)) {
|
||||
if (canon_copy_from_read_buf(tty, &kb, &nr))
|
||||
return kb - kbuf;
|
||||
} else {
|
||||
if (copy_from_read_buf(tty, &kb, &nr))
|
||||
return kb - kbuf;
|
||||
}
|
||||
|
||||
/* No more data - release locks and stop retries */
|
||||
n_tty_kick_worker(tty);
|
||||
n_tty_check_unthrottle(tty);
|
||||
up_read(&tty->termios_rwsem);
|
||||
mutex_unlock(&ldata->atomic_read_lock);
|
||||
*cookie = NULL;
|
||||
return kb - kbuf;
|
||||
}
|
||||
|
||||
c = job_control(tty, file);
|
||||
if (c < 0)
|
||||
return c;
|
||||
@ -2178,17 +2201,13 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
/* First test for status change. */
|
||||
if (packet && tty->link->ctrl_status) {
|
||||
unsigned char cs;
|
||||
if (b != buf)
|
||||
if (kb != kbuf)
|
||||
break;
|
||||
spin_lock_irq(&tty->link->ctrl_lock);
|
||||
cs = tty->link->ctrl_status;
|
||||
tty->link->ctrl_status = 0;
|
||||
spin_unlock_irq(&tty->link->ctrl_lock);
|
||||
if (put_user(cs, b)) {
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
*kb++ = cs;
|
||||
nr--;
|
||||
break;
|
||||
}
|
||||
@ -2231,33 +2250,35 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
}
|
||||
|
||||
if (ldata->icanon && !L_EXTPROC(tty)) {
|
||||
retval = canon_copy_from_read_buf(tty, &b, &nr);
|
||||
if (retval)
|
||||
break;
|
||||
if (canon_copy_from_read_buf(tty, &kb, &nr))
|
||||
goto more_to_be_read;
|
||||
} else {
|
||||
int uncopied;
|
||||
|
||||
/* Deal with packet mode. */
|
||||
if (packet && b == buf) {
|
||||
if (put_user(TIOCPKT_DATA, b)) {
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
if (packet && kb == kbuf) {
|
||||
*kb++ = TIOCPKT_DATA;
|
||||
nr--;
|
||||
}
|
||||
|
||||
uncopied = copy_from_read_buf(tty, &b, &nr);
|
||||
uncopied += copy_from_read_buf(tty, &b, &nr);
|
||||
if (uncopied) {
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
/*
|
||||
* Copy data, and if there is more to be had
|
||||
* and we have nothing more to wait for, then
|
||||
* let's mark us for retries.
|
||||
*
|
||||
* NOTE! We return here with both the termios_sem
|
||||
* and atomic_read_lock still held, the retries
|
||||
* will release them when done.
|
||||
*/
|
||||
if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) {
|
||||
more_to_be_read:
|
||||
remove_wait_queue(&tty->read_wait, &wait);
|
||||
*cookie = cookie;
|
||||
return kb - kbuf;
|
||||
}
|
||||
}
|
||||
|
||||
n_tty_check_unthrottle(tty);
|
||||
|
||||
if (b - buf >= minimum)
|
||||
if (kb - kbuf >= minimum)
|
||||
break;
|
||||
if (time)
|
||||
timeout = time;
|
||||
@ -2269,8 +2290,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||
remove_wait_queue(&tty->read_wait, &wait);
|
||||
mutex_unlock(&ldata->atomic_read_lock);
|
||||
|
||||
if (b - buf)
|
||||
retval = b - buf;
|
||||
if (kb - kbuf)
|
||||
retval = kb - kbuf;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
Reference in New Issue
Block a user