Browse Source

Plan 9 from Bell Labs 2003-11-25

David du Colombier 20 years ago
parent
commit
430e619a78

+ 5 - 6
dist/replica/plan9.db

@@ -4847,8 +4847,7 @@ sys/man/3/mnt - 664 sys sys 944959698 1994
 sys/man/3/mouse - 664 sys sys 1020313480 4276
 sys/man/3/pipe - 664 sys sys 1032640674 1145
 sys/man/3/pnp - 664 sys sys 1019864752 4645
-sys/man/3/proc - 664 sys sys 1067722856 9526
-sys/man/3/realtime - 664 sys sys 1067722856 7946
+sys/man/3/proc - 664 sys sys 1069706068 11896
 sys/man/3/root - 664 sys sys 1046958450 632
 sys/man/3/rtc - 664 sys sys 954378857 667
 sys/man/3/sd - 664 sys sys 1018386776 4805
@@ -7301,7 +7300,7 @@ sys/src/cmd/file.c - 664 sys sys 1065017417 20590
 sys/src/cmd/fmt.c - 664 sys sys 1069018548 3897
 sys/src/cmd/fortune.c - 664 sys sys 1035832953 1674
 sys/src/cmd/fossil - 20000000775 sys sys 1042005512 0
-sys/src/cmd/fossil/9.h - 664 sys sys 1068129294 4165
+sys/src/cmd/fossil/9.h - 664 sys sys 1069683864 4268
 sys/src/cmd/fossil/9auth.c - 664 sys sys 1061530721 3393
 sys/src/cmd/fossil/9dir.c - 664 sys sys 1042005502 1995
 sys/src/cmd/fossil/9excl.c - 664 sys sys 1042005502 1887
@@ -7310,7 +7309,7 @@ sys/src/cmd/fossil/9fsys.c - 664 sys sys 1068591358 29979
 sys/src/cmd/fossil/9lstn.c - 664 sys sys 1042005503 2865
 sys/src/cmd/fossil/9p.c - 664 sys sys 1066098096 21486
 sys/src/cmd/fossil/9ping.c - 664 sys sys 1042005503 1563
-sys/src/cmd/fossil/9proc.c - 664 sys sys 1068129294 15100
+sys/src/cmd/fossil/9proc.c - 664 sys sys 1069683861 14521
 sys/src/cmd/fossil/9srv.c - 664 sys sys 1066098097 3682
 sys/src/cmd/fossil/9user.c - 664 sys sys 1066098098 17239
 sys/src/cmd/fossil/Ccli.c - 664 sys sys 1042005504 1624
@@ -7321,7 +7320,7 @@ sys/src/cmd/fossil/archive.c - 664 sys sys 1061530723 9109
 sys/src/cmd/fossil/build - 664 sys sys 1042005505 449
 sys/src/cmd/fossil/buildsh - 775 sys sys 1042005505 561
 sys/src/cmd/fossil/bwatch.c - 664 sys sys 1042005505 6754
-sys/src/cmd/fossil/cache.c - 664 sys sys 1068526660 43049
+sys/src/cmd/fossil/cache.c - 664 sys sys 1069683862 43183
 sys/src/cmd/fossil/conf.rc - 775 sys sys 1055703747 1407
 sys/src/cmd/fossil/dat.h - 664 sys sys 1061530727 7851
 sys/src/cmd/fossil/deadlock - 775 sys sys 1042005506 413
@@ -7336,7 +7335,7 @@ sys/src/cmd/fossil/flfmt.c - 664 sys sys 1061530720 10358
 sys/src/cmd/fossil/flproto - 664 sys sys 1042005508 210
 sys/src/cmd/fossil/fns.h - 664 sys sys 1068526660 3077
 sys/src/cmd/fossil/fossil-acid - 664 sys sys 1042005508 3965
