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
