shithub: riscv

Download patch

ref: 9c40e15ba8ae000f73c23d89143d6c44b75220fd
parent: 152c9d525b58a2ed4ea4daac47438efd835202ce
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Tue Feb 18 17:15:06 EST 2014

exportfs: fix flush races, proc sweeping

--- a/sys/src/cmd/exportfs/exportfs.c
+++ b/sys/src/cmd/exportfs/exportfs.c
@@ -460,8 +460,8 @@
 		fatal(Enomem);
 	n = convS2M(t, data, messagesize);
 	if(write(netfd, data, n)!=n){
-		syslog(0, "exportfs", "short write: %r");
-		fatal("mount write");
+		/* not fatal, might have got a note due to flush */
+		fprint(2, "exportfs: short write in reply: %r\n");
 	}
 	free(data);
 }
@@ -570,8 +570,6 @@
 		unlock(&sbufalloc);
 		w = emallocz(sizeof(*w) + messagesize);
 	}
-	w->pid = 0;
-	w->canint = 0;
 	w->flushtag = NOTAG;
 	return w;
 }
@@ -579,8 +577,6 @@
 void
 putsbuf(Fsrpc *w)
 {
-	w->pid = 0;
-	w->canint = 0;
 	w->flushtag = NOTAG;
 	lock(&sbufalloc);
 	w->next = sbufalloc.free;
--- a/sys/src/cmd/exportfs/exportfs.h
+++ b/sys/src/cmd/exportfs/exportfs.h
@@ -15,8 +15,6 @@
 struct Fsrpc
 {
 	Fsrpc	*next;		/* freelist */
-	uintptr	pid;		/* Pid of slave process executing the rpc */
-	int	canint;		/* Interrupt gate */
 	int	flushtag;	/* Tag on which to reply to flush */
 	Fcall	work;		/* Plan 9 incoming Fcall */
 	uchar	buf[];		/* Data buffer */
@@ -53,9 +51,10 @@
 
 struct Proc
 {
-	uintptr	pid;
+	Lock;
 	Fsrpc	*busy;
 	Proc	*next;
+	int	pid;
 };
 
 struct Qidtab
@@ -70,7 +69,6 @@
 
 enum
 {
-	MAXPROC		= 50,
 	FHASHSIZE	= 64,
 	Fidchunk	= 1000,
 	Npsmpt		= 32,
@@ -128,7 +126,7 @@
 void	slaveopen(Fsrpc*);
 void	slaveread(Fsrpc*);
 void	slavewrite(Fsrpc*);
-void	blockingslave(void);
+void	blockingslave(Proc*);
 void	reopen(Fid *f);
 void	noteproc(int, char*);
 void	flushaction(void*, char*);
--- a/sys/src/cmd/exportfs/exportsrv.c
+++ b/sys/src/cmd/exportfs/exportsrv.c
@@ -64,14 +64,20 @@
 
 	for(m = Proclist; m; m = m->next){
 		w = m->busy;
-		if(w != 0 && w->pid == m->pid && w->work.tag == t->work.oldtag) {
+		if(w == nil || w->work.tag != t->work.oldtag)
+			continue;
+
+		lock(m);
+		w = m->busy;
+		if(w != nil && w->work.tag == t->work.oldtag) {
 			w->flushtag = t->work.tag;
 			DEBUG(DFD, "\tset flushtag %d\n", t->work.tag);
-			if(w->canint)
-				postnote(PNPROC, w->pid, "flush");
+			postnote(PNPROC, m->pid, "flush");
+			unlock(m);
 			putsbuf(t);
 			return;
 		}
+		unlock(m);
 	}
 
 	reply(&t->work, &rhdr, 0);
@@ -459,10 +465,10 @@
 void
 slave(Fsrpc *f)
 {
-	Proc *p;
-	uintptr pid;
-	Fcall rhdr;
 	static int nproc;
+	Proc *p, **l;
+	Fcall rhdr;
+	int pid;
 
 	if(readonly){
 		switch(f->work.type){
@@ -479,30 +485,41 @@
 		}
 	}
 	for(;;) {
-		for(p = Proclist; p; p = p->next) {
-			if(p->busy == 0) {
-				f->pid = p->pid;
-				p->busy = f;
-				do {
-					pid = (uintptr)rendezvous((void*)p->pid, f);
-				}
-				while(pid == ~0);	/* Interrupted */
-				if(pid != p->pid)
-					fatal("rendezvous sync fail");
-				return;
-			}	
+		for(l = &Proclist; (p = *l) != nil; l = &p->next) {
+			if(p->busy != nil)
+				continue;
+
+			p->busy = f;
+			while(rendezvous(p, f) == (void*)~0)
+				;
+
+			/* swept a slave proc */
+			if(f == nil){
+				*l = p->next;
+				free(p);
+				nproc--;
+				break;
+			}
+			f = nil;
+
+			/*
+			 * as long as the number of slave procs
+			 * is small, dont bother sweeping.
+			 */
+			if(nproc < 16)
+				break;
 		}
+		if(f == nil)
+			return;
 
-		if(nproc >= MAXPROC){
+		p = emallocz(sizeof(Proc));
+		pid = rfork(RFPROC|RFMEM|RFNOWAIT);
+		switch(pid) {
+		case -1:
 			reply(&f->work, &rhdr, Enoprocs);
 			putsbuf(f);
+			free(p);
 			return;
-		}
-		nproc++;
-		pid = rfork(RFPROC|RFMEM);
-		switch(pid) {
-		case -1:
-			fatal("rfork");
 
 		case 0:
 			if (local[0] != '\0')
@@ -511,44 +528,34 @@
 						local, remote);
 				else
 					procsetname("%s -> %s", local, remote);
-			blockingslave();
-			fatal("slave");
+			blockingslave(p);
+			_exits(0);
 
 		default:
-			p = emallocz(sizeof(Proc));
-			p->busy = 0;
 			p->pid = pid;
 			p->next = Proclist;
 			Proclist = p;
-			while(rendezvous((void*)pid, p) == (void*)~0)
-				;
+			nproc++;
 		}
 	}
 }
 
 void
-blockingslave(void)
+blockingslave(Proc *m)
 {
 	Fsrpc *p;
 	Fcall rhdr;
-	Proc *m;
-	uintptr pid;
 
 	notify(flushaction);
 
-	pid = getpid();
-
-	do {
-		m = rendezvous((void*)pid, 0);
-	}
-	while(m == (void*)~0);	/* Interrupted */
-	
 	for(;;) {
-		p = rendezvous((void*)pid, (void*)pid);
-		if(p == (void*)~0)			/* Interrupted */
+		p = rendezvous(m, nil);
+		if(p == (void*)~0)	/* Interrupted */
 			continue;
+		if(p == nil)		/* Swept */
+			break;
 
-		DEBUG(DFD, "\tslave: %p %F p %p\n", pid, &p->work, p->pid);
+		DEBUG(DFD, "\tslave: %d %F\n", m->pid, &p->work);
 		if(p->flushtag != NOTAG)
 			goto flushme;
 
@@ -568,13 +575,17 @@
 		default:
 			reply(&p->work, &rhdr, "exportfs: slave type error");
 		}
-		if(p->flushtag != NOTAG) {
 flushme:
+		lock(m);
+		m->busy = nil;
+		unlock(m);
+
+		/* no more flushes can come in now */
+		if(p->flushtag != NOTAG) {
 			p->work.type = Tflush;
 			p->work.tag = p->flushtag;
 			reply(&p->work, &rhdr, 0);
 		}
-		m->busy = 0;
 		putsbuf(p);
 	}
 }
@@ -654,16 +665,8 @@
 	
 	path = makepath(f->f, "");
 	DEBUG(DFD, "\topen: %s %d\n", path, work->mode);
-
-	p->canint = 1;
-	if(p->flushtag != NOTAG){
-		free(path);
-		return;
-	}
-	/* There is a race here I ignore because there are no locks */
 	f->fid = open(path, work->mode);
 	free(path);
-	p->canint = 0;
 	if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) {
 	Error:
 		errstr(err, sizeof err);
@@ -703,9 +706,6 @@
 	}
 
 	n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
-	p->canint = 1;
-	if(p->flushtag != NOTAG)
-		return;
 	data = malloc(n);
 	if(data == 0) {
 		reply(work, &rhdr, Enomem);
@@ -717,7 +717,6 @@
 		r = preaddir(f, (uchar*)data, n, work->offset);
 	else
 		r = pread(f->fid, data, n, work->offset);
-	p->canint = 0;
 	if(r < 0) {
 		free(data);
 		errstr(err, sizeof err);
@@ -724,7 +723,6 @@
 		reply(work, &rhdr, err);
 		return;
 	}
-
 	DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r);
 
 	rhdr.data = data;
@@ -750,11 +748,7 @@
 	}
 
 	n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
-	p->canint = 1;
-	if(p->flushtag != NOTAG)
-		return;
 	n = pwrite(f->fid, work->data, n, work->offset);
-	p->canint = 0;
 	if(n < 0) {
 		errstr(err, sizeof err);
 		reply(work, &rhdr, err);