shithub: riscv

Download patch

ref: 3af236b5e36951ba637d59e4d0362cdb0e428bd2
parent: eaf42a2c7875c312c9880a1f9bf5fa5ab1fff8b1
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Aug 9 14:19:47 EDT 2015

kernel: fix Mheadache

there was a race between cunmount() and walk() on Mhead.from as Mhead.from was
unconditionally freed when we cunmount(), but findmount might have already
returned the Mhead in walk(). we have to ensure that Mhead.from is not freed
before the Mhead itself (now done in putmhead() once the reference count of the
Mhead drops to zero).

the Mhead struct contained two unused locks, removing.

no need to hold Pgrp.ns lock in closegrp() as nobody can get to it (refcount
droped to zero).

avoid cclose() and freemount() while holding Mhead.lock or Pgrp.ns locks as
it might block on a hung up fileserver.

remove the debug prints...

cleanup: use nil for pointers, remove redundant nil checks before putmhead().

--- a/sys/src/9/port/chan.c
+++ b/sys/src/9/port/chan.c
@@ -5,9 +5,6 @@
 #include	"fns.h"
 #include	"../port/error.h"
 
-int chandebug=0;		/* toggled by sysr1 */
-#define DBG if(chandebug)iprint
-
 enum
 {
 	PATHSLOP	= 20,
@@ -38,44 +35,6 @@
 
 #define SEP(c) ((c) == 0 || (c) == '/')
 
-static void
-dumpmount(void)		/* DEBUGGING */
-{
-	Pgrp *pg;
-	Mount *t;
-	Mhead **h, **he, *f;
-
-	if(up == nil){
-		print("no process for dumpmount\n");
-		return;
-	}
-	pg = up->pgrp;
-	if(pg == nil){
-		print("no pgrp for dumpmount\n");
-		return;
-	}
-	rlock(&pg->ns);
-	if(waserror()){
-		runlock(&pg->ns);
-		nexterror();
-	}
-
-	he = &pg->mnthash[MNTHASH];
-	for(h = pg->mnthash; h < he; h++){
-		for(f = *h; f != nil; f = f->hash){
-			print("head: %#p: %s %#llux.%lud %C %lud -> \n", f,
-				f->from->path->s, f->from->qid.path,
-				f->from->qid.vers, devtab[f->from->type]->dc,
-				f->from->dev);
-			for(t = f->mount; t != nil; t = t->next)
-				print("\t%#p: %s (umh %#p) (path %#.8llux dev %C %lud)\n",
-					t, t->to->path->s, t->to->umh, t->to->qid.path, devtab[t->to->type]->dc, t->to->dev);
-		}
-	}
-	poperror();
-	runlock(&pg->ns);
-}
-
 char*
 chanpath(Chan *c)
 {
@@ -269,8 +228,6 @@
 	return c;
 }
 
-Ref npath;
-
 Path*
 newpath(char *s)
 {
@@ -284,7 +241,6 @@
 	p->s = smalloc(p->alen);
 	memmove(p->s, s, i+1);
 	p->ref = 1;
-	incref(&npath);
 
 	/*
 	 * Cannot use newpath for arbitrary names because the mtpt 
@@ -308,8 +264,6 @@
 	
 	pp = smalloc(sizeof(Path));
 	pp->ref = 1;
-	incref(&npath);
-	DBG("copypath %s %p => %p\n", p->s, p, pp);
 	
 	pp->len = p->len;
 	pp->alen = p->alen;
@@ -332,23 +286,14 @@
 pathclose(Path *p)
 {
 	int i;
-	
-	if(p == nil)
-		return;
-//XXX
-	DBG("pathclose %p %s ref=%ld =>", p, p->s, p->ref);
-	for(i=0; i<p->mlen; i++)
-		DBG(" %p", p->mtpt[i]);
-	DBG("\n");
 
-	if(decref(p))
+	if(p == nil || decref(p))
 		return;
-	decref(&npath);
-	free(p->s);
 	for(i=0; i<p->mlen; i++)
 		if(p->mtpt[i] != nil)
 			cclose(p->mtpt[i]);
 	free(p->mtpt);
+	free(p->s);
 	free(p);
 }
 
@@ -406,8 +351,9 @@
 	p = uniquepath(p);
 
 	i = strlen(s);
-	if(p->len+1+i+1 > p->alen){
-		a = p->len+1+i+1 + PATHSLOP;
+	a = p->len+1+i+1;
+	if(a > p->alen){
+		a += PATHSLOP;
 		t = smalloc(a);
 		memmove(t, p->s, p->len+1);
 		free(p->s);
@@ -421,7 +367,6 @@
 	p->len += i;
 	if(isdotdot(s)){
 		fixdotdotname(p);
-		DBG("addelem %s .. => rm %p\n", p->s, p->mtpt[p->mlen-1]);
 		if(p->mlen > 1 && (c = p->mtpt[--p->mlen]) != nil){
 			p->mtpt[p->mlen] = nil;
 			cclose(c);
@@ -434,7 +379,6 @@
 			free(p->mtpt);
 			p->mtpt = tt;
 		}
-		DBG("addelem %s %s => add %p\n", p->s, s, from);
 		p->mtpt[p->mlen++] = from;
 		if(from != nil)
 			incref(from);
@@ -572,8 +516,6 @@
 	if(c == nil || c->ref < 1 || c->flag&CFREE)
 		panic("cclose %#p", getcallerpc(&c));
 
-	DBG("cclose %p name=%s ref=%ld\n", c, chanpath(c), c->ref);
-
 	if(decref(c))
 		return;
 
@@ -598,12 +540,8 @@
 	if(c == nil || c->ref < 1 || c->flag&CFREE)
 		panic("ccloseq %#p", getcallerpc(&c));
 
-	DBG("ccloseq %p name=%s ref=%ld\n", c, chanpath(c), c->ref);
-
-	if(decref(c))
-		return;
-
-	closechanq(c);
+	if(decref(c) == 0)
+		closechanq(c);
 }
 
 /*
@@ -669,6 +607,35 @@
 	return mh;
 }
 
+/*
+ * This is necessary because there are many
+ * pointers to the top of a given mount list:
+ *
+ *	- the mhead in the namespace hash table
+ *	- the mhead in chans returned from findmount:
+ *	  used in namec and then by unionread.
+ *	- the mhead in chans returned from createdir:
+ *	  used in the open/create race protect, which is gone.
+ *
+ * The RWlock in the Mhead protects the mount list it contains.
+ * The mount list is deleted in cunmount() and closepgrp().
+ * The RWlock ensures that nothing is using the mount list at that time.
+ *
+ * It is okay to replace c->mh with whatever you want as 
+ * long as you are sure you have a unique reference to it.
+ *
+ * This comment might belong somewhere else.
+ */
+void
+putmhead(Mhead *m)
+{
+	if(m != nil && decref(m) == 0){
+		assert(m->mount == nil);
+		cclose(m->from);
+		free(m);
+	}
+}
+
 int
 cmount(Chan **newp, Chan *old, int flag, char *spec)
 {
@@ -781,7 +748,6 @@
 		f->next = m->mount;
 		m->mount = nm;
 	}
-
 	wunlock(&m->lock);
 	poperror();
 	return nm->mountid;
@@ -822,35 +788,32 @@
 	}
 
 	wlock(&m->lock);
+	f = m->mount;
 	if(mounted == nil){
 		*l = m->hash;
-		wunlock(&pg->ns);
-		mountfree(m->mount);
 		m->mount = nil;
-		cclose(m->from);
 		wunlock(&m->lock);
+		wunlock(&pg->ns);
+		mountfree(f);
 		putmhead(m);
 		return;
 	}
-
-	p = &m->mount;
-	for(f = *p; f != nil; f = f->next){
-		/* BUG: Needs to be 2 pass */
+	for(p = &m->mount; f != nil; f = f->next){
 		if(eqchan(f->to, mounted, 1) ||
 		  (f->to->mchan != nil && eqchan(f->to->mchan, mounted, 1))){
 			*p = f->next;
 			f->next = nil;
-			mountfree(f);
 			if(m->mount == nil){
 				*l = m->hash;
-				cclose(m->from);
 				wunlock(&m->lock);
 				wunlock(&pg->ns);
+				mountfree(f);
 				putmhead(m);
 				return;
 			}
 			wunlock(&m->lock);
 			wunlock(&pg->ns);
+			mountfree(f);
 			return;
 		}
 		p = &f->next;
@@ -882,6 +845,7 @@
 int
 findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
 {
+	Chan *to;
 	Pgrp *pg;
 	Mhead *m;
 
@@ -888,30 +852,24 @@
 	pg = up->pgrp;
 	rlock(&pg->ns);
 	for(m = MOUNTH(pg, qid); m != nil; m = m->hash){
-		rlock(&m->lock);
-		if(m->from == nil){
-			print("m %p m->from 0\n", m);
-			runlock(&m->lock);
-			continue;
-		}
 		if(eqchantdqid(m->from, type, dev, qid, 1)){
+			rlock(&m->lock);
 			runlock(&pg->ns);
-			if(mp != nil){
+			if(mp != nil)
 				incref(m);
-				if(*mp != nil)
-					putmhead(*mp);
+			to = m->mount->to;
+			incref(to);
+			runlock(&m->lock);
+			if(mp != nil){
+				putmhead(*mp);
 				*mp = m;
 			}
 			if(*cp != nil)
 				cclose(*cp);
-			incref(m->mount->to);
-			*cp = m->mount->to;
-			runlock(&m->lock);
+			*cp = to;
 			return 1;
 		}
-		runlock(&m->lock);
 	}
-
 	runlock(&pg->ns);
 	return 0;
 }
@@ -922,7 +880,7 @@
 static int
 domount(Chan **cp, Mhead **mp, Path **path)
 {
-	Chan **lc;
+	Chan **lc, *from;
 	Path *p;
 
 	if(findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid) == 0)
@@ -934,12 +892,12 @@
 		if(p->mlen <= 0)
 			print("domount: path %s has mlen==%d\n", p->s, p->mlen);
 		else{
+			from = (*mp)->from;
+			incref(from);
 			lc = &p->mtpt[p->mlen-1];
-DBG("domount %p %s => add %p (was %p)\n", p, p->s, (*mp)->from, p->mtpt[p->mlen-1]);
-			incref((*mp)->from);
 			if(*lc != nil)
 				cclose(*lc);
-			*lc = (*mp)->from;
+			*lc = from;
 		}
 		*path = p;
 	}
@@ -961,7 +919,6 @@
 			path->s, path->ref, path->mlen, getcallerpc(&c));
 
 	if(path->mlen > 0 && (nc = path->mtpt[path->mlen-1]) != nil){
-DBG("undomount %p %s => remove %p\n", path, path->s, nc);
 		cclose(c);
 		path->mtpt[path->mlen-1] = nil;
 		c = nc;
@@ -1026,8 +983,7 @@
 			pathclose(path);
 			cclose(c);
 			kstrcpy(up->errstr, Enotdir, ERRMAX);
-			if(mh != nil)
-				putmhead(mh);
+			putmhead(mh);
 			return -1;
 		}
 		ntry = nnames - nhave;
@@ -1073,8 +1029,7 @@
 				pathclose(path);
 				if(nerror)
 					*nerror = nhave+1;
-				if(mh != nil)
-					putmhead(mh);
+				putmhead(mh);
 				return -1;
 			}
 		}
