Merge branch 'tty-splice' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux into tty-next

Fixes both the "splice/sendfile to a tty" and "splice/sendfile from a
tty" regression from 5.10.

* 'tty-splice' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux:
  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
  tty: implement write_iter
This commit is contained in:
Greg Kroah-Hartman 2021-01-21 09:40:55 +01:00
commit 3cfade53c7
14 changed files with 264 additions and 151 deletions

View File

@ -802,7 +802,8 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
* We don't provide read/write/poll interface for user space.
*/
static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset)
{
return 0;
}
@ -819,29 +820,28 @@ static __poll_t hci_uart_tty_poll(struct tty_struct *tty,
return 0;
}
static struct tty_ldisc_ops hci_uart_ldisc = {
.owner = THIS_MODULE,
.magic = TTY_LDISC_MAGIC,
.name = "n_hci",
.open = hci_uart_tty_open,
.close = hci_uart_tty_close,
.read = hci_uart_tty_read,
.write = hci_uart_tty_write,
.ioctl = hci_uart_tty_ioctl,
.compat_ioctl = hci_uart_tty_ioctl,
.poll = hci_uart_tty_poll,
.receive_buf = hci_uart_tty_receive,
.write_wakeup = hci_uart_tty_wakeup,
};
static int __init hci_uart_init(void)
{
static struct tty_ldisc_ops hci_uart_ldisc;
int err;
BT_INFO("HCI UART driver ver %s", VERSION);
/* Register the tty discipline */
memset(&hci_uart_ldisc, 0, sizeof(hci_uart_ldisc));
hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
hci_uart_ldisc.name = "n_hci";
hci_uart_ldisc.open = hci_uart_tty_open;
hci_uart_ldisc.close = hci_uart_tty_close;
hci_uart_ldisc.read = hci_uart_tty_read;
hci_uart_ldisc.write = hci_uart_tty_write;
hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
hci_uart_ldisc.compat_ioctl = hci_uart_tty_ioctl;
hci_uart_ldisc.poll = hci_uart_tty_poll;
hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup;
hci_uart_ldisc.owner = THIS_MODULE;
err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
if (err) {
BT_ERR("HCI line discipline registration failed. (%d)", err);

View File

@ -156,7 +156,9 @@ out:
* returning 0 characters.
*/
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file,
unsigned char *kbuf, size_t nr,
void **cookie, unsigned long offset)
{
struct serport *serport = (struct serport*) tty->disc_data;
struct serio *serio;

View File

@ -259,7 +259,8 @@ static int ppp_asynctty_hangup(struct tty_struct *tty)
*/
static ssize_t
ppp_asynctty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t count)
unsigned char *buf, size_t count,
void **cookie, unsigned long offset)
{
return -EAGAIN;
}

View File

@ -257,7 +257,8 @@ static int ppp_sync_hangup(struct tty_struct *tty)
*/
static ssize_t
ppp_sync_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t count)
unsigned char *buf, size_t count,
void **cookie, unsigned long offset)
{
return -EAGAIN;
}

View File

@ -2559,7 +2559,8 @@ static void gsmld_write_wakeup(struct tty_struct *tty)
*/
static ssize_t gsmld_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset)
{
return -EOPNOTSUPP;
}

View File

@ -416,13 +416,19 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data,
* Returns the number of bytes returned or error code.
*/
static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
__u8 __user *buf, size_t nr)
__u8 *kbuf, size_t nr,
void **cookie, unsigned long offset)
{
struct n_hdlc *n_hdlc = tty->disc_data;
int ret = 0;
struct n_hdlc_buf *rbuf;
DECLARE_WAITQUEUE(wait, current);
/* Is this a repeated call for an rbuf we already found earlier? */
rbuf = *cookie;
if (rbuf)
goto have_rbuf;
add_wait_queue(&tty->read_wait, &wait);
for (;;) {
@ -436,25 +442,8 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
set_current_state(TASK_INTERRUPTIBLE);
rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
if (rbuf) {
if (rbuf->count > nr) {
/* too large for caller's buffer */
ret = -EOVERFLOW;
} else {
__set_current_state(TASK_RUNNING);
if (copy_to_user(buf, rbuf->buf, rbuf->count))
ret = -EFAULT;
else
ret = rbuf->count;
}
if (n_hdlc->rx_free_buf_list.count >
DEFAULT_RX_BUF_COUNT)
kfree(rbuf);
else
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf);
if (rbuf)
break;
}
/* no data */
if (tty_io_nonblock(tty, file)) {
@ -473,6 +462,39 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
remove_wait_queue(&tty->read_wait, &wait);
__set_current_state(TASK_RUNNING);
if (!rbuf)
return ret;
*cookie = rbuf;
have_rbuf:
/* Have we used it up entirely? */
if (offset >= rbuf->count)
goto done_with_rbuf;
/* More data to go, but can't copy any more? EOVERFLOW */
ret = -EOVERFLOW;
if (!nr)
goto done_with_rbuf;
/* Copy as much data as possible */
ret = rbuf->count - offset;
if (ret > nr)
ret = nr;
memcpy(kbuf, rbuf->buf+offset, ret);
offset += ret;
/* If we still have data left, we leave the rbuf in the cookie */
if (offset < rbuf->count)
return ret;
done_with_rbuf:
*cookie = NULL;
if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT)
kfree(rbuf);
else
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf);
return ret;
} /* end of n_hdlc_tty_read() */

