trivfs_S_io_write takes tdev->lock before net_bh_lock, while tunnel_xmit
(which runs under net_bh, with net_bh_lock held by net_bh_worker) takes
tdev->lock.  This is an ABBA deadlock: a thread can hold tdev and wait for
net_bh while the net_bh worker holds net_bh and waits for tdev.  A few
rapid TCP connections through the TUN can deadlock pfinet, which then
stops answering RPCs and takes the whole host off the network while the
machine itself keeps running.

Take net_bh_lock before tdev->lock, matching the order used by
tunnel_xmit.

* pfinet/tunnel.c (trivfs_S_io_write): Lock net_bh_lock before tdev->lock.
---
 pfinet/tunnel.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c
index 4301d05..6732ac5 100644
--- a/pfinet/tunnel.c
+++ b/pfinet/tunnel.c
@@ -388,9 +388,9 @@ trivfs_S_io_write (struct trivfs_protid *cred,
 
   tdev = (struct tunnel_device *) cred->po->cntl->hook;
 
+  pthread_mutex_lock (&net_bh_lock);
   pthread_mutex_lock (&tdev->lock);
 
-  pthread_mutex_lock (&net_bh_lock);
   skb = alloc_skb (NET_IP_ALIGN + datalen, GFP_ATOMIC);
   skb_reserve(skb, NET_IP_ALIGN);
   skb->len = datalen;
@@ -402,11 +402,12 @@ trivfs_S_io_write (struct trivfs_protid *cred,
   skb->mac.raw = skb->data;
   skb->protocol = htons (ETH_P_IP);
   netif_rx (skb);
+
+  pthread_mutex_unlock (&tdev->lock);
   pthread_mutex_unlock (&net_bh_lock);
 
   *amount = datalen;
 
-  pthread_mutex_unlock (&tdev->lock);
   return 0;
 }
 
-- 
2.47.3


Reply via email to