@@ -1113,8 +1068,7 @@
 						kstrcpy(up->errstr, Enotdir, ERRMAX);
 					}
 					free(wq);
-					if(mh != nil)
-						putmhead(mh);
+					putmhead(mh);
 					return -1;
 				}
 				n = wq->nqid;
@@ -1140,11 +1094,8 @@
 		mh = nmh;
 		free(wq);
 	}
-
 	putmhead(mh);
-
 	c = cunique(c);
-
 	if(c->umh != nil){	//BUG
 		print("walk umh\n");
 		putmhead(c->umh);
@@ -1176,7 +1127,7 @@
 		nexterror();
 	}
 	for(f = m->mount; f != nil; f = f->next){
-		if(f->to != nil && (f->mflag&MCREATE) != 0){
+		if((f->mflag&MCREATE) != 0){
 			nc = cclone(f->to);
 			runlock(&m->lock);
 			poperror();
@@ -1251,15 +1202,6 @@
 		*slash++ = '\0';
 		name = slash;
 	}
-	
-	if(0 && chandebug){
-		int i;
-		
-		print("parsename %s:", e->name);
-		for(i=0; i<=e->nelems; i++)
-			print(" %d", e->off[i]);
-		print("\n");
-	}
 }
 
 void*
@@ -1360,7 +1302,6 @@
 		free(aname);
 		nexterror();
 	}