View File

@ -20,7 +20,8 @@ static void n_null_close(struct tty_struct *tty)
}
static ssize_t n_null_read(struct tty_struct *tty, struct file *file,
unsigned char __user * buf, size_t nr)
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset)
{
return -EOPNOTSUPP;
}

View File

@ -129,7 +129,7 @@ static void remove_client_block(struct r3964_info *pInfo,
static int r3964_open(struct tty_struct *tty);
static void r3964_close(struct tty_struct *tty);
static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
unsigned char __user * buf, size_t nr);
void *cookie, unsigned char *buf, size_t nr);
static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr);
static int r3964_ioctl(struct tty_struct *tty, struct file *file,
@ -1058,7 +1058,8 @@ static void r3964_close(struct tty_struct *tty)
}
static ssize_t r3964_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 r3964_info *pInfo = tty->disc_data;
struct r3964_client_info *pClient;
@ -1109,10 +1110,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
kfree(pMsg);
TRACE_M("r3964_read - msg kfree %p", pMsg);
if (copy_to_user(buf, &theMsg, ret)) {
ret = -EFAULT;
goto unlock;
}
memcpy(kbuf, &theMsg, ret);
TRACE_PS("read - return %d", ret);
goto unlock;

View File

@ -118,7 +118,9 @@ static void n_tracerouter_close(struct tty_struct *tty)
* -EINVAL
*/
static ssize_t n_tracerouter_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr) {
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset)
{
return -EINVAL;
}

View File

@ -115,7 +115,9 @@ static void n_tracesink_close(struct tty_struct *tty)
* -EINVAL
*/
static ssize_t n_tracesink_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr) {
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset)
{
return -EINVAL;
}

View File