-sys/src/cmd/fossil/fossil.c - 664 sys sys 1061530719 2112
+sys/src/cmd/fossil/fossil.c - 664 sys sys 1069683860 2114
 sys/src/cmd/fossil/fs.c - 664 sys sys 1061530725 21071
 sys/src/cmd/fossil/fs.h - 664 sys sys 1055703744 1398
 sys/src/cmd/fossil/history - 664 sys sys 1055703793 1400

+ 6 - 0
dist/replica/plan9.log

@@ -12443,3 +12443,9 @@
 1069592527 52 c 386/bin/upas/smtp - 775 sys sys 1069592384 271115
 1069592527 53 c 386/bin/upas/smtpd - 775 sys sys 1069592385 320515
 1069592527 54 c 386/lib/libndb.a - 664 sys sys 1069592386 54610
+1069684229 0 c sys/src/cmd/fossil/9.h - 664 sys sys 1069683864 4268
+1069684229 1 c sys/src/cmd/fossil/9proc.c - 664 sys sys 1069683861 14521
+1069684229 2 c sys/src/cmd/fossil/cache.c - 664 sys sys 1069683862 43183
+1069684229 3 c sys/src/cmd/fossil/fossil.c - 664 sys sys 1069683860 2114
+1069707632 0 c sys/man/3/proc - 664 sys sys 1069706068 11896
+1069707632 1 d sys/man/3/realtime - 664 sys sys 1067722856 0

+ 92 - 14
sys/man/3/proc

@@ -5,6 +5,7 @@ proc \- running processes
 .nf
 .B bind #p /proc
 
+.B /proc/trace
 .BI /proc/ n /args
 .BI /proc/ n /ctl
 .BI /proc/ n /fd
@@ -28,7 +29,9 @@ proc \- running processes
 The
 .I proc
 device serves a two-level directory structure.
-The first level contains numbered directories
+The first level contains the
+.B trace
+file (see below) and numbered directories
 corresponding to pids of live processes;
 each such directory contains a set of files
 representing the corresponding process.
@@ -266,6 +269,11 @@ Set the base priority for the process to the integer
 Wire the process to processor
 .IR n .
 .TP 10n
+.B trace
+Toggle trace event generation for this process into
+.B /proc/trace
+(see below).
+.TP 10n
 .B "period\ \fInu
 Set the real-time scheduling period of the process to
 .IR nu ,
@@ -289,9 +297,9 @@ empty.  The time is interpreted, respectively, as
 or, in the case of an absent units specifier, as
 .IR nanoseconds .
 If the time specifier is signed, it is interpreted as an increment or decrement
-from a previously set value.
-See
-.IR realtime (3).
+from a previously set value.  See also the
+.B admit
+command below.
 .TP 10n
 .B "deadline\ \fInu
 Set the real-time deadline interval of the process to
@@ -303,8 +311,6 @@ and
 are interpreted as for
 .B period
 above.
-See
-.IR realtime (3).
 .TP 10n
 .B "cost\ \fInu
 Set the real-time cost (maximum CPU time per period) of the process to
@@ -318,14 +324,15 @@ are interpreted as for
 above.
 .TP 10n
 .B "sporadic
-Use sporadic scheduling for the real-time process.
-See
-.IR realtime (3).
+Use sporadic scheduling for the real-time process.  The description of the
+.B admit
+command below contains further details.
 .TP 10n
 .B "yieldonblock
 Make the real-time process yield on blocking I/O.
-See
-.IR realtime (3).
+  The description of the
+.B admit
+command below contains further details.
 .TP 10n
 .B "admit
 Given real-time
@@ -342,9 +349,59 @@ to
 perform a schedulability test and start scheduling the process as a real-time
 process if the test succeeds.  If the test fails, the
 .B write
