summaryrefslogtreecommitdiff
path: root/sys/src/9
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2020-05-10 22:51:40 +0200
committercinap_lenrek <cinap_lenrek@felloff.net>2020-05-10 22:51:40 +0200
commit27fc79b04bee837f513f8ac92c3e50ae76c27abe (patch)
tree5b08a030b7d3ccc39d953f5e692cfce53627f8bd /sys/src/9
parentdbfec06bf1f8de922ca3af09d14675ef44ada5d2 (diff)
devip: fix ifc recursive rlock() deadlock
ipiput4() and ipiput6() are called with the incoming interface rlocked while ipoput4() and ipoput6() also rlock() the outgoing interface once a route has been found. it is common that the incoming and outgoing interfaces are the same recusive rlocking(). the deadlock happens when a reader holds the rlock for the incoming interface, then ip/ipconfig tries to add a new address, trying to wlock the interface. as there are still active readers on the ifc, ip/ipconfig process gets queued on the inteface RWlock. now the reader finds the outgoing route which has the same interface as the incoming packet and tries to rlock the ifc again. but now theres a writer queued, so we also go to sleep waiting four outselfs to release the lock. the solution is to never wait for the outgoing interface rlock, but instead use non-queueing canrlock() and if it cannot be acquired, discard the packet.
Diffstat (limited to 'sys/src/9')
-rw-r--r--sys/src/9/ip/ip.c5
-rw-r--r--sys/src/9/ip/ipifc.c3
-rw-r--r--sys/src/9/ip/ipv6.c5
3 files changed, 10 insertions, 3 deletions
diff --git a/sys/src/9/ip/ip.c b/sys/src/9/ip/ip.c
index 9f7bfa0a0..7f197b22b 100644
--- a/sys/src/9/ip/ip.c
+++ b/sys/src/9/ip/ip.c
@@ -123,7 +123,10 @@ ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
else
gate = r->v4.gate;
- rlock(ifc);
+ if(!canrlock(ifc)){
+ ip->stats[OutDiscards]++;
+ goto free;
+ }
if(waserror()){
runlock(ifc);
nexterror();
diff --git a/sys/src/9/ip/ipifc.c b/sys/src/9/ip/ipifc.c
index 7af840991..9dd55f93b 100644
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -1167,7 +1167,8 @@ findipifc(Fs *f, uchar *local, uchar *remote, int type)
xspec = 0;
for(cp = f->ipifc->conv; *cp != nil; cp++){
ifc = (Ipifc*)(*cp)->ptcl;
- rlock(ifc);
+ if(!canrlock(ifc))
+ continue;
for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
if(type & Runi){
if(ipcmp(remote, lifc->local) == 0){
diff --git a/sys/src/9/ip/ipv6.c b/sys/src/9/ip/ipv6.c
index 7d7cdc09a..1d8750cde 100644
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -76,7 +76,10 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
else
gate = r->v6.gate;
- rlock(ifc);
+ if(!canrlock(ifc)){
+ ip->stats[OutDiscards]++;
+ goto free;
+ }
if(waserror()){
runlock(ifc);
nexterror();