@ -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);
}
/**
@ -1946,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);
@ -1989,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
@ -2019,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);
@ -2063,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)
@ -2079,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;
}
extern ssize_t redirected_tty_write(struct file *, const char __user *,
@ -2134,10 +2130,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;
@ -2146,6 +2143,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;
@ -2183,17 +2204,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;
}
@ -2236,33 +2253,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;
@ -2274,8 +2293,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;
}

View File

@ -142,10 +142,9 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */
/* Mutex to protect creating and releasing a tty */
DEFINE_MUTEX(tty_mutex);
static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
ssize_t redirected_tty_write(struct file *, const char __user *,
size_t, loff_t *);
static ssize_t tty_read(struct kiocb *, struct iov_iter *);
static ssize_t tty_write(struct kiocb *, struct iov_iter *);
ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *);
static __poll_t tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
@ -477,8 +476,10 @@ static void tty_show_fdinfo(struct seq_file *m, struct file *file)
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.read_iter = tty_read,
.write_iter = tty_write,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
@ -490,8 +491,10 @@ static const struct file_operations tty_fops = {
static const struct file_operations console_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = redirected_tty_write,
.read_iter = tty_read,
.write_iter = redirected_tty_write,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
@ -623,9 +626,9 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
/* This breaks for file handles being sent over AF_UNIX sockets ? */
list_for_each_entry(priv, &tty->tty_files, list) {
filp = priv->file;
if (filp->f_op->write == redirected_tty_write)
if (filp->f_op->write_iter == redirected_tty_write)
cons_filp = filp;
if (filp->f_op->write != tty_write)
if (filp->f_op->write_iter != tty_write)
continue;
closecount++;
__tty_fasync(-1, filp, 0); /* can't block */
@ -848,6 +851,65 @@ static void tty_update_time(struct timespec64 *time)
time->tv_sec = sec;
}
/*
* Iterate on the ldisc ->read() function until we've gotten all
* the data the ldisc has for us.
*
* The "cookie" is something that the ldisc read function can fill
* in to let us know that there is more data to be had.
*
* We promise to continue to call the ldisc until it stops returning
* data or clears the cookie. The cookie may be something that the
* ldisc maintains state for and needs to free.
*/
static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty,
struct file *file, struct iov_iter *to)
{
int retval = 0;
void *cookie = NULL;
unsigned long offset = 0;
char kernel_buf[64];
size_t count = iov_iter_count(to);
do {
int size, copied;
size = count > sizeof(kernel_buf) ? sizeof(kernel_buf) : count;
size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset);
if (!size)
break;
/*
* A ldisc read error return will override any previously copied
* data (eg -EOVERFLOW from HDLC)
*/
if (size < 0) {
memzero_explicit(kernel_buf, sizeof(kernel_buf));
return size;
}
copied = copy_to_iter(kernel_buf, size, to);
offset += copied;
count -= copied;
/*
* If the user copy failed, we still need to do another ->read()
* call if we had a cookie to let the ldisc clear up.
*
* But make sure size is zeroed.
*/
if (unlikely(copied != size)) {
count = 0;
retval = -EFAULT;
}
} while (cookie);
/* We always clear tty buffer in case they contained passwords */
memzero_explicit(kernel_buf, sizeof(kernel_buf));
return offset ? offset : retval;
}
/**
* tty_read - read method for tty device files
* @file: pointer to tty file
@ -863,10 +925,10 @@ static void tty_update_time(struct timespec64 *time)
* read calls may be outstanding in parallel.
*/
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to)
{
int i;
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
@ -879,12 +941,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
/* We want to wait for the line discipline to sort out in this
situation */
ld = tty_ldisc_ref_wait(tty);
if (!ld)
return hung_up_tty_read(file, buf, count, ppos);
if (ld->ops->read)
i = ld->ops->read(tty, file, buf, count);
else
i = -EIO;
i = -EIO;
if (ld && ld->ops->read)
i = iterate_tty_read(ld, tty, file, to);
tty_ldisc_deref(ld);
if (i > 0)
@ -918,9 +977,9 @@ static inline ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const char __user *buf,
size_t count)
struct iov_iter *from)
{
size_t count = iov_iter_count(from);
ssize_t ret, written = 0;
unsigned int chunk;
@ -972,14 +1031,20 @@ static inline ssize_t do_tty_write(
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))
if (copy_from_iter(tty->write_buf, size, from) != size)
break;
ret = write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
/* FIXME! Have Al check this! */
if (ret != size)
iov_iter_revert(from, size-ret);
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
@ -1039,9 +1104,9 @@ void tty_write_message(struct tty_struct *tty, char *msg)
* write method will not be invoked in parallel for each device.
*/
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
@ -1054,18 +1119,15 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
if (tty->ops->write_room == NULL)
tty_err(tty, "missing write_room method\n");
ld = tty_ldisc_ref_wait(tty);
if (!ld)
return hung_up_tty_write(file, buf, count, ppos);
if (!ld->ops->write)
if (!ld || !ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
ret = do_tty_write(ld->ops->write, tty, file, from);
tty_ldisc_deref(ld);
return ret;
}
ssize_t redirected_tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *p = NULL;
@ -1076,11 +1138,11 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
if (p) {
ssize_t res;
res = vfs_write(p, buf, count, &p->f_pos);
res = vfs_iocb_iter_write(p, iocb, iter);
fput(p);
return res;
}
return tty_write(file, buf, count, ppos);
return tty_write(iocb, iter);
}
/*
@ -2332,7 +2394,7 @@ static int tioccons(struct file *file)
{
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (file->f_op->write == redirected_tty_write) {
if (file->f_op->write_iter == redirected_tty_write) {
struct file *f;
spin_lock(&redirect_lock);
f = redirect;
@ -2925,7 +2987,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
static int this_tty(const void *t, struct file *file, unsigned fd)
{
if (likely(file->f_op->read != tty_read))
if (likely(file->f_op->read_iter != tty_read))
return 0;
return file_tty(file) != t ? 0 : fd + 1;
}

View File

@ -185,7 +185,8 @@ struct tty_ldisc_ops {
void (*close)(struct tty_struct *);
void (*flush_buffer)(struct tty_struct *tty);
ssize_t (*read)(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr);
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset);
ssize_t (*write)(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr);
int (*ioctl)(struct tty_struct *tty, struct file *file,

View File

@ -292,7 +292,8 @@ static int nci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
/* We don't provide read/write/poll interface for user space. */
static ssize_t nci_uart_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
unsigned char *buf, size_t nr,
void **cookie, unsigned long offset)
{
return 0;
}