-will fail with error set to the reason for failure.
-See
-.IR realtime (3).
+will fail with error set to the reason for failure.  For details on real time, read on.
+.PP
+Real-time processes are periodically
+.IR released ,
+giving them a higher priority than non-real-time processes until they either
+give up the processor voluntarily, they exhaust their CPU allocation, or they reach their
+.IR deadline .
+The moment of release is dictated by the
+.I period
+and whther the process is
+.I sporadic
+or not.
+Non-sporadic processes are called
+.I periodic
+and they are released precisely at intervals of their period (but perods can be skipped
+if the process blocks on I/O).  Sporadic processes are released whenever they become
+runnable (after being blocked by
+.IR sleep ()
+or I/O), but always at least an interval of
+.I period
+after the previous release.
+.PP
+The
+.I deadline
+of a real-time process specifies that the process must complete within the first
+.I deadline
+seconds of its
+.IR period .
+The dealine must be less than or equal to the period.  If it is not specified, it is set to the period.
+.PP
+The
+.I cost
+of a real-time process describes the maximum CPU time the process may use per period.
+.PP
+A real-time process can give up the CPU before its deadline is reached or its allocation is exhausted.
+It does this by calling
+.IR sleep (0).
+If
+.I yieldonblock
+is specified, it also does it by executing any blocking system call.
+.I Yieldonblock
+is assumed for
+.I sporadic
+processes.
+.PP
+Of the released processes, the one with the earliest deadline has the highest priority.
+Care should be taken using spin locks (see
+.IR lock (2))
+because a real-time process spinning on a lock will not give up the processor until
+its CPU allocation is exhausted; this is unlikely to be the desired behavior.
+.PP
+When a real-time process reaches its deadline or exhausts its CPU allocation, it remains
+schedulable, but at a very low priority.
 .PP
 The priority is interpreted by Plan 9's multilevel process scheduler.
 Priorities run from 0 to 19, with higher
@@ -401,12 +458,33 @@ in
 .IR fork (2)).
 The file may be written to cause the process to change to another note group,
 provided the group exists and is owned by the same user.
+.PP
+The file
+.B /proc/trace
+can be opened once and read to see trace events from processes that have
+had the string
+.B trace
+written to their
+.B ctl
+file.
+Each event produces, in native machine format, the
+.IR pid ,
+a
+.IR type ,
+and a
+.I "time stamp"
+(see
+.B /sys/include/trace.h
+and
+.BR /sys/src/cmd/trace.c ).
 .SH FILES
 .nf
 .B /sys/src/9/*/mem.h
 .B /sys/src/9/*/dat.h
+.B /sys/include/trace.h
 .fi
 .SH SEE ALSO
+.IR trace (1),
 .IR debugger (2),
 .IR mach (2),
 .IR cons (3)

+ 0 - 212
sys/man/3/realtime

