On Fri, May 22, 2020 at 07:57:13AM +1000, David Gwynne wrote: > On Wed, May 20, 2020 at 05:42:35PM +0300, Vitaliy Makkoveev wrote: > > I got splassert with pppx(4) and net/ifq.c rev 1.38 raised by > > NET_ASSERT_LOCKED() in netinet/ip_output.c:113 and underlaying routines. > > > > net/ifq.c rev 1.38 is not in snapshot yet so you need to checkout and > > build kernel to reproduce. > > > > ---- dmesg begin ---- > > > > splassert: ip_output: want 2 have 0 > > Starting stack trace... > > ip_output(fffffd801f8c8800,0,0,0,0,0) at ip_output+0x8f > > pipex_l2tp_output(fffffd801f8c8800,ffff80000e787808) at > > pipex_l2tp_output+0x21d > > pipex_ppp_output(fffffd801f8c8800,ffff80000e787808,21) at > > pipex_ppp_output+0xda > > pppx_if_start(ffff80000e787268) at pppx_if_start+0x83 > > if_qstart_compat(ffff80000e7874e0) at if_qstart_compat+0x2e > > ifq_serialize(ffff80000e7874e0,ffff80000e7875b0) at ifq_serialize+0x103 > > taskq_thread(ffffffff81f3df30) at taskq_thread+0x4d > > end trace frame: 0x0, count: 250 > > End of stack trace. > > splassert: ipsp_spd_lookup: want 2 have 0 > > Starting stack trace... > > ipsp_spd_lookup(fffffd801f8c8800,2,14,ffff80000e726c9c,2,0) at > > ipsp_spd_lookup+0x80 > > ip_output_ipsec_lookup(fffffd801f8c8800,14,ffff80000e726c9c,0,1) at > > ip_output_ipsec_lookup+0x4d > > ip_output(fffffd801f8c8800,0,0,0,0,0) at ip_output+0x4fa > > pipex_l2tp_output(fffffd801f8c8800,ffff80000e787808) at > > pipex_l2tp_output+0x21d > > pipex_ppp_output(fffffd801f8c8800,ffff80000e787808,21) at > > pipex_ppp_output+0xda > > pppx_if_start(ffff80000e787268) at pppx_if_start+0x83 > > if_qstart_compat(ffff80000e7874e0) at if_qstart_compat+0x2e > > ifq_serialize(ffff80000e7874e0,ffff80000e7875b0) at ifq_serialize+0x103 > > taskq_thread(ffffffff81f3df30) at taskq_thread+0x4d > > end trace frame: 0x0, count: 248 > > End of stack trace. > > splassert: spd_table_get: want 2 have 0 > > > > ---- dmesg end ---- > > > > 1. `pxi_if' owned by struct pppx_if has IFXF_MPSAFE flag unset > > 1.1 pppx(4) sets IFQ_SET_MAXLEN(&ifp->if_snd, 1) at net/if_pppx.c:866 > > 2. pppx_if_output() is called under NET_LOCK() > > 3. pppx_if_output() calls if_enqueue() at net/if_pppx.c:1123 > > 4. pppx(4) doesn't set `ifp->if_enqueue' so if_enqueue() calls > > if_enqueue_ifq() at net/if.c:709 (which is set in net/if.c:639) > > 5. if_enqueue_ifq() calls ifq_start() at net/if.c:734 > > 6. ifq_start() we a still under NET_LOCK() here > > > > 6.a. in net/ifq.c rev 1.37 ifq_start() checks "ifq_len(ifq) >= > > min(ifp->if_txmit, ifq->ifq_maxlen)" and this was always true because > > (1.1) so we always call ifq_run_start() which calls ifq_serialize(). > > > > ifq_serialize() will call if_qstart_compat() which calls > > pppx_if_start() which calls pipex_ppp_output() etc while we still > > holding NET_LOCK() so the assertions I reported above are not raised. > > > > 6.b. net/ifq.c rev 1.38 introduce checks of IFXF_MPSAFE flag. so we are > > always going to net/ifq.c:132 where we adding out task to `systq' > > referenced by `ifq_softnet' (ifq_softnet set to `systq' at > > net/ifq.c:199). > > > > taskq_thread() doesn't grab NET_LOCK() so after net/ifq.c rev 1.38 > > ifq_serialize() and underlaying pppx_if_start() call without NET_LOCK() > > and corresponding asserts raised. > > > > The problem I pointed is not in net/ifq.c rev 1.38 but in pppx(4). > > `if_start' routines should grab NET_LOCK() by themself if it is required > > but pppx_if_start() and pppac_start() did't do that. pppac_start() has > > no underlaying NET_ASSERT_LOCKED() so the pppx(4) is the only case were > > the problem is shown. > > > > Since NET_LOCK() is required by pipex(4), diff below adds it to > > pppx_if_start() and pppac_start(). > > > > After net/ifq.c rev 1.38 pppx_if_start() will newer be called from > > pppx_if_output() but from `systq' only so I don't add lock/unlock > > dances around if_enqueue() at net/if_pppx.c:1123. > > > > Diff tested for both pppx(4) and pppac(4) cases. > > thanks for the detailed analysis. i wondered how the ifq change > triggered this exactly, and your mail makes it clear. > > however, pppx/pppac/pipex are not the first or only drivers in the tree > that encapsulate a packet in IP from their if_start routine and send it > out with the network stack. the way this has been solved in every other > driver has been to call ip{,6}_send to transmit the packet instead > of ip{,6}_output. > > can you try the following diff? >
I tested this diff and it works for me. But the problem I pointed is about pipex(4) locking. pipex(4) requires NET_LOCK() be grabbed not only for underlaying ip{,6}_output() but for itself too. But since pppac_start() has unpredictable behavior I suggested to make it predictable [1]. With net/ifq.c rev 1.37 and rev 1.39 pppx_if_start() routine will be allways called under NET_LOCK(), but pppac_start() will not. It depends on packet count stored in `if_snd' (look at net/ifq.c:125). With net/ifq.c rev 1.38 pppx_if_start() and pppac_start() have fully identical behavior and we can simply add NET_LOCK() [2] required by pipex(4). Also I can write my own handler for `ifp->if_enqueue' to be sure about `ifp->if_start' locking state, but I still have misunderstanding why pppx(4) does "IFQ_SET_MAXLEN(&ifp->if_snd, 1)". Just to make locking state of pppx_if_start() predictable or for avoid hidden bug triggering? Also this time I'am not shure about KERNEL_LOCK() locking while pipex(4) is accessed from the stack so I can't say is NET_LOCK() really required for the whole pipex(4). 1. https://marc.info/?l=openbsd-tech&m=159006455720473&w=2 2. https://marc.info/?l=openbsd-tech&m=158998597126002&w=2