in n_tty.c [2028:2196]
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char *kbuf, size_t nr,
void **cookie, unsigned long offset)
{
struct n_tty_data *ldata = tty->disc_data;
unsigned char *kb = kbuf;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int c;
int minimum, time;
ssize_t retval = 0;
long timeout;
bool 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;
/*
* Internal serialization of reads.
*/
if (file->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&ldata->atomic_read_lock))
return -EAGAIN;
} else {
if (mutex_lock_interruptible(&ldata->atomic_read_lock))
return -ERESTARTSYS;
}
down_read(&tty->termios_rwsem);
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!ldata->icanon) {
minimum = MIN_CHAR(tty);
if (minimum) {
time = (HZ / 10) * TIME_CHAR(tty);
} else {
timeout = (HZ / 10) * TIME_CHAR(tty);
minimum = 1;
}
}
packet = tty->ctrl.packet;
tail = ldata->read_tail;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
/* First test for status change. */
if (packet && tty->link->ctrl.pktstatus) {
unsigned char cs;
if (kb != kbuf)
break;
spin_lock_irq(&tty->link->ctrl.lock);
cs = tty->link->ctrl.pktstatus;
tty->link->ctrl.pktstatus = 0;
spin_unlock_irq(&tty->link->ctrl.lock);
*kb++ = cs;
nr--;
break;
}
if (!input_available_p(tty, 0)) {
up_read(&tty->termios_rwsem);
tty_buffer_flush_work(tty->port);
down_read(&tty->termios_rwsem);
if (!input_available_p(tty, 0)) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
if (tty_hung_up_p(file))
break;
/*
* Abort readers for ttys which never actually
* get hung up. See __tty_hangup().
*/
if (test_bit(TTY_HUPPING, &tty->flags))
break;
if (!timeout)
break;
if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
up_read(&tty->termios_rwsem);
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
timeout);
down_read(&tty->termios_rwsem);
continue;
}
}
if (ldata->icanon && !L_EXTPROC(tty)) {
if (canon_copy_from_read_buf(tty, &kb, &nr))
goto more_to_be_read;
} else {
/* Deal with packet mode. */
if (packet && kb == kbuf) {
*kb++ = TIOCPKT_DATA;
nr--;
}
/*
* 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 (kb - kbuf >= minimum)
break;
if (time)
timeout = time;
}
if (tail != ldata->read_tail)
n_tty_kick_worker(tty);
up_read(&tty->termios_rwsem);
remove_wait_queue(&tty->read_wait, &wait);
mutex_unlock(&ldata->atomic_read_lock);
if (kb - kbuf)
retval = kb - kbuf;
return retval;
}