summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authored <ed@FreeBSD.org>2008-11-01 08:35:28 +0000
committered <ed@FreeBSD.org>2008-11-01 08:35:28 +0000
commitc2c324d37976496417e7195d8daae3975be8eaa1 (patch)
tree92b387a3b5878c3317cf997130d7bc5769e5f6e5
parentec4658731c7b03d16de04acf5f20aa4dc53635cb (diff)
downloadFreeBSD-src-c2c324d37976496417e7195d8daae3975be8eaa1.zip
FreeBSD-src-c2c324d37976496417e7195d8daae3975be8eaa1.tar.gz
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>
-rw-r--r--sys/conf/files2
-rw-r--r--sys/kern/kern_cons.c (renamed from sys/kern/tty_cons.c)240
-rw-r--r--sys/kern/tty.c96
-rw-r--r--sys/sys/tty.h3
4 files changed, 96 insertions, 245 deletions
diff --git a/sys/conf/files b/sys/conf/files
index fba04aa..ad5a19a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -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
diff --git a/sys/kern/tty_cons.c b/sys/kern/kern_cons.c
index 81e1708..3930f59 100644
--- a/sys/kern/tty_cons.c
+++ b/sys/kern/kern_cons.c
@@ -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;
}
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index 16fda40..8f6f9b2 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -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>
diff --git a/sys/sys/tty.h b/sys/sys/tty.h
index 9c4e956..7505562 100644
--- a/sys/sys/tty.h
+++ b/sys/sys/tty.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);
OpenPOWER on IntegriCloud