After net/ifq.c rev 1.38 was reverted pppac(4) still has this problem. pppac_output() called under NET_LOCK(). pppac_output() calls if_enqueue() which calls ifq_start(). But now ifq_start() can run pppac_input() directly under NET_LOCK() and also is can enqueue work and pppac_input() will be called without NET_LOCK(). pppac_input() calls pipex_output() which requires NET_LOCK().
We can be sure what pppx_if_input() will be always called under NET_LOCK() because it's `if_snd' queue size is 1. I guess to make this restriction to pppac(4) just to be sure pppac_input() is always called under NET_LOCK() as temporary solution. I guess it's better then unlock/lock dances around if_enqueue(). Also 6.7 release has this problem. And I like to know, how to fix it too. Index: sys/net/if_pppx.c =================================================================== RCS file: /cvs/src/sys/net/if_pppx.c,v retrieving revision 1.84 diff -u -p -r1.84 if_pppx.c --- sys/net/if_pppx.c 18 Apr 2020 04:03:56 -0000 1.84 +++ sys/net/if_pppx.c 21 May 2020 12:31:49 -0000 @@ -1054,6 +1054,8 @@ pppx_if_start(struct ifnet *ifp) struct mbuf *m; int proto; + NET_ASSERT_LOCKED(); + if (!ISSET(ifp->if_flags, IFF_RUNNING)) return; @@ -1290,6 +1292,8 @@ pppacopen(dev_t dev, int flags, int mode ifp->if_output = pppac_output; ifp->if_start = pppac_start; ifp->if_ioctl = pppac_ioctl; + /* XXX: To be sure pppac_input() is called under NET_LOCK() */ + IFQ_SET_MAXLEN(&ifp->if_snd, 1); if_counters_alloc(ifp); if_attach(ifp); @@ -1609,6 +1613,8 @@ pppac_output(struct ifnet *ifp, struct m { int error; + NET_ASSERT_LOCKED(); + if (!ISSET(ifp->if_flags, IFF_RUNNING)) { error = EHOSTDOWN; goto drop; @@ -1639,6 +1645,8 @@ pppac_start(struct ifnet *ifp) { struct pppac_softc *sc = ifp->if_softc; struct mbuf *m; + + NET_ASSERT_LOCKED(); while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) { #if NBPFILTER > 0