summaryrefslogtreecommitdiff
path: root/drivers/tty/n_tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r--drivers/tty/n_tty.c72
1 files changed, 45 insertions, 27 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 4bf0fc0843d7..eddeb7889e62 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -79,6 +79,9 @@ struct n_tty_data {
unsigned long overrun_time;
int num_overrun;
+ /* non-atomic */
+ bool no_room;
+
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char echo_overrun:1;
@@ -114,25 +117,10 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
return put_user(x, ptr);
}
-/**
- * n_tty_set_room - receive space
- * @tty: terminal
- *
- * Updates tty->receive_room to reflect the currently available space
- * in the input buffer, and re-schedules the flip buffer work if space
- * just became available.
- *
- * Locks: Concurrent update is protected with read_lock
- */
-
-static int set_room(struct tty_struct *tty)
+static int receive_room(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
int left;
- int old_left;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&ldata->read_lock, flags);
if (I_PARMRK(tty)) {
/* Multiply read_cnt by 3, since each byte might take up to
@@ -150,18 +138,27 @@ static int set_room(struct tty_struct *tty)
*/
if (left <= 0)
left = ldata->icanon && !ldata->canon_data;
- old_left = tty->receive_room;
- tty->receive_room = left;
- raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
- return left && !old_left;
+ return left;
}
+/**
+ * n_tty_set_room - receive space
+ * @tty: terminal
+ *
+ * Re-schedules the flip buffer work if space just became available.
+ *
+ * Locks: Concurrent update is protected with read_lock
+ */
+
static void n_tty_set_room(struct tty_struct *tty)
{
+ struct n_tty_data *ldata = tty->disc_data;
+
/* Did this open up the receive buffer? We may need to flip */
- if (set_room(tty)) {
+ if (unlikely(ldata->no_room) && receive_room(tty)) {
+ ldata->no_room = 0;
+
WARN_RATELIMIT(tty->port->itty == NULL,
"scheduling with invalid itty\n");
/* see if ldisc has been killed - if so, this means that
@@ -1408,8 +1405,8 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
* calls one at a time and in order (or using flush_to_ldisc)
*/
-static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
- char *fp, int count)
+static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
const unsigned char *p;
@@ -1464,8 +1461,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
tty->ops->flush_chars(tty);
}
- set_room(tty);
-
if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) ||
L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
@@ -1480,7 +1475,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
*/
while (1) {
tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
- if (tty->receive_room >= TTY_THRESHOLD_THROTTLE)
+ if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
break;
if (!tty_throttle_safe(tty))
break;
@@ -1488,6 +1483,28 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
__tty_set_flow_change(tty, 0);
}
+static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ __receive_buf(tty, cp, fp, count);
+}
+
+static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int room;
+
+ tty->receive_room = room = receive_room(tty);
+ if (!room)
+ ldata->no_room = 1;
+ count = min(count, room);
+ if (count)
+ __receive_buf(tty, cp, fp, count);
+
+ return count;
+}
+
int is_ignored(int sig)
{
return (sigismember(&current->blocked, sig) ||
@@ -2203,6 +2220,7 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = {
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup,
.fasync = n_tty_fasync,
+ .receive_buf2 = n_tty_receive_buf2,
};
/**