summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/n_tty.c70
1 files changed, 47 insertions, 23 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 0f76b9096840..20983afb6086 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -74,6 +74,11 @@
#define ECHO_OP_SET_CANON_COL 0x81
#define ECHO_OP_ERASE_TAB 0x82
+#define ECHO_COMMIT_WATERMARK 256
+#define ECHO_BLOCK 256
+#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
+
+
#undef N_TTY_TRACE
#ifdef N_TTY_TRACE
# define n_tty_trace(f, args...) trace_printk(f, ##args)
@@ -766,15 +771,40 @@ static void __process_echoes(struct tty_struct *tty)
}
}
+ /* If the echo buffer is nearly full (so that the possibility exists
+ * of echo overrun before the next commit), then discard enough
+ * data at the tail to prevent a subsequent overrun */
+ while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
+ if (echo_buf(ldata, tail == ECHO_OP_START)) {
+ if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB)
+ tail += 3;
+ else
+ tail += 2;
+ } else
+ tail++;
+ }
+
ldata->echo_tail = tail;
}
static void commit_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
+ size_t nr, old;
+ size_t head;
+
+ head = ldata->echo_head;
+ old = ldata->echo_commit - ldata->echo_tail;
+
+ /* Process committed echoes if the accumulated # of bytes
+ * is over the threshold (and try again each time another
+ * block is accumulated) */
+ nr = head - ldata->echo_tail;
+ if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
+ return;
mutex_lock(&ldata->output_lock);
- ldata->echo_commit = ldata->echo_head;
+ ldata->echo_commit = head;
__process_echoes(tty);
mutex_unlock(&ldata->output_lock);
@@ -797,37 +827,29 @@ static void process_echoes(struct tty_struct *tty)
tty->ops->flush_chars(tty);
}
+static void flush_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (!L_ECHO(tty) || ldata->echo_commit == ldata->echo_head)
+ return;
+
+ mutex_lock(&ldata->output_lock);
+ ldata->echo_commit = ldata->echo_head;
+ __process_echoes(tty);
+ mutex_unlock(&ldata->output_lock);
+}
+
/**
* add_echo_byte - add a byte to the echo buffer
* @c: unicode byte to echo
* @ldata: n_tty data
*
* Add a character or operation byte to the echo buffer.
- *
- * Locks: may claim output_lock to prevent concurrent modify of
- * echo_tail by process_echoes().
*/
-static void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
+static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
{
- if (ldata->echo_head - ldata->echo_tail == N_TTY_BUF_SIZE) {
- size_t head = ldata->echo_head;
-
- mutex_lock(&ldata->output_lock);
- /*
- * Since the buffer start position needs to be advanced,
- * be sure to step by a whole operation byte group.
- */
- if (echo_buf(ldata, head) == ECHO_OP_START) {
- if (echo_buf(ldata, head + 1) == ECHO_OP_ERASE_TAB)
- ldata->echo_tail += 3;
- else
- ldata->echo_tail += 2;
- } else
- ldata->echo_tail++;
- mutex_unlock(&ldata->output_lock);
- }
-
*echo_buf_addr(ldata, ldata->echo_head++) = c;
}
@@ -1515,6 +1537,8 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
break;
}
}
+
+ flush_echoes(tty);
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}