-	DBG("namec %s %d %d\n", aname, amode, omode);
 	name = aname;
 
 	/*
@@ -1432,8 +1373,6 @@
 		if(e.off[e.nerror]==0)
 			print("nerror=%d but off=%d\n",
 				e.nerror, e.off[e.nerror]);
-		if(0 && chandebug)
-			print("showing %d+%d/%d (of %d) of %s (%d %d)\n", e.prefix, e.off[e.nerror], e.nerror, e.nelems, aname, e.off[0], e.off[1]);
 		len = e.prefix+e.off[e.nerror];
 		free(e.off);
 		namelenerror(aname, len, tmperrbuf);
@@ -1480,8 +1419,7 @@
 		m = nil;
 		if(!nomount)
 			domount(&c, &m, nil);
-		if(c->umh != nil)
-			putmhead(c->umh);
+		putmhead(c->umh);
 		c->umh = m;
 		break;
 
@@ -1519,11 +1457,11 @@
 
 		case Aopen:
 		case Acreate:
-if(c->umh != nil){
-	print("cunique umh Open\n");
-	putmhead(c->umh);
-	c->umh = nil;
-}
+			if(c->umh != nil){
+				print("cunique umh Open\n");
+				putmhead(c->umh);
+				c->umh = nil;
+			}
 			/* only save the mount head if it's a multiple element union */
 			if(m != nil && m->mount != nil && m->mount->next != nil)
 				c->umh = m;
