summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authored <ed@FreeBSD.org>2009-02-11 16:28:49 +0000
committered <ed@FreeBSD.org>2009-02-11 16:28:49 +0000
commit5f2edd80fc90f7699d3f3274153b723af5f0e152 (patch)
tree705a88f64ce1671f4a752f6c09d52d89e75c747b
parentced47d0a8e4610843f05ea493da7c712e17f9eb2 (diff)
downloadFreeBSD-src-5f2edd80fc90f7699d3f3274153b723af5f0e152.zip
FreeBSD-src-5f2edd80fc90f7699d3f3274153b723af5f0e152.tar.gz
Serialize write() calls on TTYs.
Just like the old TTY layer, the current MPSAFE TTY layer does not make any attempt to serialize calls of write(). Data is copied into the kernel in 256 (TTY_STACKBUF) byte chunks. If a write() call occurs at the same time, the data may interleave. This is especially likely when the TTY starts blocking, because the output queue reaches the high watermark. I've implemented this by adding a new flag, TTY_BUSY_OUT, which is used to mark a TTY as having a thread stuck in write(). Because I don't want non-blocking processes to be possibly blocked by a sleeping thread, I'm still allowing it to bypass the protection. According to this message, the Linux kernel returns EAGAIN in such cases, but I think that's a little too restrictive: http://kerneltrap.org/index.php?q=mailarchive/linux-kernel/2007/5/2/85418/thread PR: kern/118287
-rw-r--r--sys/kern/tty.c30
-rw-r--r--sys/sys/tty.h3
-rw-r--r--usr.sbin/pstat/pstat.85
-rw-r--r--usr.sbin/pstat/pstat.c5
4 files changed, 37 insertions, 6 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index d73683c..ee95958 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -438,15 +438,28 @@ ttydev_write(struct cdev *dev, struct uio *uio, int ioflag)
if (tp->t_termios.c_lflag & TOSTOP) {
error = tty_wait_background(tp, curthread, SIGTTOU);
- if (error) {
- tty_unlock(tp);
- return (error);
- }
+ if (error)
+ goto done;
}
- error = ttydisc_write(tp, uio, ioflag);
- tty_unlock(tp);
+ if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) {
+ /* Allow non-blocking writes to bypass serialization. */
+ error = ttydisc_write(tp, uio, ioflag);
+ } else {
+ /* Serialize write() calls. */
+ while (tp->t_flags & TF_BUSY_OUT) {
+ error = tty_wait(tp, &tp->t_bgwait);
+ if (error)
+ goto done;
+ }
+
+ tp->t_flags |= TF_BUSY_OUT;
+ error = ttydisc_write(tp, uio, ioflag);
+ tp->t_flags &= ~TF_BUSY_OUT;
+ cv_broadcast(&tp->t_bgwait);
+ }
+done: tty_unlock(tp);
return (error);
}
@@ -1880,6 +1893,11 @@ static struct {
{ TF_ZOMBIE, 'Z' },
{ TF_HOOK, 's' },
+ /* Keep these together -> 'bi' and 'bo'. */
+ { TF_BUSY, 'b' },
+ { TF_BUSY_IN, 'i' },
+ { TF_BUSY_OUT, 'o' },
+
{ 0, '\0'},
};
diff --git a/sys/sys/tty.h b/sys/sys/tty.h
index 218fd21..8935093 100644
--- a/sys/sys/tty.h
+++ b/sys/sys/tty.h
@@ -83,6 +83,9 @@ struct tty {
#define TF_BYPASS 0x04000 /* Optimized input path. */
#define TF_ZOMBIE 0x08000 /* Modem disconnect received. */
#define TF_HOOK 0x10000 /* TTY has hook attached. */
+#define TF_BUSY_IN 0x20000 /* Process busy in read() -- not supported. */
+#define TF_BUSY_OUT 0x40000 /* Process busy in write(). */
+#define TF_BUSY (TF_BUSY_IN|TF_BUSY_OUT)
unsigned int t_revokecnt; /* (t) revoke() count. */
/* Buffering mechanisms. */
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
index b92abd8..5e3760c 100644
--- a/usr.sbin/pstat/pstat.8
+++ b/usr.sbin/pstat/pstat.8
@@ -206,6 +206,11 @@ block mode input routine in use
connection lost
.It s
i/o being snooped
+.It b
+busy in
+.Xr read 2
+or
+.Xr write 2
.El
.Pp
The
diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c
index 2cb52fe..8272eee 100644
--- a/usr.sbin/pstat/pstat.c
+++ b/usr.sbin/pstat/pstat.c
@@ -315,6 +315,11 @@ static struct {
{ TF_ZOMBIE, 'Z' },
{ TF_HOOK, 's' },
+ /* Keep these together -> 'bi' and 'bo'. */
+ { TF_BUSY, 'b' },
+ { TF_BUSY_IN, 'i' },
+ { TF_BUSY_OUT, 'o' },
+
{ 0, '\0'},
};
OpenPOWER on IntegriCloud