shithub: riscv

Download patch

ref: 6e284eaad57843950a54f44f02f920216dd3fc88
parent: 5c2b4dfc8173355629e70a6437ccfc378f0bf757
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Wed May 16 17:41:42 EDT 2018

ndb/cs: prevent deadlock with ndb/cs by mounting /srv/dns *AFTER* /net

the dnsquery() library function should not start mouting /srv/dns on
its own. this problem arrises only for ndb/cs as it is started before
ndb/dns.

the issue with mounting /srv/dns before /net is when ndb/cs attempts
to read the list of interfaces, accessing /net/ipifc, which triggers
a rpc to ndb/dns as it is ontop of the mount. this can yield a deadlock
when ndb/dns blocks its 9p loop waiting for requests to complete on
a refresh and the requests are stuck waiting for ndb/cs to translate
a dial string for announce().

--- a/sys/src/cmd/ndb/cs.c
+++ b/sys/src/cmd/ndb/cs.c
@@ -1081,12 +1081,8 @@
 	Network *np;
 
 	if(background){
-		switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
-		case 0:
-			break;
-		default:
+		if(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT) != 0)
 			return;
-		}
 		qlock(&netlock);
 	}
 
@@ -1627,6 +1623,43 @@
 
 }
 
+static int
+mountdns(void)
+{
+	static QLock mountlock;
+	static int mounted;
+	char buf[128], *p;
+	int fd;
+
+	if(mounted)
+		return 0;
+
+	qlock(&mountlock);
+	snprint(buf, sizeof(buf), "%s/dns", mntpt);
+	if(access(buf, AEXIST) == 0)
+		goto done;
+	if(strcmp(mntpt, "/net") == 0)
+		snprint(buf, sizeof(buf), "/srv/dns");
+	else {
+		snprint(buf, sizeof(buf), "/srv/dns%s", mntpt);
+		while((p = strchr(buf+8, '/')) != nil)
+			*p = '_';
+	}
+	if((fd = open(buf, ORDWR)) < 0){
+err:
+		qunlock(&mountlock);
+		return -1;	
+	}
+	if(mount(fd, -1, mntpt, MAFTER, "") < 0){
+		close(fd);
+		goto err;
+	}
+done:
+	mounted = 1;
+	qunlock(&mountlock);
+	return 0;
+}
+
 static Ndbtuple*
 dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
 {
@@ -1659,6 +1692,11 @@
 	if(*isslave == 0){
 		qlock(&dblock);
 		werrstr("too much activity");
+		return nil;
+	}
+
+	if(mountdns() < 0){
+		qlock(&dblock);
 		return nil;
 	}
 
--- a/sys/src/libndb/dnsquery.c
+++ b/sys/src/libndb/dnsquery.c
@@ -12,13 +12,12 @@
  *  search for a tuple that has the given 'attr=val' and also 'rattr=x'.
  *  copy 'x' into 'buf' and return the whole tuple.
  *
- *  return 0 if not found.
+ *  return nil if not found.
  */
 Ndbtuple*
 dnsquery(char *net, char *val, char *type)
 {
-	char rip[128];
-	char *p;
+	char buf[128];
 	Ndbtuple *t;
 	int fd;
 
@@ -28,37 +27,18 @@
 
 	if(net == nil)
 		net = "/net";
-	snprint(rip, sizeof(rip), "%s/dns", net);
-	fd = open(rip, ORDWR);
-	if(fd < 0){
-		if(strcmp(net, "/net") == 0)
-			snprint(rip, sizeof(rip), "/srv/dns");
-		else {
-			snprint(rip, sizeof(rip), "/srv/dns%s", net);
-			p = strrchr(rip, '/');
-			*p = '_';
-		}
-		fd = open(rip, ORDWR);
-		if(fd < 0)
-			return nil;
-		if(mount(fd, -1, net, MBEFORE, "") < 0){
-			close(fd);
-			return nil;
-		}
-		/* fd is now closed */
-		snprint(rip, sizeof(rip), "%s/dns", net);
-		fd = open(rip, ORDWR);
-		if(fd < 0)
-			return nil;
-	}
 
+	snprint(buf, sizeof(buf), "%s/dns", net);
+	if((fd = open(buf, ORDWR)) < 0)
+		return nil;
+
 	/* zero out the error string */
 	werrstr("");
 
 	/* if this is a reverse lookup, first lookup the domain name */
 	if(strcmp(type, "ptr") == 0){
-		mkptrname(val, rip, sizeof rip);
-		t = doquery(fd, rip, "ptr");
+		mkptrname(val, buf, sizeof buf);
+		t = doquery(fd, buf, "ptr");
 	} else
 		t = doquery(fd, val, type);