@@ -1642,8 +1580,7 @@
 				cnew->flag |= CCEXEC;
 			if(omode & ORCLOSE)
 				cnew->flag |= CRCLOSE;
-			if(m != nil)
-				putmhead(m);
+			putmhead(m);
 			cclose(c);
 			c = cnew;
 			c->path = addelem(c->path, e.elems[e.nelems-1], nil);
@@ -1652,8 +1589,7 @@
 
 		/* create failed */
 		cclose(cnew);
-		if(m != nil)
-			putmhead(m);
+		putmhead(m);
 		if(omode & OEXCL)
 			nexterror();
 		/* save error */
@@ -1788,32 +1724,3 @@
 		return;
 	error(Enotdir);
 }
-
-/*
- * This is necessary because there are many
- * pointers to the top of a given mount list:
- *
- *	- the mhead in the namespace hash table
- *	- the mhead in chans returned from findmount:
- *	  used in namec and then by unionread.
- *	- the mhead in chans returned from createdir:
- *	  used in the open/create race protect, which is gone.
- *
- * The RWlock in the Mhead protects the mount list it contains.
- * The mount list is deleted when we cunmount.
- * The RWlock ensures that nothing is using the mount list at that time.
- *
- * It is okay to replace c->mh with whatever you want as 
- * long as you are sure you have a unique reference to it.
- *
- * This comment might belong somewhere else.
- */
-void
-putmhead(Mhead *m)
-{
-	if(m != nil && decref(m) == 0){
-		m->mount = (Mount*)0xCafeBeef;
-		free(m);
-	}
-}
-
--- a/sys/src/9/port/pgrp.c
+++ b/sys/src/9/port/pgrp.c
@@ -67,29 +67,24 @@
 void
 closepgrp(Pgrp *p)
 {
-	Mhead **h, **e, *f, *next;
+	Mhead **h, **e, *f;
+	Mount *m;
 
-	if(decref(p) != 0)
+	if(decref(p))
 		return;
 
-	qlock(&p->debug);
-	wlock(&p->ns);
-	p->pgrpid = -1;
-
 	e = &p->mnthash[MNTHASH];
 	for(h = p->mnthash; h < e; h++) {
-		for(f = *h; f; f = next) {
+		while((f = *h) != nil){
+			*h = f->hash;
 			wlock(&f->lock);
-			cclose(f->from);
-			mountfree(f->mount);
+			m = f->mount;
 			f->mount = nil;
-			next = f->hash;
 			wunlock(&f->lock);
+			mountfree(m);
 			putmhead(f);
 		}
 	}
-	wunlock(&p->ns);
-	qunlock(&p->debug);
 	free(p);
 }
 
@@ -98,12 +93,12 @@
 {
 	Mount *f;
 
-	m->order = 0;
-	if(*order == 0) {
+	m->order = nil;
+	if(*order == nil) {
 		*order = m;
 		return;
 	}
-	for(f = *order; f; f = f->order) {
+	for(f = *order; f != nil; f = f->order) {
 		if(m->mountid < f->mountid) {
 			m->order = f;
 			*order = m;
@@ -125,17 +120,17 @@
 	Mhead *f, **tom, **l, *mh;
 
 	wlock(&from->ns);
-	order = 0;
+	order = nil;
 	tom = to->mnthash;
 	for(i = 0; i < MNTHASH; i++) {
 		l = tom++;
-		for(f = from->mnthash[i]; f; f = f->hash) {
+		for(f = from->mnthash[i]; f != nil; f = f->hash) {
 			rlock(&f->lock);
 			mh = newmhead(f->from);
 			*l = mh;
 			l = &mh->hash;
 			link = &mh->mount;
-			for(m = f->mount; m; m = m->next) {
+			for(m = f->mount; m != nil; m = m->next) {
 				n = newmount(mh, m->to, m->mflag, m->spec);
 				m->copy = n;
 				pgrpinsert(&order, m);
@@ -148,7 +143,7 @@
 	/*
 	 * Allocate mount ids in the same sequence as the parent group
 	 */
-	for(m = order; m; m = m->order)
+	for(m = order; m != nil; m = m->order)
 		m->copy->mountid = incref(&mountid);
 	wunlock(&from->ns);
 }
@@ -184,7 +179,7 @@
 
 	new->maxfd = f->maxfd;
 	for(i = 0; i <= f->maxfd; i++) {
-		if(c = f->fd[i]){
+		if((c = f->fd[i]) != nil){
 			incref(c);
 			new->fd[i] = c;
 		}
@@ -200,12 +195,9 @@
 	int i;
 	Chan *c;
 
-	if(f == 0)
+	if(f == nil || decref(f))
 		return;
 
-	if(decref(f) != 0)
-		return;
-
 	/*
 	 * If we get into trouble, forceclosefgrp
 	 * will bail us out.
@@ -212,7 +204,7 @@
 	 */
 	up->closingfgrp = f;
 	for(i = 0; i <= f->maxfd; i++)
-		if(c = f->fd[i]){
+		if((c = f->fd[i]) != nil){
 			f->fd[i] = nil;
 			cclose(c);
 		}
@@ -246,7 +238,7 @@
 
 	f = up->closingfgrp;
 	for(i = 0; i <= f->maxfd; i++)
-		if(c = f->fd[i]){
+		if((c = f->fd[i]) != nil){
 			f->fd[i] = nil;
 			ccloseq(c);
 		}
@@ -259,12 +251,12 @@
 	Mount *m;
 
 	m = smalloc(sizeof(Mount));
-	m->to = to;
 	m->head = mh;
+	m->to = to;
 	incref(to);
 	m->mountid = incref(&mountid);
 	m->mflag = flag;
-	if(spec != 0)
+	if(spec != nil)
 		kstrdup(&m->spec, spec);
 
 	return m;
@@ -275,13 +267,11 @@
 {
 	Mount *f;
 
-	while(m) {
-		f = m->next;
-		cclose(m->to);
-		m->mountid = 0;
-		free(m->spec);
-		free(m);
-		m = f;
+	while((f = m) != nil) {
+		m = m->next;
+		cclose(f->to);
+		free(f->spec);
+		free(f);
 	}
 }
 
@@ -288,15 +278,15 @@
 void
 resrcwait(char *reason)
 {
+	static ulong lastwhine;
 	ulong now;
 	char *p;
-	static ulong lastwhine;
 
-	if(up == 0)
+	if(up == nil)
 		panic("resrcwait");
 
 	p = up->psstate;
-	if(reason) {
+	if(reason != nil) {
 		if(waserror()){
 			up->psstate = p;
 			nexterror();
@@ -310,7 +300,7 @@
 		}
 	}
 	tsleep(&up->sleep, return0, 0, 100+nrand(200));
-	if(reason) {
+	if(reason != nil) {
 		up->psstate = p;
 		poperror();
 	}
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -476,11 +476,9 @@
 struct Pgrp
 {
 	Ref;
-	Lock;
+	RWlock	ns;			/* Namespace n read/one write lock */
 	int	noattach;
 	ulong	pgrpid;
-	QLock	debug;			/* single access via devproc.c */
-	RWlock	ns;			/* Namespace n read/one write lock */
 	Mhead	*mnthash[MNTHASH];
 };