Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/tty.c
Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
* while unlocked. | * while unlocked. | ||||
*/ | */ | ||||
static int | static int | ||||
tty_watermarks(struct tty *tp) | tty_watermarks(struct tty *tp) | ||||
{ | { | ||||
size_t bs = 0; | size_t bs = 0; | ||||
int error; | int error; | ||||
tty_assert_locked(tp); | |||||
ttydisc_assert_locked(tp); | |||||
/* Provide an input buffer for 2 seconds of data. */ | /* Provide an input buffer for 2 seconds of data. */ | ||||
if (tp->t_termios.c_cflag & CREAD) | if (tp->t_termios.c_cflag & CREAD) | ||||
bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); | bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); | ||||
error = ttyinq_setsize(&tp->t_inq, tp, bs); | error = ttyinq_setsize(&tp->t_inq, tp, bs); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | goto out; | ||||
/* Set low watermark at 10% (when 90% is available). */ | /* Set low watermark at 10% (when 90% is available). */ | ||||
tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; | tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; | ||||
/* Provide an output buffer for 2 seconds of data. */ | /* Provide an output buffer for 2 seconds of data. */ | ||||
bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); | bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); | ||||
error = ttyoutq_setsize(&tp->t_outq, tp, bs); | error = ttyoutq_setsize(&tp->t_outq, tp, bs); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | goto out; | ||||
/* Set low watermark at 10% (when 90% is available). */ | /* Set low watermark at 10% (when 90% is available). */ | ||||
tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; | tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; | ||||
return (0); | out: | ||||
return (error); | |||||
} | } | ||||
static int | static int | ||||
tty_drain(struct tty *tp, int leaving) | tty_drain(struct tty *tp, int leaving) | ||||
{ | { | ||||
sbintime_t timeout_at; | sbintime_t timeout_at; | ||||
size_t bytes; | size_t bytes; | ||||
int error; | int error; | ||||
ttydisc_assert_locked(tp); | |||||
if (ttyhook_hashook(tp, getc_inject)) | if (ttyhook_hashook(tp, getc_inject)) | ||||
/* buffer is inaccessible */ | /* buffer is inaccessible */ | ||||
return (0); | return (0); | ||||
/* | /* | ||||
* For close(), use the recent historic timeout of "1 second without | * For close(), use the recent historic timeout of "1 second without | ||||
* making progress". For tcdrain(), use t_drainwait as the timeout, | * making progress". For tcdrain(), use t_drainwait as the timeout, | ||||
* with zero meaning "no timeout" which gives POSIX behavior. | * with zero meaning "no timeout" which gives POSIX behavior. | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | ttydev_enter(struct tty *tp) | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
ttydev_leave(struct tty *tp) | ttydev_leave(struct tty *tp) | ||||
{ | { | ||||
/* | |||||
* The locking in this function, like elsewhere, is also a nightmare | |||||
* that will be corrected when the tty lock gets converted to a | |||||
* sleepable lock. | |||||
*/ | |||||
tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { | if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { | ||||
/* Device is still opened somewhere. */ | /* Device is still opened somewhere. */ | ||||
if (ttydisc_lock_owned(tp)) | |||||
ttydisc_unlock(tp); | |||||
tty_unlock(tp); | tty_unlock(tp); | ||||
return; | return; | ||||
} | } | ||||
if (!ttydisc_lock_owned(tp)) | |||||
ttydisc_lock(tp); | |||||
tp->t_flags |= TF_OPENCLOSE; | tp->t_flags |= TF_OPENCLOSE; | ||||
/* Remove console TTY. */ | /* Remove console TTY. */ | ||||
constty_clear(tp); | constty_clear(tp); | ||||
/* Drain any output. */ | /* | ||||
* Drain any output. Pick up ttydisc_lock in advance of tty_drain, | |||||
* which will perhaps want to sleep and can't handle having both locks. | |||||
*/ | |||||
if (!tty_gone(tp)) | if (!tty_gone(tp)) | ||||
tty_drain(tp, 1); | tty_drain(tp, 1); | ||||
ttydisc_close(tp); | ttydisc_close(tp); | ||||
/* Free i/o queues now since they might be large. */ | /* Free i/o queues now since they might be large. */ | ||||
ttyinq_free(&tp->t_inq); | ttyinq_free(&tp->t_inq); | ||||
tp->t_inlow = 0; | tp->t_inlow = 0; | ||||
ttyoutq_free(&tp->t_outq); | ttyoutq_free(&tp->t_outq); | ||||
tp->t_outlow = 0; | tp->t_outlow = 0; | ||||
if (!tty_gone(tp)) | if (!tty_gone(tp)) | ||||
ttydevsw_close(tp); | ttydevsw_close(tp); | ||||
tp->t_flags &= ~TF_OPENCLOSE; | tp->t_flags &= ~TF_OPENCLOSE; | ||||
cv_broadcast(&tp->t_dcdwait); | cv_broadcast(&tp->t_dcdwait); | ||||
ttydisc_unlock(tp); | |||||
tty_rel_free(tp); | tty_rel_free(tp); | ||||
} | } | ||||
/* | /* | ||||
* Operations that are exposed through the character device in /dev. | * Operations that are exposed through the character device in /dev. | ||||
*/ | */ | ||||
static int | static int | ||||
ttydev_open(struct cdev *dev, int oflags, int devtype __unused, | ttydev_open(struct cdev *dev, int oflags, int devtype __unused, | ||||
Show All 10 Lines | if (tty_gone(tp)) { | ||||
tty_unlock(tp); | tty_unlock(tp); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* | /* | ||||
* Block when other processes are currently opening or closing | * Block when other processes are currently opening or closing | ||||
* the TTY. | * the TTY. | ||||
*/ | */ | ||||
ttydisc_lock(tp); | |||||
while (tp->t_flags & TF_OPENCLOSE) { | while (tp->t_flags & TF_OPENCLOSE) { | ||||
error = tty_wait(tp, &tp->t_dcdwait); | error = tty_wait(tp, &tp->t_dcdwait); | ||||
if (error != 0) { | if (error != 0) { | ||||
tty_unlock(tp); | tty_unlock(tp); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
tp->t_flags |= TF_OPENCLOSE; | tp->t_flags |= TF_OPENCLOSE; | ||||
ttydisc_unlock(tp); | |||||
/* | /* | ||||
* Make sure the "tty" and "cua" device cannot be opened at the | * Make sure the "tty" and "cua" device cannot be opened at the | ||||
* same time. The console is a "tty" device. | * same time. The console is a "tty" device. | ||||
*/ | */ | ||||
if (TTY_CALLOUT(tp, dev)) { | if (TTY_CALLOUT(tp, dev)) { | ||||
if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) { | if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) { | ||||
error = EBUSY; | error = EBUSY; | ||||
goto done; | goto done; | ||||
} | } | ||||
} else { | } else { | ||||
if (tp->t_flags & TF_OPENED_OUT) { | if (tp->t_flags & TF_OPENED_OUT) { | ||||
error = EBUSY; | error = EBUSY; | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { | if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { | ||||
error = EBUSY; | error = EBUSY; | ||||
goto done; | goto done; | ||||
} | } | ||||
if (!tty_opened(tp)) { | if (!tty_opened(tp)) { | ||||
/* Set proper termios flags. */ | /* | ||||
* Set proper termios flags. Further mutations of t_termios | |||||
* will require the ttydisc lock. | |||||
*/ | |||||
if (TTY_CALLOUT(tp, dev)) | if (TTY_CALLOUT(tp, dev)) | ||||
tp->t_termios = tp->t_termios_init_out; | tp->t_termios = tp->t_termios_init_out; | ||||
else | else | ||||
tp->t_termios = tp->t_termios_init_in; | tp->t_termios = tp->t_termios_init_in; | ||||
ttydisc_lock(tp); | |||||
ttydevsw_param(tp, &tp->t_termios); | ttydevsw_param(tp, &tp->t_termios); | ||||
/* Prevent modem control on callout devices and /dev/console. */ | /* Prevent modem control on callout devices and /dev/console. */ | ||||
if (TTY_CALLOUT(tp, dev) || dev == dev_console) | if (TTY_CALLOUT(tp, dev) || dev == dev_console) | ||||
tp->t_termios.c_cflag |= CLOCAL; | tp->t_termios.c_cflag |= CLOCAL; | ||||
if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0) | if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0) | ||||
ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); | ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); | ||||
error = ttydevsw_open(tp); | error = ttydevsw_open(tp); | ||||
if (error != 0) | if (error != 0) | ||||
goto done; | goto done; | ||||
ttydisc_open(tp); | ttydisc_open(tp); | ||||
error = tty_watermarks(tp); | error = tty_watermarks(tp); | ||||
/* tty_watermarks dropped the ttydisc lock. */ | |||||
if (error != 0) | if (error != 0) | ||||
goto done; | goto done; | ||||
} else { | |||||
ttydisc_lock(tp); | |||||
} | } | ||||
/* Wait for Carrier Detect. */ | /* Wait for Carrier Detect. */ | ||||
if ((oflags & O_NONBLOCK) == 0 && | if ((oflags & O_NONBLOCK) == 0 && | ||||
(tp->t_termios.c_cflag & CLOCAL) == 0) { | (tp->t_termios.c_cflag & CLOCAL) == 0) { | ||||
while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { | while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { | ||||
error = tty_wait(tp, &tp->t_dcdwait); | error = tty_wait(tp, &tp->t_dcdwait); | ||||
if (error != 0) | if (error != 0) | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
if (dev == dev_console) | if (dev == dev_console) | ||||
tp->t_flags |= TF_OPENED_CONS; | tp->t_flags |= TF_OPENED_CONS; | ||||
else if (TTY_CALLOUT(tp, dev)) | else if (TTY_CALLOUT(tp, dev)) | ||||
tp->t_flags |= TF_OPENED_OUT; | tp->t_flags |= TF_OPENED_OUT; | ||||
else | else | ||||
tp->t_flags |= TF_OPENED_IN; | tp->t_flags |= TF_OPENED_IN; | ||||
MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | ||||
(tp->t_flags & TF_OPENED_OUT) == 0); | (tp->t_flags & TF_OPENED_OUT) == 0); | ||||
done: tp->t_flags &= ~TF_OPENCLOSE; | done: | ||||
if (!ttydisc_lock_owned(tp)) | |||||
ttydisc_lock(tp); | |||||
tp->t_flags &= ~TF_OPENCLOSE; | |||||
cv_broadcast(&tp->t_dcdwait); | cv_broadcast(&tp->t_dcdwait); | ||||
ttydev_leave(tp); | ttydev_leave(tp); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
ttydev_close(struct cdev *dev, int fflag, int devtype __unused, | ttydev_close(struct cdev *dev, int fflag, int devtype __unused, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
tty_lock(tp); | tty_lock(tp); | ||||
ttydisc_lock(tp); | |||||
/* | /* | ||||
* Don't actually close the device if it is being used as the | * Don't actually close the device if it is being used as the | ||||
* console. | * console. | ||||
*/ | */ | ||||
MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || | ||||
(tp->t_flags & TF_OPENED_OUT) == 0); | (tp->t_flags & TF_OPENED_OUT) == 0); | ||||
if (dev == dev_console) | if (dev == dev_console) | ||||
tp->t_flags &= ~TF_OPENED_CONS; | tp->t_flags &= ~TF_OPENED_CONS; | ||||
else | else | ||||
tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT); | tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT); | ||||
if (tp->t_flags & TF_OPENED) { | if (tp->t_flags & TF_OPENED) { | ||||
ttydisc_unlock(tp); | |||||
tty_unlock(tp); | tty_unlock(tp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* If revoking, flush output now to avoid draining it later. */ | /* If revoking, flush output now to avoid draining it later. */ | ||||
if ((fflag & FREVOKE) != 0) { | if ((fflag & FREVOKE) != 0) { | ||||
tty_flush(tp, FWRITE); | tty_flush(tp, FWRITE); | ||||
knlist_delete(&tp->t_inpoll.si_note, td, 1); | knlist_delete(&tp->t_inpoll.si_note, td, 1); | ||||
Show All 12 Lines | ttydev_close(struct cdev *dev, int fflag, int devtype __unused, | ||||
return (0); | return (0); | ||||
} | } | ||||
static __inline int | static __inline int | ||||
tty_is_ctty(struct tty *tp, struct proc *p) | tty_is_ctty(struct tty *tp, struct proc *p) | ||||
{ | { | ||||
tty_assert_locked(tp); | KASSERT(tty_lock_owned(tp) || ttydisc_lock_owned(tp), | ||||
("neither ttymtx nor ttydiscmtx owned")); | |||||
return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); | return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); | ||||
} | } | ||||
int | int | ||||
tty_wait_background(struct tty *tp, struct thread *td, int sig) | tty_wait_background(struct tty *tp, struct thread *td, int sig) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
struct pgrp *pg; | struct pgrp *pg; | ||||
ksiginfo_t ksi; | ksiginfo_t ksi; | ||||
int error; | int error; | ||||
MPASS(sig == SIGTTIN || sig == SIGTTOU); | MPASS(sig == SIGTTIN || sig == SIGTTOU); | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
p = td->td_proc; | p = td->td_proc; | ||||
for (;;) { | for (;;) { | ||||
pg = p->p_pgrp; | pg = p->p_pgrp; | ||||
PGRP_LOCK(pg); | PGRP_LOCK(pg); | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (sig != 0) { | ||||
ksi.ksi_code = SI_KERNEL; | ksi.ksi_code = SI_KERNEL; | ||||
ksi.ksi_signo = sig; | ksi.ksi_signo = sig; | ||||
sig = 0; | sig = 0; | ||||
} | } | ||||
pgsignal(pg, ksi.ksi_signo, 1, &ksi); | pgsignal(pg, ksi.ksi_signo, 1, &ksi); | ||||
PGRP_UNLOCK(pg); | PGRP_UNLOCK(pg); | ||||
error = tty_wait(tp, &tp->t_bgwait); | error = tty_wait(tp, &tp->t_bgwait); | ||||
kevans: This is also fixed... this patch grew organically as I worked out the intricacies of tty bits… | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) | ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) | ||||
{ | { | ||||
struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
int error; | int error; | ||||
/* | |||||
* We'll pick up the tty lock just long enough to ensure we're alive and | |||||
* well, then we'll pick up the ttydisc lock and drop the tty lock -- we | |||||
* won't be needing it for the rest of this as we serialize I/O on the | |||||
* ttydisc lock. | |||||
*/ | |||||
error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
ttydisc_lock(tp); | |||||
error = ttydisc_read(tp, uio, ioflag); | error = ttydisc_read(tp, uio, ioflag); | ||||
ttydisc_unlock(tp); | |||||
tty_unlock(tp); | tty_unlock(tp); | ||||
/* | /* | ||||
* The read() call should not throw an error when the device is | * The read() call should not throw an error when the device is | ||||
* being destroyed. Silently convert it to an EOF. | * being destroyed. Silently convert it to an EOF. | ||||
*/ | */ | ||||
done: if (error == ENXIO) | done: if (error == ENXIO) | ||||
error = 0; | error = 0; | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) | ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) | ||||
{ | { | ||||
struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
int defer, error; | int defer, error; | ||||
error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
ttydisc_lock(tp); | |||||
if (tp->t_termios.c_lflag & TOSTOP) { | if (tp->t_termios.c_lflag & TOSTOP) { | ||||
error = tty_wait_background(tp, curthread, SIGTTOU); | error = tty_wait_background(tp, curthread, SIGTTOU); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
} | } | ||||
if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { | if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { | ||||
Show All 10 Lines | if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { | ||||
tp->t_flags |= TF_BUSY_OUT; | tp->t_flags |= TF_BUSY_OUT; | ||||
defer = sigdeferstop(SIGDEFERSTOP_ERESTART); | defer = sigdeferstop(SIGDEFERSTOP_ERESTART); | ||||
error = ttydisc_write(tp, uio, ioflag); | error = ttydisc_write(tp, uio, ioflag); | ||||
sigallowstop(defer); | sigallowstop(defer); | ||||
tp->t_flags &= ~TF_BUSY_OUT; | tp->t_flags &= ~TF_BUSY_OUT; | ||||
cv_signal(&tp->t_outserwait); | cv_signal(&tp->t_outserwait); | ||||
} | } | ||||
done: tty_unlock(tp); | done: ttydisc_unlock(tp); | ||||
tty_unlock(tp); | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, | ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
Show All 34 Lines | #ifdef COMPAT_43TTY | ||||
case TIOCSETN: | case TIOCSETN: | ||||
case TIOCSETP: | case TIOCSETP: | ||||
case TIOCSLTC: | case TIOCSLTC: | ||||
#endif /* COMPAT_43TTY */ | #endif /* COMPAT_43TTY */ | ||||
/* | /* | ||||
* If the ioctl() causes the TTY to be modified, let it | * If the ioctl() causes the TTY to be modified, let it | ||||
* wait in the background. | * wait in the background. | ||||
*/ | */ | ||||
ttydisc_lock(tp); | |||||
error = tty_wait_background(tp, curthread, SIGTTOU); | error = tty_wait_background(tp, curthread, SIGTTOU); | ||||
ttydisc_unlock(tp); | |||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
} | } | ||||
if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { | if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { | ||||
struct termios *old = &tp->t_termios; | struct termios *old = &tp->t_termios; | ||||
struct termios *new = (struct termios *)data; | struct termios *new = (struct termios *)data; | ||||
struct termios *lock = TTY_CALLOUT(tp, dev) ? | struct termios *lock = TTY_CALLOUT(tp, dev) ? | ||||
Show All 32 Lines | |||||
{ | { | ||||
struct tty *tp = dev->si_drv1; | struct tty *tp = dev->si_drv1; | ||||
int error, revents = 0; | int error, revents = 0; | ||||
error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
if (error) | if (error) | ||||
return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); | return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); | ||||
ttydisc_lock(tp); | |||||
if (events & (POLLIN|POLLRDNORM)) { | if (events & (POLLIN|POLLRDNORM)) { | ||||
/* See if we can read something. */ | /* See if we can read something. */ | ||||
if (ttydisc_read_poll(tp) > 0) | if (ttydisc_read_poll(tp) > 0) | ||||
revents |= events & (POLLIN|POLLRDNORM); | revents |= events & (POLLIN|POLLRDNORM); | ||||
} | } | ||||
if (tp->t_flags & TF_ZOMBIE) { | if (tp->t_flags & TF_ZOMBIE) { | ||||
/* Hangup flag on zombie state. */ | /* Hangup flag on zombie state. */ | ||||
revents |= POLLHUP; | revents |= POLLHUP; | ||||
} else if (events & (POLLOUT|POLLWRNORM)) { | } else if (events & (POLLOUT|POLLWRNORM)) { | ||||
/* See if we can write something. */ | /* See if we can write something. */ | ||||
if (ttydisc_write_poll(tp) > 0) | if (ttydisc_write_poll(tp) > 0) | ||||
revents |= events & (POLLOUT|POLLWRNORM); | revents |= events & (POLLOUT|POLLWRNORM); | ||||
} | } | ||||
ttydisc_unlock(tp); | |||||
if (revents == 0) { | if (revents == 0) { | ||||
if (events & (POLLIN|POLLRDNORM)) | if (events & (POLLIN|POLLRDNORM)) | ||||
selrecord(td, &tp->t_inpoll); | selrecord(td, &tp->t_inpoll); | ||||
if (events & (POLLOUT|POLLWRNORM)) | if (events & (POLLOUT|POLLWRNORM)) | ||||
selrecord(td, &tp->t_outpoll); | selrecord(td, &tp->t_outpoll); | ||||
} | } | ||||
Show All 32 Lines | tty_kqops_read_detach(struct knote *kn) | ||||
knlist_remove(&tp->t_inpoll.si_note, kn, 0); | knlist_remove(&tp->t_inpoll.si_note, kn, 0); | ||||
} | } | ||||
static int | static int | ||||
tty_kqops_read_event(struct knote *kn, long hint __unused) | tty_kqops_read_event(struct knote *kn, long hint __unused) | ||||
{ | { | ||||
struct tty *tp = kn->kn_hook; | struct tty *tp = kn->kn_hook; | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { | if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { | ||||
kn->kn_flags |= EV_EOF; | kn->kn_flags |= EV_EOF; | ||||
return (1); | return (1); | ||||
} else { | } else { | ||||
kn->kn_data = ttydisc_read_poll(tp); | kn->kn_data = ttydisc_read_poll(tp); | ||||
return (kn->kn_data > 0); | return (kn->kn_data > 0); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
tty_kqops_write_detach(struct knote *kn) | tty_kqops_write_detach(struct knote *kn) | ||||
{ | { | ||||
struct tty *tp = kn->kn_hook; | struct tty *tp = kn->kn_hook; | ||||
knlist_remove(&tp->t_outpoll.si_note, kn, 0); | knlist_remove(&tp->t_outpoll.si_note, kn, 0); | ||||
} | } | ||||
static int | static int | ||||
tty_kqops_write_event(struct knote *kn, long hint __unused) | tty_kqops_write_event(struct knote *kn, long hint __unused) | ||||
{ | { | ||||
struct tty *tp = kn->kn_hook; | struct tty *tp = kn->kn_hook; | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
if (tty_gone(tp)) { | if (tty_gone(tp)) { | ||||
kn->kn_flags |= EV_EOF; | kn->kn_flags |= EV_EOF; | ||||
return (1); | return (1); | ||||
} else { | } else { | ||||
kn->kn_data = ttydisc_write_poll(tp); | kn->kn_data = ttydisc_write_poll(tp); | ||||
return (kn->kn_data > 0); | return (kn->kn_data > 0); | ||||
} | } | ||||
Show All 20 Lines | ttydev_kqfilter(struct cdev *dev, struct knote *kn) | ||||
error = ttydev_enter(tp); | error = ttydev_enter(tp); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
switch (kn->kn_filter) { | switch (kn->kn_filter) { | ||||
case EVFILT_READ: | case EVFILT_READ: | ||||
kn->kn_hook = tp; | kn->kn_hook = tp; | ||||
kn->kn_fop = &tty_kqops_read; | kn->kn_fop = &tty_kqops_read; | ||||
ttydisc_lock(tp); | |||||
knlist_add(&tp->t_inpoll.si_note, kn, 1); | knlist_add(&tp->t_inpoll.si_note, kn, 1); | ||||
ttydisc_unlock(tp); | |||||
break; | break; | ||||
case EVFILT_WRITE: | case EVFILT_WRITE: | ||||
kn->kn_hook = tp; | kn->kn_hook = tp; | ||||
kn->kn_fop = &tty_kqops_write; | kn->kn_fop = &tty_kqops_write; | ||||
ttydisc_lock(tp); | |||||
knlist_add(&tp->t_outpoll.si_note, kn, 1); | knlist_add(&tp->t_outpoll.si_note, kn, 1); | ||||
ttydisc_unlock(tp); | |||||
break; | break; | ||||
default: | default: | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
tty_unlock(tp); | tty_unlock(tp); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 253 Lines • ▼ Show 20 Lines | |||||
struct tty * | struct tty * | ||||
tty_alloc(struct ttydevsw *tsw, void *sc) | tty_alloc(struct ttydevsw *tsw, void *sc) | ||||
{ | { | ||||
return (tty_alloc_mutex(tsw, sc, NULL)); | return (tty_alloc_mutex(tsw, sc, NULL)); | ||||
} | } | ||||
struct tty * | struct tty * | ||||
tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *mutex) | tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *discmtx) | ||||
{ | { | ||||
struct tty *tp; | struct tty *tp; | ||||
/* Make sure the driver defines all routines. */ | /* Make sure the driver defines all routines. */ | ||||
#define PATCH_FUNC(x) do { \ | #define PATCH_FUNC(x) do { \ | ||||
if (tsw->tsw_ ## x == NULL) \ | if (tsw->tsw_ ## x == NULL) \ | ||||
tsw->tsw_ ## x = ttydevsw_def ## x; \ | tsw->tsw_ ## x = ttydevsw_def ## x; \ | ||||
} while (0) | } while (0) | ||||
Show All 22 Lines | #undef PATCH_FUNC | ||||
tty_init_termios(tp); | tty_init_termios(tp); | ||||
cv_init(&tp->t_inwait, "ttyin"); | cv_init(&tp->t_inwait, "ttyin"); | ||||
cv_init(&tp->t_outwait, "ttyout"); | cv_init(&tp->t_outwait, "ttyout"); | ||||
cv_init(&tp->t_outserwait, "ttyosr"); | cv_init(&tp->t_outserwait, "ttyosr"); | ||||
cv_init(&tp->t_bgwait, "ttybg"); | cv_init(&tp->t_bgwait, "ttybg"); | ||||
cv_init(&tp->t_dcdwait, "ttydcd"); | cv_init(&tp->t_dcdwait, "ttydcd"); | ||||
/* Allow drivers to use a custom mutex to lock the TTY. */ | if (discmtx == &Giant) { | ||||
if (mutex != NULL) { | /* | ||||
tp->t_mtx = mutex; | * This should go away when the syscons/Giant problem is | ||||
* resolved. For now, we still have the one driver locked by | |||||
* Giant, so we have to special-case it and maintain the old | |||||
* behavior. It will get a separate discmtx and maintain Giant | |||||
* as the main tty lock. | |||||
*/ | |||||
tp->t_mtx = discmtx; | |||||
discmtx = NULL; | |||||
} else { | } else { | ||||
tp->t_mtx = &tp->t_mtxobj; | sx_init(&tp->t_sxobj, "ttysx"); | ||||
mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF); | |||||
} | } | ||||
knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx); | if (discmtx != NULL) { | ||||
knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx); | tp->t_discmtx = discmtx; | ||||
} else { | |||||
tp->t_discmtx = &tp->t_discmtxobj; | |||||
mtx_init(&tp->t_discmtxobj, "ttydiscmtx", NULL, MTX_DEF); | |||||
} | |||||
knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_discmtx); | |||||
knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_discmtx); | |||||
return (tp); | return (tp); | ||||
} | } | ||||
static void | static void | ||||
tty_dealloc(void *arg) | tty_dealloc(void *arg) | ||||
{ | { | ||||
struct tty *tp = arg; | struct tty *tp = arg; | ||||
Show All 14 Lines | tty_dealloc(void *arg) | ||||
knlist_destroy(&tp->t_outpoll.si_note); | knlist_destroy(&tp->t_outpoll.si_note); | ||||
cv_destroy(&tp->t_inwait); | cv_destroy(&tp->t_inwait); | ||||
cv_destroy(&tp->t_outwait); | cv_destroy(&tp->t_outwait); | ||||
cv_destroy(&tp->t_bgwait); | cv_destroy(&tp->t_bgwait); | ||||
cv_destroy(&tp->t_dcdwait); | cv_destroy(&tp->t_dcdwait); | ||||
cv_destroy(&tp->t_outserwait); | cv_destroy(&tp->t_outserwait); | ||||
if (tp->t_mtx == &tp->t_mtxobj) | /* We didn't bother initializing the sx if we were given Giant. */ | ||||
mtx_destroy(&tp->t_mtxobj); | if (tp->t_mtx == NULL) | ||||
sx_destroy(&tp->t_sxobj); | |||||
if (tp->t_discmtx == &tp->t_discmtxobj) | |||||
mtx_destroy(&tp->t_discmtxobj); | |||||
ttydevsw_free(tp); | ttydevsw_free(tp); | ||||
free(tp, M_TTY); | free(tp, M_TTY); | ||||
} | } | ||||
static void | static void | ||||
tty_rel_free(struct tty *tp) | tty_rel_free(struct tty *tp) | ||||
{ | { | ||||
struct cdev *dev; | struct cdev *dev; | ||||
Show All 26 Lines | |||||
void | void | ||||
tty_rel_pgrp(struct tty *tp, struct pgrp *pg) | tty_rel_pgrp(struct tty *tp, struct pgrp *pg) | ||||
{ | { | ||||
MPASS(tp->t_sessioncnt > 0); | MPASS(tp->t_sessioncnt > 0); | ||||
tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
ttydisc_lock(tp); | |||||
if (tp->t_pgrp == pg) | if (tp->t_pgrp == pg) | ||||
tp->t_pgrp = NULL; | tp->t_pgrp = NULL; | ||||
ttydisc_unlock(tp); | |||||
tty_unlock(tp); | tty_unlock(tp); | ||||
} | } | ||||
void | void | ||||
tty_rel_sess(struct tty *tp, struct session *sess) | tty_rel_sess(struct tty *tp, struct session *sess) | ||||
{ | { | ||||
tty_assert_locked(tp); | |||||
MPASS(tp->t_sessioncnt > 0); | MPASS(tp->t_sessioncnt > 0); | ||||
ttydisc_lock(tp); | |||||
/* Current session has left. */ | /* Current session has left. */ | ||||
if (tp->t_session == sess) { | if (tp->t_session == sess) { | ||||
tp->t_session = NULL; | tp->t_session = NULL; | ||||
MPASS(tp->t_pgrp == NULL); | MPASS(tp->t_pgrp == NULL); | ||||
} | } | ||||
tp->t_sessioncnt--; | tp->t_sessioncnt--; | ||||
ttydisc_unlock(tp); | |||||
tty_rel_free(tp); | tty_rel_free(tp); | ||||
} | } | ||||
void | void | ||||
tty_rel_gone(struct tty *tp) | tty_rel_gone(struct tty *tp) | ||||
{ | { | ||||
tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
MPASS(!tty_gone(tp)); | MPASS(!tty_gone(tp)); | ||||
if (!ttydisc_lock_owned(tp)) | |||||
ttydisc_lock(tp); | |||||
/* Simulate carrier removal. */ | /* Simulate carrier removal. */ | ||||
ttydisc_modem(tp, 0); | ttydisc_modem(tp, 0); | ||||
/* Wake up all blocked threads. */ | /* Wake up all blocked threads. */ | ||||
tty_wakeup(tp, FREAD|FWRITE); | tty_wakeup(tp, FREAD|FWRITE); | ||||
cv_broadcast(&tp->t_bgwait); | cv_broadcast(&tp->t_bgwait); | ||||
cv_broadcast(&tp->t_dcdwait); | cv_broadcast(&tp->t_dcdwait); | ||||
tp->t_flags |= TF_GONE; | tp->t_flags |= TF_GONE; | ||||
ttydisc_unlock(tp); | |||||
tty_rel_free(tp); | tty_rel_free(tp); | ||||
} | } | ||||
static int | static int | ||||
tty_drop_ctty(struct tty *tp, struct proc *p) | tty_drop_ctty(struct tty *tp, struct proc *p) | ||||
{ | { | ||||
struct session *session; | struct session *session; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
Show All 25 Lines | if (session->s_ttyp == NULL || session->s_ttyp != tp) { | ||||
return (ENOTTY); | return (ENOTTY); | ||||
} | } | ||||
if (!SESS_LEADER(p)) { | if (!SESS_LEADER(p)) { | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
return (EPERM); | return (EPERM); | ||||
} | } | ||||
ttydisc_lock(tp); | |||||
tp->t_sessioncnt--; | |||||
ttydisc_unlock(tp); | |||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
SESS_LOCK(session); | SESS_LOCK(session); | ||||
vp = session->s_ttyvp; | vp = session->s_ttyvp; | ||||
session->s_ttyp = NULL; | session->s_ttyp = NULL; | ||||
session->s_ttyvp = NULL; | session->s_ttyvp = NULL; | ||||
session->s_ttydp = NULL; | session->s_ttydp = NULL; | ||||
SESS_UNLOCK(session); | SESS_UNLOCK(session); | ||||
tp->t_sessioncnt--; | |||||
p->p_flag &= ~P_CONTROLT; | p->p_flag &= ~P_CONTROLT; | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
/* | /* | ||||
* If we did have a vnode, release our reference. Ordinarily we manage | * If we did have a vnode, release our reference. Ordinarily we manage | ||||
* these at the devfs layer, but we can't necessarily know that we were | * these at the devfs layer, but we can't necessarily know that we were | ||||
* invoked on the vnode referenced in the session (i.e. the vnode we | * invoked on the vnode referenced in the session (i.e. the vnode we | ||||
▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
void | void | ||||
tty_signal_sessleader(struct tty *tp, int sig) | tty_signal_sessleader(struct tty *tp, int sig) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
struct session *s; | struct session *s; | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
MPASS(sig >= 1 && sig < NSIG); | MPASS(sig >= 1 && sig < NSIG); | ||||
/* Make signals start output again. */ | /* Make signals start output again. */ | ||||
tp->t_flags &= ~TF_STOPPED; | tp->t_flags &= ~TF_STOPPED; | ||||
tp->t_termios.c_lflag &= ~FLUSHO; | tp->t_termios.c_lflag &= ~FLUSHO; | ||||
/* | /* | ||||
* Load s_leader exactly once to avoid race where s_leader is | * Load s_leader exactly once to avoid race where s_leader is | ||||
Show All 9 Lines | tty_signal_sessleader(struct tty *tp, int sig) | ||||
} | } | ||||
} | } | ||||
void | void | ||||
tty_signal_pgrp(struct tty *tp, int sig) | tty_signal_pgrp(struct tty *tp, int sig) | ||||
{ | { | ||||
ksiginfo_t ksi; | ksiginfo_t ksi; | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
MPASS(sig >= 1 && sig < NSIG); | MPASS(sig >= 1 && sig < NSIG); | ||||
/* Make signals start output again. */ | /* Make signals start output again. */ | ||||
tp->t_flags &= ~TF_STOPPED; | tp->t_flags &= ~TF_STOPPED; | ||||
tp->t_termios.c_lflag &= ~FLUSHO; | tp->t_termios.c_lflag &= ~FLUSHO; | ||||
if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) | if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) | ||||
tty_info(tp); | tty_info(tp); | ||||
if (tp->t_pgrp != NULL) { | if (tp->t_pgrp != NULL) { | ||||
ksiginfo_init(&ksi); | ksiginfo_init(&ksi); | ||||
ksi.ksi_signo = sig; | ksi.ksi_signo = sig; | ||||
ksi.ksi_code = SI_KERNEL; | ksi.ksi_code = SI_KERNEL; | ||||
PGRP_LOCK(tp->t_pgrp); | PGRP_LOCK(tp->t_pgrp); | ||||
pgsignal(tp->t_pgrp, sig, 1, &ksi); | pgsignal(tp->t_pgrp, sig, 1, &ksi); | ||||
PGRP_UNLOCK(tp->t_pgrp); | PGRP_UNLOCK(tp->t_pgrp); | ||||
} | } | ||||
} | } | ||||
void | void | ||||
tty_wakeup(struct tty *tp, int flags) | tty_wakeup(struct tty *tp, int flags) | ||||
{ | { | ||||
ttydisc_assert_locked(tp); | |||||
if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) | if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) | ||||
pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); | pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); | ||||
if (flags & FWRITE) { | if (flags & FWRITE) { | ||||
cv_broadcast(&tp->t_outwait); | cv_broadcast(&tp->t_outwait); | ||||
selwakeup(&tp->t_outpoll); | selwakeup(&tp->t_outpoll); | ||||
KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); | KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); | ||||
} | } | ||||
if (flags & FREAD) { | if (flags & FREAD) { | ||||
cv_broadcast(&tp->t_inwait); | cv_broadcast(&tp->t_inwait); | ||||
selwakeup(&tp->t_inpoll); | selwakeup(&tp->t_inpoll); | ||||
KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); | KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); | ||||
} | } | ||||
} | } | ||||
int | int | ||||
tty_wait(struct tty *tp, struct cv *cv) | tty_wait(struct tty *tp, struct cv *cv) | ||||
{ | { | ||||
int error; | int error; | ||||
int revokecnt = tp->t_revokecnt; | int revokecnt = tp->t_revokecnt; | ||||
bool locktty; | |||||
tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); | ttydisc_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); | ||||
MPASS(!tty_gone(tp)); | MPASS(!tty_gone(tp)); | ||||
error = cv_wait_sig(cv, tp->t_mtx); | locktty = tty_lock_owned(tp); | ||||
if (locktty) | |||||
tty_unlock(tp); | |||||
error = cv_wait_sig(cv, ttydisc_getlock(tp)); | |||||
/* If we had the tty lock coming in, relock it. */ | |||||
if (locktty) { | |||||
ttydisc_unlock(tp); | |||||
tty_lock(tp); | |||||
ttydisc_lock(tp); | |||||
} | |||||
/* Bail out when the device slipped away. */ | /* Bail out when the device slipped away. */ | ||||
if (tty_gone(tp)) | if (tty_gone(tp)) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* Restart the system call when we may have been revoked. */ | /* Restart the system call when we may have been revoked. */ | ||||
if (tp->t_revokecnt != revokecnt) | if (tp->t_revokecnt != revokecnt) | ||||
return (ERESTART); | return (ERESTART); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
tty_timedwait(struct tty *tp, struct cv *cv, int hz) | tty_timedwait(struct tty *tp, struct cv *cv, int hz) | ||||
{ | { | ||||
int error; | int error; | ||||
int revokecnt = tp->t_revokecnt; | int revokecnt = tp->t_revokecnt; | ||||
bool locktty; | |||||
tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); | ttydisc_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); | ||||
MPASS(!tty_gone(tp)); | MPASS(!tty_gone(tp)); | ||||
error = cv_timedwait_sig(cv, tp->t_mtx, hz); | locktty = tty_lock_owned(tp); | ||||
if (locktty) | |||||
tty_unlock(tp); | |||||
error = cv_timedwait_sig(cv, ttydisc_getlock(tp), hz); | |||||
/* If we had the tty lock coming in, relock it. */ | |||||
if (locktty) { | |||||
ttydisc_unlock(tp); | |||||
tty_lock(tp); | |||||
ttydisc_lock(tp); | |||||
} | |||||
/* Bail out when the device slipped away. */ | /* Bail out when the device slipped away. */ | ||||
if (tty_gone(tp)) | if (tty_gone(tp)) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* Restart the system call when we may have been revoked. */ | /* Restart the system call when we may have been revoked. */ | ||||
if (tp->t_revokecnt != revokecnt) | if (tp->t_revokecnt != revokecnt) | ||||
return (ERESTART); | return (ERESTART); | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
tty_flush(struct tty *tp, int flags) | tty_flush(struct tty *tp, int flags) | ||||
{ | { | ||||
ttydisc_assert_locked(tp); | |||||
if (flags & FWRITE) { | if (flags & FWRITE) { | ||||
tp->t_flags &= ~TF_HIWAT_OUT; | tp->t_flags &= ~TF_HIWAT_OUT; | ||||
ttyoutq_flush(&tp->t_outq); | ttyoutq_flush(&tp->t_outq); | ||||
tty_wakeup(tp, FWRITE); | tty_wakeup(tp, FWRITE); | ||||
if (!tty_gone(tp)) { | if (!tty_gone(tp)) { | ||||
ttydevsw_outwakeup(tp); | ttydevsw_outwakeup(tp); | ||||
ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE); | ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE); | ||||
} | } | ||||
} | } | ||||
if (flags & FREAD) { | if (flags & FREAD) { | ||||
tty_hiwat_in_unblock(tp); | tty_hiwat_in_unblock(tp); | ||||
ttyinq_flush(&tp->t_inq); | ttyinq_flush(&tp->t_inq); | ||||
tty_wakeup(tp, FREAD); | tty_wakeup(tp, FREAD); | ||||
if (!tty_gone(tp)) { | if (!tty_gone(tp)) { | ||||
ttydevsw_inwakeup(tp); | ttydevsw_inwakeup(tp); | ||||
ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD); | ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void | void | ||||
tty_set_winsize(struct tty *tp, const struct winsize *wsz) | tty_set_winsize(struct tty *tp, const struct winsize *wsz) | ||||
{ | { | ||||
ttydisc_assert_unlocked(tp); | |||||
tty_assert_locked(tp); | |||||
if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0) | if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0) | ||||
return; | return; | ||||
tp->t_winsize = *wsz; | tp->t_winsize = *wsz; | ||||
ttydisc_lock(tp); | |||||
tty_signal_pgrp(tp, SIGWINCH); | tty_signal_pgrp(tp, SIGWINCH); | ||||
ttydisc_unlock(tp); | |||||
} | } | ||||
static int | static int | ||||
tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, | tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
int error; | int error; | ||||
switch (cmd) { | switch (cmd) { | ||||
/* | /* | ||||
* Modem commands. | * Modem commands. | ||||
* The SER_* and TIOCM_* flags are the same, but one bit | * The SER_* and TIOCM_* flags are the same, but one bit | ||||
* shifted. I don't know why. | * shifted. I don't know why. | ||||
*/ | */ | ||||
case TIOCSDTR: | case TIOCSDTR: | ||||
ttydisc_lock(tp); | |||||
ttydevsw_modem(tp, SER_DTR, 0); | ttydevsw_modem(tp, SER_DTR, 0); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCCDTR: | case TIOCCDTR: | ||||
ttydisc_lock(tp); | |||||
ttydevsw_modem(tp, 0, SER_DTR); | ttydevsw_modem(tp, 0, SER_DTR); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCMSET: { | case TIOCMSET: { | ||||
int bits = *(int *)data; | int bits = *(int *)data; | ||||
ttydisc_lock(tp); | |||||
ttydevsw_modem(tp, | ttydevsw_modem(tp, | ||||
(bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, | (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, | ||||
((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCMBIS: { | case TIOCMBIS: { | ||||
int bits = *(int *)data; | int bits = *(int *)data; | ||||
ttydisc_lock(tp); | |||||
ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); | ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCMBIC: { | case TIOCMBIC: { | ||||
int bits = *(int *)data; | int bits = *(int *)data; | ||||
ttydisc_lock(tp); | |||||
ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCMGET: | case TIOCMGET: | ||||
ttydisc_lock(tp); | |||||
*(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); | *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case FIOASYNC: | case FIOASYNC: | ||||
if (*(int *)data) | if (*(int *)data) | ||||
tp->t_flags |= TF_ASYNC; | tp->t_flags |= TF_ASYNC; | ||||
else | else | ||||
tp->t_flags &= ~TF_ASYNC; | tp->t_flags &= ~TF_ASYNC; | ||||
return (0); | return (0); | ||||
Show All 30 Lines | case TIOCGETA: | ||||
*(struct termios*)data = tp->t_termios; | *(struct termios*)data = tp->t_termios; | ||||
return (0); | return (0); | ||||
case TIOCSETA: | case TIOCSETA: | ||||
case TIOCSETAW: | case TIOCSETAW: | ||||
case TIOCSETAF: { | case TIOCSETAF: { | ||||
struct termios *t = data; | struct termios *t = data; | ||||
bool canonicalize = false; | bool canonicalize = false; | ||||
ttydisc_lock(tp); | |||||
/* | /* | ||||
* Who makes up these funny rules? According to POSIX, | * Who makes up these funny rules? According to POSIX, | ||||
* input baud rate is set equal to the output baud rate | * input baud rate is set equal to the output baud rate | ||||
* when zero. | * when zero. | ||||
*/ | */ | ||||
if (t->c_ispeed == 0) | if (t->c_ispeed == 0) | ||||
t->c_ispeed = t->c_ospeed; | t->c_ispeed = t->c_ospeed; | ||||
/* Discard any unsupported bits. */ | /* Discard any unsupported bits. */ | ||||
t->c_iflag &= TTYSUP_IFLAG; | t->c_iflag &= TTYSUP_IFLAG; | ||||
t->c_oflag &= TTYSUP_OFLAG; | t->c_oflag &= TTYSUP_OFLAG; | ||||
t->c_lflag &= TTYSUP_LFLAG; | t->c_lflag &= TTYSUP_LFLAG; | ||||
t->c_cflag &= TTYSUP_CFLAG; | t->c_cflag &= TTYSUP_CFLAG; | ||||
/* Set terminal flags through tcsetattr(). */ | /* Set terminal flags through tcsetattr(). */ | ||||
if (cmd == TIOCSETAW || cmd == TIOCSETAF) { | if (cmd == TIOCSETAW || cmd == TIOCSETAF) { | ||||
error = tty_drain(tp, 0); | error = tty_drain(tp, 0); | ||||
if (error) | if (error) { | ||||
ttydisc_unlock(tp); | |||||
return (error); | return (error); | ||||
} | |||||
if (cmd == TIOCSETAF) | if (cmd == TIOCSETAF) | ||||
tty_flush(tp, FREAD); | tty_flush(tp, FREAD); | ||||
} | } | ||||
/* | /* | ||||
* Only call param() when the flags really change. | * Only call param() when the flags really change. | ||||
*/ | */ | ||||
if ((t->c_cflag & CIGNORE) == 0 && | if ((t->c_cflag & CIGNORE) == 0 && | ||||
(tp->t_termios.c_cflag != t->c_cflag || | (tp->t_termios.c_cflag != t->c_cflag || | ||||
((tp->t_termios.c_iflag ^ t->c_iflag) & | ((tp->t_termios.c_iflag ^ t->c_iflag) & | ||||
(IXON|IXOFF|IXANY)) || | (IXON|IXOFF|IXANY)) || | ||||
tp->t_termios.c_ispeed != t->c_ispeed || | tp->t_termios.c_ispeed != t->c_ispeed || | ||||
tp->t_termios.c_ospeed != t->c_ospeed)) { | tp->t_termios.c_ospeed != t->c_ospeed)) { | ||||
error = ttydevsw_param(tp, t); | error = ttydevsw_param(tp, t); | ||||
if (error) | if (error) { | ||||
ttydisc_unlock(tp); | |||||
return (error); | return (error); | ||||
} | |||||
/* XXX: CLOCAL? */ | /* XXX: CLOCAL? */ | ||||
tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE; | tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE; | ||||
tp->t_termios.c_ispeed = t->c_ispeed; | tp->t_termios.c_ispeed = t->c_ispeed; | ||||
tp->t_termios.c_ospeed = t->c_ospeed; | tp->t_termios.c_ospeed = t->c_ospeed; | ||||
/* Baud rate has changed - update watermarks. */ | /* Baud rate has changed - update watermarks. */ | ||||
error = tty_watermarks(tp); | error = tty_watermarks(tp); | ||||
if (error) | if (error) { | ||||
ttydisc_unlock(tp); | |||||
return (error); | return (error); | ||||
} | } | ||||
} | |||||
/* | /* | ||||
* We'll canonicalize any partial input if we're transitioning | * We'll canonicalize any partial input if we're transitioning | ||||
* ICANON one way or the other. If we're going from -ICANON -> | * ICANON one way or the other. If we're going from -ICANON -> | ||||
* ICANON, then in the worst case scenario we're in the middle | * ICANON, then in the worst case scenario we're in the middle | ||||
* of a line but both ttydisc_read() and FIONREAD will search | * of a line but both ttydisc_read() and FIONREAD will search | ||||
* for one of our line terminals. | * for one of our line terminals. | ||||
*/ | */ | ||||
Show All 28 Lines | case TIOCSETAF: { | ||||
* and VSTART may have been changed. | * and VSTART may have been changed. | ||||
*/ | */ | ||||
if (tp->t_termios.c_iflag & IXON && | if (tp->t_termios.c_iflag & IXON && | ||||
tp->t_termios.c_cc[VSTOP] == CTRL('S') && | tp->t_termios.c_cc[VSTOP] == CTRL('S') && | ||||
tp->t_termios.c_cc[VSTART] == CTRL('Q')) | tp->t_termios.c_cc[VSTART] == CTRL('Q')) | ||||
ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); | ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); | ||||
else | else | ||||
ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); | ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCGETD: | case TIOCGETD: | ||||
/* For compatibility - we only support TTYDISC. */ | /* For compatibility - we only support TTYDISC. */ | ||||
*(int *)data = TTYDISC; | *(int *)data = TTYDISC; | ||||
return (0); | return (0); | ||||
case TIOCGPGRP: | case TIOCGPGRP: | ||||
if (!tty_is_ctty(tp, td->td_proc)) | if (!tty_is_ctty(tp, td->td_proc)) | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (p->p_session->s_ttyp != NULL || | ||||
* TTYs of which the session leader has been | * TTYs of which the session leader has been | ||||
* killed or the TTY revoked. | * killed or the TTY revoked. | ||||
*/ | */ | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
return (EPERM); | return (EPERM); | ||||
} | } | ||||
/* Connect the session to the TTY. */ | /* Connect the session to the TTY. */ | ||||
ttydisc_lock(tp); | |||||
tp->t_session = p->p_session; | tp->t_session = p->p_session; | ||||
tp->t_session->s_ttyp = tp; | tp->t_session->s_ttyp = tp; | ||||
tp->t_sessioncnt++; | tp->t_sessioncnt++; | ||||
/* Assign foreground process group. */ | /* Assign foreground process group. */ | ||||
tp->t_pgrp = p->p_pgrp; | tp->t_pgrp = p->p_pgrp; | ||||
ttydisc_unlock(tp); | |||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
p->p_flag |= P_CONTROLT; | p->p_flag |= P_CONTROLT; | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCSPGRP: { | case TIOCSPGRP: { | ||||
Show All 19 Lines | case TIOCSPGRP: { | ||||
/* | /* | ||||
* Determine if this TTY is the controlling TTY after | * Determine if this TTY is the controlling TTY after | ||||
* relocking the TTY. | * relocking the TTY. | ||||
*/ | */ | ||||
if (!tty_is_ctty(tp, td->td_proc)) { | if (!tty_is_ctty(tp, td->td_proc)) { | ||||
sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | ||||
return (ENOTTY); | return (ENOTTY); | ||||
} | } | ||||
ttydisc_lock(tp); | |||||
tp->t_pgrp = pg; | tp->t_pgrp = pg; | ||||
ttydisc_unlock(tp); | |||||
sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | ||||
/* Wake up the background process groups. */ | /* Wake up the background process groups. */ | ||||
cv_broadcast(&tp->t_bgwait); | cv_broadcast(&tp->t_bgwait); | ||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCFLUSH: { | case TIOCFLUSH: { | ||||
int flags = *(int *)data; | int flags = *(int *)data; | ||||
if (flags == 0) | if (flags == 0) | ||||
flags = (FREAD|FWRITE); | flags = (FREAD|FWRITE); | ||||
else | else | ||||
flags &= (FREAD|FWRITE); | flags &= (FREAD|FWRITE); | ||||
ttydisc_lock(tp); | |||||
tty_flush(tp, flags); | tty_flush(tp, flags); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
case TIOCDRAIN: | case TIOCDRAIN: | ||||
/* Drain TTY output. */ | /* Drain TTY output. */ | ||||
return tty_drain(tp, 0); | ttydisc_lock(tp); | ||||
error = tty_drain(tp, 0); | |||||
ttydisc_unlock(tp); | |||||
return (error); | |||||
case TIOCGDRAINWAIT: | case TIOCGDRAINWAIT: | ||||
ttydisc_lock(tp); | |||||
*(int *)data = tp->t_drainwait; | *(int *)data = tp->t_drainwait; | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCSDRAINWAIT: | case TIOCSDRAINWAIT: | ||||
error = priv_check(td, PRIV_TTY_DRAINWAIT); | error = priv_check(td, PRIV_TTY_DRAINWAIT); | ||||
if (error == 0) | if (error == 0) | ||||
tp->t_drainwait = *(int *)data; | tp->t_drainwait = *(int *)data; | ||||
return (error); | return (error); | ||||
case TIOCCONS: | case TIOCCONS: | ||||
/* Set terminal as console TTY. */ | /* Set terminal as console TTY. */ | ||||
Show All 10 Lines | case TIOCGWINSZ: | ||||
/* Obtain window size. */ | /* Obtain window size. */ | ||||
*(struct winsize*)data = tp->t_winsize; | *(struct winsize*)data = tp->t_winsize; | ||||
return (0); | return (0); | ||||
case TIOCSWINSZ: | case TIOCSWINSZ: | ||||
/* Set window size. */ | /* Set window size. */ | ||||
tty_set_winsize(tp, data); | tty_set_winsize(tp, data); | ||||
return (0); | return (0); | ||||
case TIOCEXCL: | case TIOCEXCL: | ||||
ttydisc_lock(tp); | |||||
tp->t_flags |= TF_EXCLUDE; | tp->t_flags |= TF_EXCLUDE; | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCNXCL: | case TIOCNXCL: | ||||
ttydisc_lock(tp); | |||||
tp->t_flags &= ~TF_EXCLUDE; | tp->t_flags &= ~TF_EXCLUDE; | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCSTOP: | case TIOCSTOP: | ||||
ttydisc_lock(tp); | |||||
tp->t_flags |= TF_STOPPED; | tp->t_flags |= TF_STOPPED; | ||||
ttydevsw_pktnotify(tp, TIOCPKT_STOP); | ttydevsw_pktnotify(tp, TIOCPKT_STOP); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCSTART: | case TIOCSTART: | ||||
ttydisc_lock(tp); | |||||
tp->t_flags &= ~TF_STOPPED; | tp->t_flags &= ~TF_STOPPED; | ||||
tp->t_termios.c_lflag &= ~FLUSHO; | tp->t_termios.c_lflag &= ~FLUSHO; | ||||
ttydevsw_outwakeup(tp); | ttydevsw_outwakeup(tp); | ||||
ttydevsw_pktnotify(tp, TIOCPKT_START); | ttydevsw_pktnotify(tp, TIOCPKT_START); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCSTAT: | case TIOCSTAT: | ||||
ttydisc_lock(tp); | |||||
tty_info(tp); | tty_info(tp); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
case TIOCSTI: | case TIOCSTI: | ||||
if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) | if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) | ||||
return (EPERM); | return (EPERM); | ||||
if (!tty_is_ctty(tp, td->td_proc) && | if (!tty_is_ctty(tp, td->td_proc) && | ||||
priv_check(td, PRIV_TTY_STI)) | priv_check(td, PRIV_TTY_STI)) | ||||
return (EACCES); | return (EACCES); | ||||
ttydisc_lock(tp); | |||||
ttydisc_rint(tp, *(char *)data, 0); | ttydisc_rint(tp, *(char *)data, 0); | ||||
ttydisc_rint_done(tp); | ttydisc_rint_done(tp); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef COMPAT_43TTY | #ifdef COMPAT_43TTY | ||||
return tty_ioctl_compat(tp, cmd, data, fflag, td); | return tty_ioctl_compat(tp, cmd, data, fflag, td); | ||||
#else /* !COMPAT_43TTY */ | #else /* !COMPAT_43TTY */ | ||||
return (ENOIOCTL); | return (ENOIOCTL); | ||||
#endif /* COMPAT_43TTY */ | #endif /* COMPAT_43TTY */ | ||||
} | } | ||||
int | int | ||||
tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td) | tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td) | ||||
{ | { | ||||
int error; | int error; | ||||
tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
if (tty_gone(tp)) | if (tty_gone(tp)) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* tty device driver may change parameters related to I/O. */ | |||||
ttydisc_lock(tp); | |||||
error = ttydevsw_ioctl(tp, cmd, data, td); | error = ttydevsw_ioctl(tp, cmd, data, td); | ||||
ttydisc_unlock(tp); | |||||
if (error == ENOIOCTL) | if (error == ENOIOCTL) | ||||
error = tty_generic_ioctl(tp, cmd, data, fflag, td); | error = tty_generic_ioctl(tp, cmd, data, fflag, td); | ||||
return (error); | return (error); | ||||
} | } | ||||
dev_t | dev_t | ||||
tty_udev(struct tty *tp) | tty_udev(struct tty *tp) | ||||
{ | { | ||||
if (tp->t_dev) | if (tp->t_dev) | ||||
return (dev2udev(tp->t_dev)); | return (dev2udev(tp->t_dev)); | ||||
else | else | ||||
return (NODEV); | return (NODEV); | ||||
} | } | ||||
int | int | ||||
tty_checkoutq(struct tty *tp) | tty_checkoutq(struct tty *tp) | ||||
{ | { | ||||
ttydisc_assert_locked(tp); | |||||
/* 256 bytes should be enough to print a log message. */ | /* 256 bytes should be enough to print a log message. */ | ||||
return (ttyoutq_bytesleft(&tp->t_outq) >= 256); | return (ttyoutq_bytesleft(&tp->t_outq) >= 256); | ||||
} | } | ||||
void | void | ||||
tty_hiwat_in_block(struct tty *tp) | tty_hiwat_in_block(struct tty *tp) | ||||
{ | { | ||||
ttydisc_assert_locked(tp); | |||||
if ((tp->t_flags & TF_HIWAT_IN) == 0 && | if ((tp->t_flags & TF_HIWAT_IN) == 0 && | ||||
tp->t_termios.c_iflag & IXOFF && | tp->t_termios.c_iflag & IXOFF && | ||||
tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { | tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { | ||||
/* | /* | ||||
* Input flow control. Only enter the high watermark when we | * Input flow control. Only enter the high watermark when we | ||||
* can successfully store the VSTOP character. | * can successfully store the VSTOP character. | ||||
*/ | */ | ||||
if (ttyoutq_write_nofrag(&tp->t_outq, | if (ttyoutq_write_nofrag(&tp->t_outq, | ||||
&tp->t_termios.c_cc[VSTOP], 1) == 0) | &tp->t_termios.c_cc[VSTOP], 1) == 0) | ||||
tp->t_flags |= TF_HIWAT_IN; | tp->t_flags |= TF_HIWAT_IN; | ||||
} else { | } else { | ||||
/* No input flow control. */ | /* No input flow control. */ | ||||
tp->t_flags |= TF_HIWAT_IN; | tp->t_flags |= TF_HIWAT_IN; | ||||
} | } | ||||
} | } | ||||
void | void | ||||
tty_hiwat_in_unblock(struct tty *tp) | tty_hiwat_in_unblock(struct tty *tp) | ||||
{ | { | ||||
ttydisc_assert_locked(tp); | |||||
if (tp->t_flags & TF_HIWAT_IN && | if (tp->t_flags & TF_HIWAT_IN && | ||||
tp->t_termios.c_iflag & IXOFF && | tp->t_termios.c_iflag & IXOFF && | ||||
tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { | tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { | ||||
/* | /* | ||||
* Input flow control. Only leave the high watermark when we | * Input flow control. Only leave the high watermark when we | ||||
* can successfully store the VSTART character. | * can successfully store the VSTART character. | ||||
*/ | */ | ||||
if (ttyoutq_write_nofrag(&tp->t_outq, | if (ttyoutq_write_nofrag(&tp->t_outq, | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | ttyhook_register(struct tty **rtp, struct proc *p, int fd, struct ttyhook *th, | ||||
/* Try to attach the hook to the TTY. */ | /* Try to attach the hook to the TTY. */ | ||||
error = EBUSY; | error = EBUSY; | ||||
tty_lock(tp); | tty_lock(tp); | ||||
MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); | MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); | ||||
if (tp->t_flags & TF_HOOK) | if (tp->t_flags & TF_HOOK) | ||||
goto done3; | goto done3; | ||||
ttydisc_lock(tp); | |||||
tp->t_flags |= TF_HOOK; | tp->t_flags |= TF_HOOK; | ||||
tp->t_hook = th; | tp->t_hook = th; | ||||
tp->t_hooksoftc = softc; | tp->t_hooksoftc = softc; | ||||
*rtp = tp; | *rtp = tp; | ||||
error = 0; | error = 0; | ||||
/* Maybe we can switch into bypass mode now. */ | /* Maybe we can switch into bypass mode now. */ | ||||
ttydisc_optimize(tp); | ttydisc_optimize(tp); | ||||
/* Silently convert rint() calls to rint_bypass() when possible. */ | /* Silently convert rint() calls to rint_bypass() when possible. */ | ||||
if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) | if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) | ||||
th->th_rint = ttyhook_defrint; | th->th_rint = ttyhook_defrint; | ||||
ttydisc_unlock(tp); | |||||
done3: tty_unlock(tp); | done3: tty_unlock(tp); | ||||
done2: dev_relthread(dev, ref); | done2: dev_relthread(dev, ref); | ||||
done1: fdrop(fp, curthread); | done1: fdrop(fp, curthread); | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
ttyhook_unregister(struct tty *tp) | ttyhook_unregister(struct tty *tp) | ||||
{ | { | ||||
tty_assert_locked(tp); | tty_assert_locked(tp); | ||||
MPASS(tp->t_flags & TF_HOOK); | MPASS(tp->t_flags & TF_HOOK); | ||||
/* Disconnect the hook. */ | /* Disconnect the hook. */ | ||||
ttydisc_lock(tp); | |||||
tp->t_flags &= ~TF_HOOK; | tp->t_flags &= ~TF_HOOK; | ||||
tp->t_hook = NULL; | tp->t_hook = NULL; | ||||
/* Maybe we need to leave bypass mode. */ | /* Maybe we need to leave bypass mode. */ | ||||
ttydisc_optimize(tp); | ttydisc_optimize(tp); | ||||
ttydisc_unlock(tp); | |||||
/* Maybe deallocate the TTY as well. */ | /* Maybe deallocate the TTY as well. */ | ||||
tty_rel_free(tp); | tty_rel_free(tp); | ||||
} | } | ||||
/* | /* | ||||
* /dev/console handling. | * /dev/console handling. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 282 Lines • Show Last 20 Lines |
This is also fixed... this patch grew organically as I worked out the intricacies of tty bits, and I suspect at some point I was dropping the ttydisc lock coming into this loop then picking it back up here. It no longer makes any sense, though.