Reimplement the /dev/console device node.
One of the pieces of code that I had left alone during the development of the MPSAFE TTY layer, was tty_cons.c. This file actually has two different functions: - It contains low-level console input/output routines (cnputc(), etc). - It creates /dev/console and wraps all its cdevsw calls to the appropriate TTY. This commit reimplements the second set of functions by moving it directly into the TTY layer. /dev/console is now a character device node that's basically a regular TTY, but does a lookup of `si_drv1' each time you open it. d_write has also been changed to call log_console(). d_close() is not present, because we must make sure we don't revoke the TTY after writing a log message to it. Even though I'm not convinced this is in line with the future directions of our console code, it is a good move for now. It removes recursive locking from the top half of the TTY layer. The previous implementation called into the TTY layer with Giant held. I'm renaming tty_cons.c to kern_cons.c now. The code hardly contains any TTY related bits, so we'd better give it a less misleading name. Tested by: Andrzej Tobola <ato iem pw edu pl>, Carlos A.M. dos Santos <unixmania gmail com>, Eygene Ryabinkin <rea-fbsd codelabs ru>
This commit is contained in:
parent
ec4658731c
commit
c2c324d379
@ -1596,6 +1596,7 @@ kern/kern_alq.c optional alq
|
||||
kern/kern_clock.c standard
|
||||
kern/kern_condvar.c standard
|
||||
kern/kern_conf.c standard
|
||||
kern/kern_cons.c standard
|
||||
kern/kern_cpu.c standard
|
||||
kern/kern_cpuset.c standard
|
||||
kern/kern_context.c standard
|
||||
@ -1708,7 +1709,6 @@ kern/sysv_sem.c optional sysvsem
|
||||
kern/sysv_shm.c optional sysvshm
|
||||
kern/tty.c standard
|
||||
kern/tty_compat.c optional compat_43tty
|
||||
kern/tty_cons.c standard
|
||||
kern/tty_info.c standard
|
||||
kern/tty_inq.c standard
|
||||
kern/tty_outq.c standard
|
||||
|
@ -68,30 +68,8 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
|
||||
|
||||
static d_open_t cnopen;
|
||||
static d_close_t cnclose;
|
||||
static d_read_t cnread;
|
||||
static d_write_t cnwrite;
|
||||
static d_ioctl_t cnioctl;
|
||||
static d_poll_t cnpoll;
|
||||
static d_kqfilter_t cnkqfilter;
|
||||
|
||||
static struct cdevsw cn_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_open = cnopen,
|
||||
.d_close = cnclose,
|
||||
.d_read = cnread,
|
||||
.d_write = cnwrite,
|
||||
.d_ioctl = cnioctl,
|
||||
.d_poll = cnpoll,
|
||||
.d_name = "console",
|
||||
.d_flags = D_TTY | D_NEEDGIANT,
|
||||
.d_kqfilter = cnkqfilter,
|
||||
};
|
||||
|
||||
struct cn_device {
|
||||
STAILQ_ENTRY(cn_device) cnd_next;
|
||||
struct vnode *cnd_vp;
|
||||
struct consdev *cnd_cn;
|
||||
};
|
||||
|
||||
@ -101,22 +79,12 @@ static struct cn_device cn_devtab[CNDEVTAB_SIZE];
|
||||
static STAILQ_HEAD(, cn_device) cn_devlist =
|
||||
STAILQ_HEAD_INITIALIZER(cn_devlist);
|
||||
|
||||
#define CND_INVALID(cnd, td) \
|
||||
(cnd == NULL || cnd->cnd_vp == NULL || \
|
||||
(cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
|
||||
|
||||
static dev_t cn_udev_t;
|
||||
SYSCTL_OPAQUE(_machdep, OID_AUTO, consdev, CTLFLAG_RD,
|
||||
&cn_udev_t, sizeof cn_udev_t, "T,struct cdev *", "");
|
||||
|
||||
int cons_avail_mask = 0; /* Bit mask. Each registered low level console
|
||||
* which is currently unavailable for inpit
|
||||
* (i.e., if it is in graphics mode) will have
|
||||
* this bit cleared.
|
||||
*/
|
||||
static int cn_mute;
|
||||
static int openflag; /* how /dev/console was opened */
|
||||
static int cn_is_open;
|
||||
static char *consbuf; /* buffer used by `consmsgbuf' */
|
||||
static struct callout conscallout; /* callout for outputting to constty */
|
||||
struct msgbuf consmsgbuf; /* message buffer for console tty */
|
||||
@ -214,6 +182,8 @@ cnadd(struct consdev *cn)
|
||||
printf("WARNING: console at %p has no name\n", cn);
|
||||
}
|
||||
STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
|
||||
if (STAILQ_FIRST(&cn_devlist) == cnd)
|
||||
ttyconsdev_select(cnd->cnd_cn->cn_name);
|
||||
|
||||
/* Add device to the active mask. */
|
||||
cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
|
||||
@ -230,10 +200,9 @@ cnremove(struct consdev *cn)
|
||||
STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
|
||||
if (cnd->cnd_cn != cn)
|
||||
continue;
|
||||
if (STAILQ_FIRST(&cn_devlist) == cnd)
|
||||
ttyconsdev_select(NULL);
|
||||
STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
|
||||
if (cnd->cnd_vp != NULL)
|
||||
vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
|
||||
cnd->cnd_vp = NULL;
|
||||
cnd->cnd_cn = NULL;
|
||||
|
||||
/* Remove this device from available mask. */
|
||||
@ -267,6 +236,7 @@ cnselect(struct consdev *cn)
|
||||
return;
|
||||
STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
|
||||
STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
|
||||
ttyconsdev_select(cnd->cnd_cn->cn_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -368,210 +338,12 @@ sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
|
||||
error = sysctl_handle_int(oidp, &cn_mute, 0, req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
if (ocn_mute && !cn_mute && cn_is_open)
|
||||
error = cnopen(NULL, openflag, 0, curthread);
|
||||
else if (!ocn_mute && cn_mute && cn_is_open) {
|
||||
error = cnclose(NULL, openflag, 0, curthread);
|
||||
cn_is_open = 1; /* XXX hack */
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
|
||||
0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
|
||||
|
||||
static int
|
||||
cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
|
||||
{
|
||||
char path[CNDEVPATHMAX];
|
||||
struct nameidata nd;
|
||||
struct vnode *vp;
|
||||
struct cdev *dev;
|
||||
struct cdevsw *csw;
|
||||
int error;
|
||||
|
||||
if ((vp = cnd->cnd_vp) != NULL) {
|
||||
if (!forceopen && vp->v_type != VBAD) {
|
||||
dev = vp->v_rdev;
|
||||
csw = dev_refthread(dev);
|
||||
if (csw == NULL)
|
||||
return (ENXIO);
|
||||
error = (*csw->d_open)(dev, openflag, 0, td);
|
||||
dev_relthread(dev);
|
||||
return (error);
|
||||
}
|
||||
cnd->cnd_vp = NULL;
|
||||
vn_close(vp, openflag, td->td_ucred, td);
|
||||
}
|
||||
snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name);
|
||||
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
|
||||
error = vn_open(&nd, &openflag, 0, NULL);
|
||||
if (error == 0) {
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
VOP_UNLOCK(nd.ni_vp, 0);
|
||||
if (nd.ni_vp->v_type == VCHR)
|
||||
cnd->cnd_vp = nd.ni_vp;
|
||||
else
|
||||
vn_close(nd.ni_vp, openflag, td->td_ucred, td);
|
||||
}
|
||||
return (cnd->cnd_vp != NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
cnopen(struct cdev *dev, int flag, int mode, struct thread *td)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
|
||||
openflag = flag | FWRITE; /* XXX */
|
||||
cn_is_open = 1; /* console is logically open */
|
||||
if (cn_mute)
|
||||
return (0);
|
||||
STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
|
||||
cn_devopen(cnd, td, 0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
cnclose(struct cdev *dev, int flag, int mode, struct thread *td)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
struct vnode *vp;
|
||||
|
||||
STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
|
||||
if ((vp = cnd->cnd_vp) == NULL)
|
||||
continue;
|
||||
cnd->cnd_vp = NULL;
|
||||
vn_close(vp, openflag, td->td_ucred, td);
|
||||
}
|
||||
cn_is_open = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
cnread(struct cdev *dev, struct uio *uio, int flag)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
struct cdevsw *csw;
|
||||
int error;
|
||||
|
||||
cnd = STAILQ_FIRST(&cn_devlist);
|
||||
if (cn_mute || CND_INVALID(cnd, curthread))
|
||||
return (0);
|
||||
dev = cnd->cnd_vp->v_rdev;
|
||||
csw = dev_refthread(dev);
|
||||
if (csw == NULL)
|
||||
return (ENXIO);
|
||||
error = (csw->d_read)(dev, uio, flag);
|
||||
dev_relthread(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
cnwrite(struct cdev *dev, struct uio *uio, int flag)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
struct cdevsw *csw;
|
||||
int error;
|
||||
|
||||
cnd = STAILQ_FIRST(&cn_devlist);
|
||||
if (cn_mute || CND_INVALID(cnd, curthread))
|
||||
goto done;
|
||||
if (constty)
|
||||
dev = constty->t_dev;
|
||||
else
|
||||
dev = cnd->cnd_vp->v_rdev;
|
||||
if (dev != NULL) {
|
||||
log_console(uio);
|
||||
csw = dev_refthread(dev);
|
||||
if (csw == NULL)
|
||||
return (ENXIO);
|
||||
error = (csw->d_write)(dev, uio, flag);
|
||||
dev_relthread(dev);
|
||||
return (error);
|
||||
}
|
||||
done:
|
||||
uio->uio_resid = 0; /* dump the data */
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
cnioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
struct cdevsw *csw;
|
||||
int error;
|
||||
|
||||
cnd = STAILQ_FIRST(&cn_devlist);
|
||||
if (cn_mute || CND_INVALID(cnd, td))
|
||||
return (0);
|
||||
/*
|
||||
* Superuser can always use this to wrest control of console
|
||||
* output from the "virtual" console.
|
||||
*/
|
||||
if (cmd == TIOCCONS && constty) {
|
||||
error = priv_check(td, PRIV_TTY_CONSOLE);
|
||||
if (error)
|
||||
return (error);
|
||||
constty = NULL;
|
||||
return (0);
|
||||
}
|
||||
dev = cnd->cnd_vp->v_rdev;
|
||||
if (dev == NULL)
|
||||
return (0); /* XXX : ENOTTY ? */
|
||||
csw = dev_refthread(dev);
|
||||
if (csw == NULL)
|
||||
return (ENXIO);
|
||||
error = (csw->d_ioctl)(dev, cmd, data, flag, td);
|
||||
dev_relthread(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* poll/kqfilter do not appear to be correct
|
||||
*/
|
||||
static int
|
||||
cnpoll(struct cdev *dev, int events, struct thread *td)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
struct cdevsw *csw;
|
||||
int error;
|
||||
|
||||
cnd = STAILQ_FIRST(&cn_devlist);
|
||||
if (cn_mute || CND_INVALID(cnd, td))
|
||||
return (0);
|
||||
dev = cnd->cnd_vp->v_rdev;
|
||||
if (dev == NULL)
|
||||
return (0);
|
||||
csw = dev_refthread(dev);
|
||||
if (csw == NULL)
|
||||
return (ENXIO);
|
||||
error = (csw->d_poll)(dev, events, td);
|
||||
dev_relthread(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
cnkqfilter(struct cdev *dev, struct knote *kn)
|
||||
{
|
||||
struct cn_device *cnd;
|
||||
struct cdevsw *csw;
|
||||
int error;
|
||||
|
||||
cnd = STAILQ_FIRST(&cn_devlist);
|
||||
if (cn_mute || CND_INVALID(cnd, curthread))
|
||||
return (EINVAL);
|
||||
dev = cnd->cnd_vp->v_rdev;
|
||||
if (dev == NULL)
|
||||
return (ENXIO);
|
||||
csw = dev_refthread(dev);
|
||||
if (csw == NULL)
|
||||
return (ENXIO);
|
||||
error = (csw->d_kqfilter)(dev, kn);
|
||||
dev_relthread(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Low level console routines.
|
||||
*/
|
||||
@ -737,8 +509,6 @@ static void
|
||||
cn_drvinit(void *unused)
|
||||
{
|
||||
|
||||
make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
|
||||
|
||||
mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
|
||||
use_cnputs_mtx = 1;
|
||||
}
|
@ -73,6 +73,10 @@ static struct sx tty_list_sx;
|
||||
SX_SYSINIT(tty_list, &tty_list_sx, "tty list");
|
||||
static unsigned int tty_list_count = 0;
|
||||
|
||||
/* Character device of /dev/console. */
|
||||
static struct cdev *dev_console;
|
||||
static const char *dev_console_filename;
|
||||
|
||||
/*
|
||||
* Flags that are supported and stored by this implementation.
|
||||
*/
|
||||
@ -86,7 +90,7 @@ static unsigned int tty_list_count = 0;
|
||||
HUPCL|CLOCAL|CCTS_OFLOW|CRTS_IFLOW|CDTR_IFLOW|\
|
||||
CDSR_OFLOW|CCAR_OFLOW)
|
||||
|
||||
#define TTY_CALLOUT(tp,d) ((tp)->t_dev != (d))
|
||||
#define TTY_CALLOUT(tp,d) ((d) != (tp)->t_dev && (d) != dev_console)
|
||||
|
||||
/*
|
||||
* Set TTY buffer sizes.
|
||||
@ -1189,11 +1193,7 @@ tty_wait(struct tty *tp, struct cv *cv)
|
||||
int error;
|
||||
int revokecnt = tp->t_revokecnt;
|
||||
|
||||
#if 0
|
||||
/* XXX: /dev/console also picks up Giant. */
|
||||
tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
|
||||
#endif
|
||||
tty_lock_assert(tp, MA_OWNED);
|
||||
MPASS(!tty_gone(tp));
|
||||
|
||||
error = cv_wait_sig(cv, tp->t_mtx);
|
||||
@ -1215,11 +1215,7 @@ tty_timedwait(struct tty *tp, struct cv *cv, int hz)
|
||||
int error;
|
||||
int revokecnt = tp->t_revokecnt;
|
||||
|
||||
#if 0
|
||||
/* XXX: /dev/console also picks up Giant. */
|
||||
tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
|
||||
#endif
|
||||
tty_lock_assert(tp, MA_OWNED);
|
||||
MPASS(!tty_gone(tp));
|
||||
|
||||
error = cv_timedwait_sig(cv, tp->t_mtx, hz);
|
||||
@ -1662,6 +1658,10 @@ tty_hiwat_in_unblock(struct tty *tp)
|
||||
ttydevsw_inwakeup(tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* TTY hooks interface.
|
||||
*/
|
||||
|
||||
static int
|
||||
ttyhook_defrint(struct tty *tp, char c, int flags)
|
||||
{
|
||||
@ -1745,6 +1745,84 @@ ttyhook_unregister(struct tty *tp)
|
||||
tty_rel_free(tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* /dev/console handling.
|
||||
*/
|
||||
|
||||
static int
|
||||
ttyconsdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
|
||||
{
|
||||
struct tty *tp;
|
||||
|
||||
/* System has no console device. */
|
||||
if (dev_console_filename == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/* Look up corresponding TTY by device name. */
|
||||
sx_slock(&tty_list_sx);
|
||||
TAILQ_FOREACH(tp, &tty_list, t_list) {
|
||||
if (strcmp(dev_console_filename, tty_devname(tp)) == 0) {
|
||||
dev_console->si_drv1 = tp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sx_sunlock(&tty_list_sx);
|
||||
|
||||
/* System console has no TTY associated. */
|
||||
if (dev_console->si_drv1 == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
return (ttydev_open(dev, oflags, devtype, td));
|
||||
}
|
||||
|
||||
static int
|
||||
ttyconsdev_write(struct cdev *dev, struct uio *uio, int ioflag)
|
||||
{
|
||||
|
||||
log_console(uio);
|
||||
|
||||
return (ttydev_write(dev, uio, ioflag));
|
||||
}
|
||||
|
||||
/*
|
||||
* /dev/console is a little different than normal TTY's. Unlike regular
|
||||
* TTY device nodes, this device node will not revoke the entire TTY
|
||||
* upon closure and all data written to it will be logged.
|
||||
*/
|
||||
static struct cdevsw ttyconsdev_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_open = ttyconsdev_open,
|
||||
.d_read = ttydev_read,
|
||||
.d_write = ttyconsdev_write,
|
||||
.d_ioctl = ttydev_ioctl,
|
||||
.d_kqfilter = ttydev_kqfilter,
|
||||
.d_poll = ttydev_poll,
|
||||
.d_mmap = ttydev_mmap,
|
||||
.d_name = "ttyconsdev",
|
||||
.d_flags = D_TTY,
|
||||
};
|
||||
|
||||
static void
|
||||
ttyconsdev_init(void *unused)
|
||||
{
|
||||
|
||||
dev_console = make_dev(&ttyconsdev_cdevsw, 0, UID_ROOT, GID_WHEEL,
|
||||
0600, "console");
|
||||
}
|
||||
|
||||
SYSINIT(tty, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyconsdev_init, NULL);
|
||||
|
||||
void
|
||||
ttyconsdev_select(const char *name)
|
||||
{
|
||||
|
||||
dev_console_filename = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging routines.
|
||||
*/
|
||||
|
||||
#include "opt_ddb.h"
|
||||
#ifdef DDB
|
||||
#include <ddb/ddb.h>
|
||||
|
@ -192,6 +192,9 @@ dev_t tty_udev(struct tty *tp);
|
||||
/* Status line printing. */
|
||||
void tty_info(struct tty *tp);
|
||||
|
||||
/* /dev/console selection. */
|
||||
void ttyconsdev_select(const char *name);
|
||||
|
||||
/* Pseudo-terminal hooks. */
|
||||
int pts_alloc_external(int fd, struct thread *td, struct file *fp,
|
||||
struct cdev *dev, const char *name);
|
||||
|
Loading…
Reference in New Issue
Block a user