Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 98001214 authored by Ivo Sieben's avatar Ivo Sieben Committed by Greg Kroah-Hartman
Browse files

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: default avatarIvo Sieben <meltedpianoman@gmail.com>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 183d95cd
Loading
Loading
Loading
Loading
+28 −28
Original line number Original line Diff line number Diff line
@@ -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 {