tty: Use raw spin lock to protect the TTY read section
The "normal" spin lock that guards the N_TTY line discipline read section is replaced by a raw spin lock. On a PREEMP_RT system this prevents unwanted scheduling overhead when data is read at the same time as data is being received: while RX IRQ threaded handling is busy a TTY read call is performed from a RT priority > threaded IRQ priority. The read call tries to take the read section spin lock (held by the threaded IRQ) which blocks and causes a context switch to/from the threaded IRQ handler until the spin lock is unlocked. Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
183d95cdd8
commit
98001214c0
1 changed files with 28 additions and 28 deletions
|
@ -100,7 +100,7 @@ struct n_tty_data {
|
||||||
struct mutex atomic_read_lock;
|
struct mutex atomic_read_lock;
|
||||||
struct mutex output_lock;
|
struct mutex output_lock;
|
||||||
struct mutex echo_lock;
|
struct mutex echo_lock;
|
||||||
spinlock_t read_lock;
|
raw_spinlock_t read_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
|
static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
|
||||||
|
@ -182,9 +182,9 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
|
||||||
* The problem of stomping on the buffers ends here.
|
* The problem of stomping on the buffers ends here.
|
||||||
* Why didn't anyone see this one coming? --AJK
|
* Why didn't anyone see this one coming? --AJK
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
put_tty_queue_nolock(c, ldata);
|
put_tty_queue_nolock(c, ldata);
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -218,9 +218,9 @@ static void reset_buffer_flags(struct tty_struct *tty)
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
ldata->read_head = ldata->read_tail = ldata->read_cnt = 0;
|
ldata->read_head = ldata->read_tail = ldata->read_cnt = 0;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
|
|
||||||
mutex_lock(&ldata->echo_lock);
|
mutex_lock(&ldata->echo_lock);
|
||||||
ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0;
|
ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0;
|
||||||
|
@ -276,7 +276,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
ssize_t n = 0;
|
ssize_t n = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
if (!ldata->icanon) {
|
if (!ldata->icanon) {
|
||||||
n = ldata->read_cnt;
|
n = ldata->read_cnt;
|
||||||
} else if (ldata->canon_data) {
|
} else if (ldata->canon_data) {
|
||||||
|
@ -284,7 +284,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
|
||||||
ldata->canon_head - ldata->read_tail :
|
ldata->canon_head - ldata->read_tail :
|
||||||
ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail);
|
ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,19 +915,19 @@ static void eraser(unsigned char c, struct tty_struct *tty)
|
||||||
kill_type = WERASE;
|
kill_type = WERASE;
|
||||||
else {
|
else {
|
||||||
if (!L_ECHO(tty)) {
|
if (!L_ECHO(tty)) {
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
|
ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
|
||||||
(N_TTY_BUF_SIZE - 1));
|
(N_TTY_BUF_SIZE - 1));
|
||||||
ldata->read_head = ldata->canon_head;
|
ldata->read_head = ldata->canon_head;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
|
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
|
ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
|
||||||
(N_TTY_BUF_SIZE - 1));
|
(N_TTY_BUF_SIZE - 1));
|
||||||
ldata->read_head = ldata->canon_head;
|
ldata->read_head = ldata->canon_head;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
finish_erasing(ldata);
|
finish_erasing(ldata);
|
||||||
echo_char(KILL_CHAR(tty), tty);
|
echo_char(KILL_CHAR(tty), tty);
|
||||||
/* Add a newline if ECHOK is on and ECHOKE is off. */
|
/* Add a newline if ECHOK is on and ECHOKE is off. */
|
||||||
|
@ -961,10 +961,10 @@ static void eraser(unsigned char c, struct tty_struct *tty)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1);
|
cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1);
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
ldata->read_head = head;
|
ldata->read_head = head;
|
||||||
ldata->read_cnt -= cnt;
|
ldata->read_cnt -= cnt;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
if (L_ECHO(tty)) {
|
if (L_ECHO(tty)) {
|
||||||
if (L_ECHOPRT(tty)) {
|
if (L_ECHOPRT(tty)) {
|
||||||
if (!ldata->erasing) {
|
if (!ldata->erasing) {
|
||||||
|
@ -1344,12 +1344,12 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
|
||||||
put_tty_queue(c, ldata);
|
put_tty_queue(c, ldata);
|
||||||
|
|
||||||
handle_newline:
|
handle_newline:
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
set_bit(ldata->read_head, ldata->read_flags);
|
set_bit(ldata->read_head, ldata->read_flags);
|
||||||
put_tty_queue_nolock(c, ldata);
|
put_tty_queue_nolock(c, ldata);
|
||||||
ldata->canon_head = ldata->read_head;
|
ldata->canon_head = ldata->read_head;
|
||||||
ldata->canon_data++;
|
ldata->canon_data++;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
|
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
|
||||||
if (waitqueue_active(&tty->read_wait))
|
if (waitqueue_active(&tty->read_wait))
|
||||||
wake_up_interruptible(&tty->read_wait);
|
wake_up_interruptible(&tty->read_wait);
|
||||||
|
@ -1423,7 +1423,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||||
unsigned long cpuflags;
|
unsigned long cpuflags;
|
||||||
|
|
||||||
if (ldata->real_raw) {
|
if (ldata->real_raw) {
|
||||||
spin_lock_irqsave(&ldata->read_lock, cpuflags);
|
raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
|
||||||
i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
|
i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
|
||||||
N_TTY_BUF_SIZE - ldata->read_head);
|
N_TTY_BUF_SIZE - ldata->read_head);
|
||||||
i = min(count, i);
|
i = min(count, i);
|
||||||
|
@ -1439,7 +1439,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||||
memcpy(ldata->read_buf + ldata->read_head, cp, i);
|
memcpy(ldata->read_buf + ldata->read_head, cp, i);
|
||||||
ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
|
ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
|
||||||
ldata->read_cnt += i;
|
ldata->read_cnt += i;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
|
||||||
} else {
|
} else {
|
||||||
for (i = count, p = cp, f = fp; i; i--, p++) {
|
for (i = count, p = cp, f = fp; i; i--, p++) {
|
||||||
if (f)
|
if (f)
|
||||||
|
@ -1635,7 +1635,7 @@ static int n_tty_open(struct tty_struct *tty)
|
||||||
mutex_init(&ldata->atomic_read_lock);
|
mutex_init(&ldata->atomic_read_lock);
|
||||||
mutex_init(&ldata->output_lock);
|
mutex_init(&ldata->output_lock);
|
||||||
mutex_init(&ldata->echo_lock);
|
mutex_init(&ldata->echo_lock);
|
||||||
spin_lock_init(&ldata->read_lock);
|
raw_spin_lock_init(&ldata->read_lock);
|
||||||
|
|
||||||
/* These are ugly. Currently a malloc failure here can panic */
|
/* These are ugly. Currently a malloc failure here can panic */
|
||||||
ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
||||||
|
@ -1703,10 +1703,10 @@ static int copy_from_read_buf(struct tty_struct *tty,
|
||||||
bool is_eof;
|
bool is_eof;
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail);
|
n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail);
|
||||||
n = min(*nr, n);
|
n = min(*nr, n);
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
if (n) {
|
if (n) {
|
||||||
retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n);
|
retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n);
|
||||||
n -= retval;
|
n -= retval;
|
||||||
|
@ -1714,13 +1714,13 @@ static int copy_from_read_buf(struct tty_struct *tty,
|
||||||
ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty);
|
ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty);
|
||||||
tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n,
|
tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n,
|
||||||
ldata->icanon);
|
ldata->icanon);
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1);
|
ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1);
|
||||||
ldata->read_cnt -= n;
|
ldata->read_cnt -= n;
|
||||||
/* Turn single EOF into zero-length read */
|
/* Turn single EOF into zero-length read */
|
||||||
if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt)
|
if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt)
|
||||||
n = 0;
|
n = 0;
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
*b += n;
|
*b += n;
|
||||||
*nr -= n;
|
*nr -= n;
|
||||||
}
|
}
|
||||||
|
@ -1900,7 +1900,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||||
|
|
||||||
if (ldata->icanon && !L_EXTPROC(tty)) {
|
if (ldata->icanon && !L_EXTPROC(tty)) {
|
||||||
/* N.B. avoid overrun if nr == 0 */
|
/* N.B. avoid overrun if nr == 0 */
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
while (nr && ldata->read_cnt) {
|
while (nr && ldata->read_cnt) {
|
||||||
int eol;
|
int eol;
|
||||||
|
|
||||||
|
@ -1918,25 +1918,25 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||||
if (--ldata->canon_data < 0)
|
if (--ldata->canon_data < 0)
|
||||||
ldata->canon_data = 0;
|
ldata->canon_data = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
|
|
||||||
if (!eol || (c != __DISABLED_CHAR)) {
|
if (!eol || (c != __DISABLED_CHAR)) {
|
||||||
if (tty_put_user(tty, c, b++)) {
|
if (tty_put_user(tty, c, b++)) {
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
b--;
|
b--;
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nr--;
|
nr--;
|
||||||
}
|
}
|
||||||
if (eol) {
|
if (eol) {
|
||||||
tty_audit_push(tty);
|
tty_audit_push(tty);
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spin_lock_irqsave(&ldata->read_lock, flags);
|
raw_spin_lock_irqsave(&ldata->read_lock, flags);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ldata->read_lock, flags);
|
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
|
||||||
if (retval)
|
if (retval)
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue