On Thu, Oct 05, 2023 at 12:08:55AM +0200, Kirill Miazine wrote:
> • Vitaliy Makkoveev [2023-10-05 00:02]:
> > > On 5 Oct 2023, at 00:56, Kirill Miazine <k...@krot.org> wrote:
> > > 
> > > new diff doesn't prevent hang in test scenario either.
> > > 
> > 
> > Which one?
> 
> I meant to say new diffS, as I had applied both... what I have now is this:
> 

Understood.

The problem lays here:

ifq_start_task(void *p)
{
        struct ifqueue *ifq = p;
        struct ifnet *ifp = ifq->ifq_if;

        if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
            ifq_empty(ifq) || ifq_is_oactive(ifq))
                return;

        ifp->if_qstart(ifq);
}

wg_qstart(struct ifqueue *ifq)
{
        /* [...] */     
        while ((m = ifq_dequeue(ifq)) != NULL) {
        /* [...] */     
}

wg_peer_destroy(struct wg_peer *peer)
{
        /* [...] */     
        NET_LOCK();
        while (!ifq_empty(&sc->sc_if.if_snd)) {
                NET_UNLOCK();
                tsleep_nsec(sc, PWAIT, "wg_ifq", 1000);
                NET_LOCK();
        }
        NET_UNLOCK();
        /* [...] */     
}

1. wg_output() placed some packets to sc->sc_if.if_snd and scheduled
ifq_start_task() to run.

2. You performed "ifconfig wg1 down", so wg_down() cleared IFF_RUNNING
flag.

3. ifq_start_task() started to run, IFF_RUNNING is not set, so
wg_qstart() will not be called as the ifq_dequeue(). Packets rests
within sc->sc_if.if_snd. The interface is down, so nothing would
schedule ifq_start_task() to run.

4. You performed "ifconfig wg1 destroy". The while(!ifq_empty()) loop is
infinite because nothing would empty sc->sc_if.if_snd at this point.

The unlocked !ISSET(ifp->if_flags, IFF_RUNNING), ifq_empty() and 
ifq_is_oactive() are bad, but netlock dances provide caches
synchronisation.

I have no quick solution for this. Probably we should rethink
ifq_start_task().

This diff checks IFF_RUNNING flag within while (!ifq_empty()) loop of
wg_peer_destroy(). If the flag is not set queue will be purged and check
performed again. I intentionally keep netlock to prevent ifconfig
manipulations on the interface.


Index: sys/net/if_wg.c
===================================================================
RCS file: /cvs/src/sys/net/if_wg.c,v
retrieving revision 1.31
diff -u -p -r1.31 if_wg.c
--- sys/net/if_wg.c     26 Sep 2023 15:16:44 -0000      1.31
+++ sys/net/if_wg.c     4 Oct 2023 23:09:14 -0000
@@ -509,6 +509,13 @@ wg_peer_destroy(struct wg_peer *peer)
 
        NET_LOCK();
        while (!ifq_empty(&sc->sc_if.if_snd)) {
+               /*
+                * XXX: `if_snd' of stopped interface could still packets
+                */
+               if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
+                       ifq_purge(&sc->sc_if.if_snd);
+                       continue;
+               }
                NET_UNLOCK();
                tsleep_nsec(sc, PWAIT, "wg_ifq", 1000);
                NET_LOCK();

Reply via email to