mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-16 01:04:08 +08:00
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:
commit
3cfade53c7
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user