@@ -1,212 +0,0 @@
-.TH REALTIME 3 
-.EQ
-delim $$
-.EN
-.SH NAME
-realtime \- real time scheduling
-.SH SYNOPSIS
-.EX
-.ta 4n +11n +7
-.B bind -a #R /dev
-.ift .sp 0.5
-.ifn .sp
-/proc/\fIn\fP/ctl
-.EE
-.SH DESCRIPTION
-The Plan 9 real-time scheduler allows mixing real-time processes and best-effort
-processes on a single system.  The scheduler is intended for real-time loads well
-under 100% CPU utilization with minimum periods in the order of a hundred thousand
-instruction times.  The precision of the scheduler depends on the precision of the
-machine's programmable real-time clock.
-.PP
-Real-time processes are scheduled using Earliest-Deadline First (EDF).  Each process
-has three primary scheduling parameters, a period $T$, a deadline $D$
-and a cost $C$, expressed in nanoseconds.
-Every $T$ nanoseconds, the process is
-.I released
-— i.e., it becomes schedulable — and it receives a
-.I slice
-of $C$ nsec.
-When the process is released, its
-.I "release time"
-$r$ is set to the current time.
-The process's
-.I "absolute deadline"
-$d$ is set to $r + D$.
-(Note the use of capital letters to indicate intervals and lower-case
-letters to indicate `points in time'.)
-Between release and deadline, the process must be scheduled often enough
-to be able to consume its slice of $C$ nsec of CPU time.
-So, to be schedulable, $C <= D <= T$ must hold.
-If $D < T$, processes are not real-time schedulable (runnable) between their
-deadline and the next release, but they may run as best-effort processes and compete with
-other processes for the CPU.
-Processes are scheduled according to the EDF rule
-from the moment of release until their deadline or their slice runs
-out, whichever happens first.  Additionally, processes can voluntarily
-declare the slice for the current period empty, allowing other
-real-time processes or best-effort processes to use the CPU time remaining in their
-slice.
-.PP
-Before they are released for the first time, processes have to be
-.IR admitted
-into the system.  Before admission, a test is conducted to determine
-whether the process, plus the already admitted processes, given their
-scheduling parameters, can all meet their deadlines.  When a process
-changes its scheduling parameters, it is temporarily
-.I expelled
-and will be readmitted only if the new scheduling parameters allow all
-processes to continue to meet their deadlines.
-.PP
-The EDF scheduler schedules the released process with the earliest
-deadline.  When a process is released it can, therefore, preempt a process
-that has a later deadline, but was released earlier.
-.\" .PP
-.\" Real-time processes sharing resources, however, may need to be prevented
-.\" from preempting each other.  They must do this by declaring their shared
-.\" resources.  The scheduler will not preempt one process with another that
-.\" shares a common resource.  To do this, processes can name resources they use
-.\" and the scheduler will not allow preemption of a process by another that
-.\" has named the same resource.
-.\" .PP
-.\" Note that, if all processes share a common resource, the scheduler reduces to
-.\" non-preemptive EDF scheduling.  Note also that resources in this context
-.\" are just named placeholders for the actual resources shared by the processes.
-.\" It is up to the programmer to define the relationship between actual
-.\" shared resources (data structures, for example) and the named resources
-.\" used by the scheduler.
-.\" .PP
-.\" During the admission test, information about resource sharing is used
-.\" to calculate
-.\" .IR "inherited deadlines" ,
-.\" for each process.  A resource's
-.\" .IR "inherited deadline" ,
-.\" $Δ$, is the minimum of the deadlines $D$ of each of the processes
-.\" that shares that resource.  A process's $Δ$ is the minimum of the
-.\" resource $Δ$s of all resources held by the process.
-.\" .PP
-.\" The scheduler allows a released process $T$ to preempt running process $T'$
-.\" iff $d < d' ^ D < Δ'$; the first half of the condition says that the
-.\" released process's deadline must be earlier, the second implies that the
-.\" released process may not share resources with the running one (after all,
-.\" if $T'$ had acquired a resource that $T$ uses, its $Δ'$ would, by the
-.\" definition of inherited deadlines, be less than or equal to $D$).
-.\" .PP
-.\" The admission test takes these limitations into account.  Note that,
-.\" in practice, processes rarely share resources.
-.PP
-Normally, processes can block (when all their processes are blocked on
-system calls) during their release.  The time spent blocking may be
-accounted against the current slice, because blocked processes may stop
-other processes from getting the processor (due to shared resources).
-Processes can tell the scheduler that they no longer require the CPU until the next
-release, by
-.I yielding
-(see below).
-.PP
-Processes can also run in another mode where blocking automatically
-implies immediate yielding.  This mode truly guarantees that deadlines
-will be made, but programmers must know more about the (blocking)
-nature of system calls used in order to use this mode.
-.sp
-.LP
-The real-time scheduler is controlled through
-.BR /proc/\fPpid\fP/ctl .
-See
-.IR proc (3).
-.PP
-The process' parameters are set or modified by writing one of these files and they
-can be examined by reading it.
-A parameter is presented in the form
-\f2name\fP \f2value\fP, \f2name\fP +\f2value\fP, or \f2name\fP -\f2value\fP.
-A command is presented in the form
-.IR commandname .
-Parameters and commands are separated by semicolons.
-The settable parameters are
-.TP
-.B period
-Sets the period.  The value must be given as a number with or without decimal point
-and directly followed (no white space) by one of
-.I s
-for seconds,
-.I ms
-for milliseconds,
-.I µs
-or
-.I us
-for microseconds,
-.I ns
-or
-nothing
-for nanoseconds.
-If the value is signed, it is taken as an increment (decrement) of the value
-perviously set.
-.TP
-.B deadline
-Sets the deadline.  Value specified as above.
-.TP
-.B cost
-Sets the cost.  Value specified as above.
-.TP
-.B yieldonblock
-When the (integer) value is non-zero or absent, the process is placed in
-.I yield-on-block
-mode: when the process is blocked on a system call, the process automatically
-declares the current deadline reached and will be released again in the next period or when the process
-becomes runnable again, whichever occurs last.
-When the value is zero, the process can block without forfeiting the current release.
-The default value is zero.
-.TP
-.B sporadic
-When the (integer) value is non-zero or absent, the process is placed in
-.I sporadic
-mode.  This mode resembles
-.IR yield-on-block .
-When the process unblocks and the time elapsed since last release is more than the period, it
-is released immediately, rather than at the next period boundary.  Sporadic scheduling is
-useful for processes that are interrupt driven.  The period merely specifies the maximum
-invocation rate.  The default value is zero.
-.TP
-.B admit
-This initiates an admission test.  If the test fails, the write containing the
-.B admit
-command will fail.  If the test succeeds the process is admitted and will be released.
-.TP
-.B expel
-Expels the process by turning it back into a regular best-effort process.
-.LP
-.BR Sleep(0)
-will yield (i.e., giveup the processor until the next release time).  In non-real-time
-processes,
-.B sleep(0)
-will invoke the scheduler, allowing higher priority processes to run.
-.TP
-.SH EXAMPLE
-.EX
-.ta 4n +4n +4n +4n +4n +4n +4n
-char *ctl[128];
-
-void
-processvideo(){
-	int fd;
-
-	snprint(ctl, sizeof ctl, "/proc/%ld/ctl", getpid());
-	if ((fd = open(ctl, ORDWR)) < 0) 
-		sysfatal("%s: %r", ctl);
-	if (fprint(fd, "period 33ms;deadline 20ms;cost 8ms;admit") < 0)
-		sysfatal("%s: admit: %r", ctl);
-	close(fd);
-	for (;;){
-		processframe();
-		sleep(0);
-	}
-}
-.EE
-.SH "SEE ALSO
-.IR trace (1)
-.LP
-.SH SOURCE
-.B /sys/src/9/port/edf.c
-.SH BUGS
-The admission test does not take multiprocessors into account.  The total
-real-time load cannot exceed a single-\s-2CPU\s0 load.

+ 6 - 6
sys/src/cmd/fossil/9.h

@@ -19,17 +19,17 @@ struct Msg {
 	Fcall	r;
 	Con*	con;
 
-	Msg*	anext;			/* alloc */
+	Msg*	anext;			/* allocation free list */
 
-	Msg*	mnext;			/* Con */
+	Msg*	mnext;			/* all active messsages on this Con */
 	Msg* 	mprev;
 
-	Msg*	rwnext;			/* proc read/write */
+	int	state;			/* */
 
-	int	state;
+	Msg*	flush;			/* flushes waiting for this Msg */
 
-	Msg*	behind;			/* proc flush */
-	Msg*	before;
+	Msg*	rwnext;			/* read/write queue */
+	int	nowq;			/* do not place on write queue */
 };
 
 enum {

+ 61 - 102
sys/src/cmd/fossil/9proc.c

@@ -101,7 +101,7 @@ static void
 msgFree(Msg* m)
 {
 	assert(m->rwnext == nil);
-	assert(m->behind == nil && m->before == nil);
+	assert(m->flush == nil);
 
 	vtLock(mbox.alock);
 	if(mbox.nmsg > mbox.maxmsg){
@@ -144,6 +144,7 @@ msgAlloc(Con* con)
 
 	m->con = con;
 	m->state = MsgR;
+	m->nowq = 0;
 
 	return m;
 }
@@ -166,32 +167,11 @@ msgMunlink(Msg* m)
 	m->mprev = m->mnext = nil;
 }
 
-static void
-msgUnlinkAndFree(Msg* m)
-{
-	/*
-	 * Unlink the message from the flush
-	 * and message lists, and free the message.
-	 * Called with con->mlock locked.
-	 */
-	if(m->behind){
-		m->behind->before = nil;
-		m->behind = nil;
-	}
-	if(m->before){
-		m->before->behind = nil;
-		m->before = nil;
-	}
-
-	msgMunlink(m);
-	msgFree(m);
-}
-
 void
 msgFlush(Msg* m)
 {
 	Con *con;
-	Msg *old;
+	Msg *flush, *old;
 
 	con = m->con;
 
@@ -231,8 +211,8 @@ msgFlush(Msg* m)
 	 * processing has been done yet and it is still on the read
 	 * queue. The second is if old is a Tflush, which doesn't
 	 * affect the server state. In both cases, put the old
-	 * message into MsgF state and let MsgProc or MsgWrite
-	 * toss it after pulling it off the appropriate queue.
+	 * message into MsgF state and let MsgWrite toss it after
+	 * pulling it off the queue.
 	 */
 	if(old->state == MsgR || old->t.type == Tflush){
 		old->state = MsgF;
@@ -243,21 +223,29 @@ msgFlush(Msg* m)
 
 	/*
 	 * Link this flush message and the old message
-	 * so we can coalesce multiple flushes (if there are
+	 * so multiple flushes can be coalesced (if there are
 	 * multiple Tflush messages for a particular pending
 	 * request, it is only necessary to respond to the last
 	 * one, so any previous can be removed) and to be
 	 * sure flushes wait for their corresponding old
 	 * message to go out first.
+	 * Waiting flush messages do not go on the write queue,
+	 * they are processed after the old message is dealt
+	 * with. There's no real need to protect the setting of
+	 * Msg.nowq, the only code to check it runs in this
+	 * process after this routine returns.
 	 */
-	if(old->behind){
+	if((flush = old->flush) != nil){
 		if(Dflag)
 			fprint(2, "msgFlush: remove %d from %d list\n",
-				old->behind->t.tag, old->t.tag);
-		msgUnlinkAndFree(old->behind);
+				old->flush->t.tag, old->t.tag);
+		m->flush = flush->flush;
+		flush->flush = nil;
+		msgMunlink(flush);
+		msgFree(flush);
 	}
-	old->behind = m;
-	m->before = old;
+	old->flush = m;
+	m->nowq = 1;
 
 	if(Dflag)
 		fprint(2, "msgFlush: add %d to %d queue\n",
@@ -348,14 +336,16 @@ msgProc(void*)
 		 * Put the message (with reply) on the
 		 * write queue and wakeup the write process.
 		 */
-		vtLock(con->wlock);
-		if(con->whead == nil)
-			con->whead = m;
-		else
-			con->wtail->rwnext = m;
-		con->wtail = m;
-		vtWakeup(con->wrendez);
-		vtUnlock(con->wlock);
+		if(!m->nowq){
+			vtLock(con->wlock);
+			if(con->whead == nil)
+				con->whead = m;
+			else
+				con->wtail->rwnext = m;
+			con->wtail = m;
+			vtWakeup(con->wrendez);
+			vtUnlock(con->wlock);
+		}
 	}
 }
 
@@ -430,67 +420,12 @@ msgRead(void* v)
 	}
 }
 
-static int
-_msgWrite(Msg* m)
-{
-	Con *con;
-	int eof, msgw, n;
-
-	/*
-	 * A message with .before set implies it is waiting
-	 * for the .before message to go out first, so punt
-	 * until the .before message goes out (see below).
-	 */
-	con = m->con;
-	if(m->before != nil){
-		if(Dflag)
-			fprint(2, "msgWrite %p: delay r %F\n", con, &m->r);
-		return 0;
-	}
-	if(m->state == MsgF)
-		msgw = 0;
-	else
-		msgw = 1;
-
-	msgMunlink(m);
-	vtUnlock(con->mlock);
-
-	eof = 0;
-	if(msgw){
-		/*
-		 * TODO: optimise this copy away somehow for
-		 * read, stat, etc.
-		 */
-		assert(n = convS2M(&m->r, con->data, con->msize));
-		if(write(con->fd, con->data, n) != n)
-			eof = 1;
-	}
-
-	if(Dflag)
-		fprint(2, "msgWrite %d: r %F\n", msgw, &m->r);
-
-	/*
-	 * Message written or flushed. If it has anything waiting
-	 * for it to have gone out, recurse down the list writing
-	 * them out too.
-	 */
-	vtLock(con->mlock);
-	if(m->behind != nil){
-		m->behind->before = nil;
-		eof += _msgWrite(m->behind);
-		m->behind = nil;
-	}
-	msgFree(m);
-
-	return eof;
-}
-
 static void
 msgWrite(void* v)
 {
-	Msg *m;
-	int eof;
 	Con *con;
+	int eof, n;
+	Msg *flush, *m;
 
 	vtThreadSetName("msgWrite");
 
@@ -510,17 +445,41 @@ msgWrite(void* v)
 		m = con->whead;
 		con->whead = m->rwnext;
 		m->rwnext = nil;
+		assert(!m->nowq);
 		vtUnlock(con->wlock);
 
+		eof = 0;
+
 		/*
-		 * If the message hasn't been flushed,
-		 * change its state so it will be written
-		 * out.
+		 * Write each message (if it hasn't been flushed)
+		 * followed by any messages waiting for it to complete.
 		 */
 		vtLock(con->mlock);
-		if(m->state != MsgF)
-			m->state = MsgW;
-		eof = _msgWrite(m);
+		while(m != nil){
+			msgMunlink(m);
+
+			if(Dflag)
+				fprint(2, "msgWrite %d: r %F\n",
+					m->state, &m->r);
+
+			if(m->state != MsgF){
+				m->state = MsgW;
+				vtUnlock(con->mlock);
+
+				n = convS2M(&m->r, con->data, con->msize);
+				if(write(con->fd, con->data, n) != n)
+					eof = 1;
+
+				vtLock(con->mlock);
+			}
+
+			if((flush = m->flush) != nil){
+				assert(flush->nowq);
+				m->flush = nil;
+			}
+			msgFree(m);
+			m = flush;
+		}
 		vtUnlock(con->mlock);
 
 		vtLock(con->lock);

+ 5 - 0
sys/src/cmd/fossil/cache.c

@@ -1102,6 +1102,7 @@ blistAlloc(Block *b)
 		p = c->blfree;
 		c->blfree = p->next;
 		vtUnlock(c->lk);
+assert(b->iostate == BioDirty);
 		return p;
 	}
 	vtUnlock(c->lk);
@@ -1144,6 +1145,7 @@ blistFree(Cache *c, BList *bl)
 	c->blfree = bl;
 	vtWakeup(c->blrend);
 	vtUnlock(c->lk);
+cacheFlush(c, 0);
 }
 
 /*
@@ -1218,6 +1220,7 @@ blockDependency(Block *b, Block *bb, int index, uchar *score, Entry *e)
 	if(p == nil)
 		return;
 
+	assert(bb->iostate == BioDirty);
 if(0)fprint(2, "%d:%x:%d depends on %d:%x:%d\n", b->part, b->addr, b->l.type, bb->part, bb->addr, bb->l.type);
 
 	p->part = bb->part;
@@ -1515,6 +1518,8 @@ blockWrite(Block *b)
 			fprint(2, "%d:%x:%d iostate is %d in blockWrite\n",
 				bb->part, bb->addr, bb->l.type, bb->iostate);
 			/* probably BioWriting if it happens? */
+			if(bb->iostate == BioClean)
+				goto ignblock;
 		}
 
 		blockPut(bb);

+ 1 - 1
sys/src/cmd/fossil/fossil.c

@@ -12,7 +12,7 @@ usage(void)
 	fprint(2, "usage: %s"
 		" [-Dt]"
 		" [-c cmd]"
-		" [-f partition]"
+		" [-f partition]\n"
 		, argv0);
 	exits("usage");
 }