aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorEd Schouten <ed@FreeBSD.org>2008-12-11 21:44:02 +0000
committerEd Schouten <ed@FreeBSD.org>2008-12-11 21:44:02 +0000
commit1ff90be789c181d199d1333867c0cba1c048fa60 (patch)
tree221bafd786e1f6cb46fff4c04a83c36c32a49862 /sys
parent3d2e6ad8b6fd9e1f026faf8980ba3ba19ecb5dcd (diff)
downloadsrc-1ff90be789c181d199d1333867c0cba1c048fa60.tar.gz
src-1ff90be789c181d199d1333867c0cba1c048fa60.zip
Add kqueue()-support to pseudo-terminal master devices.
One thing I didn't expect many applications to use, was kqueue() on pseudo-terminal master devices. There are applications that use kqueue() on the TTY itself (rtorrent, etc). That doesn't mean we shouldn't implement this. Libraries like libevent use kqueue() by default, which means they wouldn't be able to use kqueue(). The old TTY layer implements a very broken version of kqueue() by performing the actual polling on the TTY device. Discussed with: peter
Notes
Notes: svn path=/head/; revision=185942
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/tty_pts.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/sys/kern/tty_pts.c b/sys/kern/tty_pts.c
index 1f87cbad46ee..9e81a6d6f1ea 100644
--- a/sys/kern/tty_pts.c
+++ b/sys/kern/tty_pts.c
@@ -254,6 +254,14 @@ done: ttydisc_rint_done(tp);
}
static int
+ptsdev_truncate(struct file *fp, off_t length, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return (EINVAL);
+}
+
+static int
ptsdev_ioctl(struct file *fp, u_long cmd, void *data,
struct ucred *active_cred, struct thread *td)
{
@@ -423,6 +431,94 @@ ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
return (revents);
}
+/*
+ * kqueue support.
+ */
+
+static void
+pts_kqops_read_detach(struct knote *kn)
+{
+ struct file *fp = kn->kn_fp;
+ struct tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
+}
+
+static int
+pts_kqops_read_event(struct knote *kn, long hint)
+{
+ struct file *fp = kn->kn_fp;
+ struct tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ if (psc->pts_flags & PTS_FINISHED) {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ } else {
+ kn->kn_data = ttydisc_getc_poll(tp);
+ return (kn->kn_data > 0);
+ }
+}
+
+static void
+pts_kqops_write_detach(struct knote *kn)
+{
+ struct file *fp = kn->kn_fp;
+ struct tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
+}
+
+static int
+pts_kqops_write_event(struct knote *kn, long hint)
+{
+ struct file *fp = kn->kn_fp;
+ struct tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ if (psc->pts_flags & PTS_FINISHED) {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ } else {
+ kn->kn_data = ttydisc_rint_poll(tp);
+ return (kn->kn_data > 0);
+ }
+}
+
+static struct filterops pts_kqops_read =
+ { 1, NULL, pts_kqops_read_detach, pts_kqops_read_event };
+static struct filterops pts_kqops_write =
+ { 1, NULL, pts_kqops_write_detach, pts_kqops_write_event };
+
+static int
+ptsdev_kqfilter(struct file *fp, struct knote *kn)
+{
+ struct tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0;
+
+ tty_lock(tp);
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_fop = &pts_kqops_read;
+ knlist_add(&psc->pts_outpoll.si_note, kn, 1);
+ break;
+ case EVFILT_WRITE:
+ kn->kn_fop = &pts_kqops_write;
+ knlist_add(&psc->pts_inpoll.si_note, kn, 1);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ tty_unlock(tp);
+ return (error);
+}
+
static int
ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
struct thread *td)
@@ -475,8 +571,10 @@ ptsdev_close(struct file *fp, struct thread *td)
static struct fileops ptsdev_ops = {
.fo_read = ptsdev_read,
.fo_write = ptsdev_write,
+ .fo_truncate = ptsdev_truncate,
.fo_ioctl = ptsdev_ioctl,
.fo_poll = ptsdev_poll,
+ .fo_kqfilter = ptsdev_kqfilter,
.fo_stat = ptsdev_stat,
.fo_close = ptsdev_close,
.fo_flags = DFLAG_PASSABLE,
@@ -493,6 +591,7 @@ ptsdrv_outwakeup(struct tty *tp)
cv_broadcast(&psc->pts_outwait);
selwakeup(&psc->pts_outpoll);
+ KNOTE_LOCKED(&psc->pts_outpoll.si_note, 0);
}
static void
@@ -502,6 +601,7 @@ ptsdrv_inwakeup(struct tty *tp)
cv_broadcast(&psc->pts_inwait);
selwakeup(&psc->pts_inpoll);
+ KNOTE_LOCKED(&psc->pts_inpoll.si_note, 0);
}
static int
@@ -566,6 +666,9 @@ ptsdrv_free(void *softc)
chgptscnt(psc->pts_uidinfo, -1, 0);
uifree(psc->pts_uidinfo);
+ knlist_destroy(&psc->pts_inpoll.si_note);
+ knlist_destroy(&psc->pts_outpoll.si_note);
+
#ifdef PTS_EXTERNAL
/* Destroy master device as well. */
if (psc->pts_cdev != NULL)
@@ -618,6 +721,8 @@ pts_alloc(int fflags, struct thread *td, struct file *fp)
uihold(uid);
tp = tty_alloc(&pts_class, psc, NULL);
+ knlist_init(&psc->pts_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL);
+ knlist_init(&psc->pts_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL);
/* Expose the slave device as well. */
tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
@@ -656,6 +761,8 @@ pts_alloc_external(int fflags, struct thread *td, struct file *fp,
uihold(uid);
tp = tty_alloc(&pts_class, psc, NULL);
+ knlist_init(&psc->pts_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL);
+ knlist_init(&psc->pts_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL);
/* Expose the slave device as well. */
tty_makedev(tp, td->td_ucred, "%s", name);