Module Name:    src
Committed By:   riastradh
Date:           Mon Jul 29 19:45:56 UTC 2024

Modified Files:
        src/sys/net: if_wg.c

Log Message:
wg(4): Trigger session initiation in wgintr, not in wg_output.

We have to look up the session in wgintr anyway, for
wg_send_data_msg.  By triggering session initiation in wgintr instead
of wg_output, we can skip the stable session lookup and reference in
wg_output -- simpler that way.

Post-fix tidying for:

PR kern/55729: net/if_wg/t_misc:wg_rekey test case fails
PR kern/56252: wg(4) state machine has race conditions
PR kern/58463: if_wg does not work when idle.


To generate a diff of this commit:
cvs rdiff -u -r1.125 -r1.126 src/sys/net/if_wg.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_wg.c
diff -u src/sys/net/if_wg.c:1.125 src/sys/net/if_wg.c:1.126
--- src/sys/net/if_wg.c:1.125	Mon Jul 29 19:44:22 2024
+++ src/sys/net/if_wg.c	Mon Jul 29 19:45:56 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wg.c,v 1.125 2024/07/29 19:44:22 riastradh Exp $	*/
+/*	$NetBSD: if_wg.c,v 1.126 2024/07/29 19:45:56 riastradh Exp $	*/
 
 /*
  * Copyright (C) Ryota Ozaki <ozaki.ry...@gmail.com>
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.125 2024/07/29 19:44:22 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.126 2024/07/29 19:45:56 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_altq_enabled.h"
@@ -1911,7 +1911,7 @@ wg_send_handshake_msg_init(struct wg_sof
 		WG_DLOG("send_hs_msg failed, error=%d\n", error);
 		wg_put_session_index(wg, wgs);
 		m = atomic_swap_ptr(&wgp->wgp_pending, NULL);
-		membar_acquire(); /* matches membar_release in wg_output */
+		membar_acquire(); /* matches membar_release in wgintr */
 		m_freem(m);
 		return;
 	}
@@ -2082,7 +2082,7 @@ wg_swap_sessions(struct wg_softc *wg, st
 	 * or else the responder will never answer.
 	 */
 	if ((m = atomic_swap_ptr(&wgp->wgp_pending, NULL)) != NULL) {
-		membar_acquire(); /* matches membar_release in wg_output */
+		membar_acquire(); /* matches membar_release in wgintr */
 		kpreempt_disable();
 		const uint32_t h = curcpu()->ci_index; // pktq_rps_hash(m)
 		M_SETCTX(m, wgp);
@@ -3708,8 +3708,28 @@ wgintr(void *cookie)
 	while ((m = pktq_dequeue(wg_pktq)) != NULL) {
 		wgp = M_GETCTX(m, struct wg_peer *);
 		if ((wgs = wg_get_stable_session(wgp, &psref)) == NULL) {
+			/*
+			 * No established session.  If we're the first
+			 * to try sending data, schedule a handshake
+			 * and queue the packet for when the handshake
+			 * is done; otherwise just drop the packet and
+			 * let the ongoing handshake attempt continue.
+			 * We could queue more data packets but it's
+			 * not clear that's worthwhile.
+			 */
 			WG_TRACE("no stable session");
-			wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE);
+			membar_release();
+			if ((m = atomic_swap_ptr(&wgp->wgp_pending, m)) ==
+			    NULL) {
+				WG_TRACE("queued first packet;"
+				    " init handshake");
+				wg_schedule_peer_task(wgp,
+				    WGP_TASK_SEND_INIT_MESSAGE);
+			} else {
+				membar_acquire();
+				WG_TRACE("first packet already queued,"
+				    " dropping");
+			}
 			goto next0;
 		}
 		if (__predict_false(wg_session_hit_limits(wgs))) {
@@ -3732,7 +3752,7 @@ wg_purge_pending_packets(struct wg_peer 
 	struct mbuf *m;
 
 	m = atomic_swap_ptr(&wgp->wgp_pending, NULL);
-	membar_acquire();     /* matches membar_release in wg_output */
+	membar_acquire();     /* matches membar_release in wgintr */
 	m_freem(m);
 #ifdef ALTQ
 	wg_start(&wgp->wgp_sc->wg_if);
@@ -4202,8 +4222,7 @@ wg_output(struct ifnet *ifp, struct mbuf
 {
 	struct wg_softc *wg = ifp->if_softc;
 	struct wg_peer *wgp = NULL;
-	struct wg_session *wgs = NULL;
-	struct psref wgp_psref, wgs_psref;
+	struct psref wgp_psref;
 	int bound;
 	int error;
 
@@ -4240,33 +4259,7 @@ wg_output(struct ifnet *ifp, struct mbuf
 	m->m_pkthdr.csum_flags = 0;
 	m->m_pkthdr.csum_data = 0;
 
-	/* Check whether there's an established session.  */
-	wgs = wg_get_stable_session(wgp, &wgs_psref);
-	if (wgs == NULL) {
-		/*
-		 * No established session.  If we're the first to try
-		 * sending data, schedule a handshake and queue the
-		 * packet for when the handshake is done; otherwise
-		 * just drop the packet and let the ongoing handshake
-		 * attempt continue.  We could queue more data packets
-		 * but it's not clear that's worthwhile.
-		 *
-		 * membar_release matches membar_acquire in
-		 * wg_swap_sessions, wg_purge_pending_packets, and
-		 * wg_send_handshake_msg_init.
-		 */
-		membar_release();
-		if ((m = atomic_swap_ptr(&wgp->wgp_pending, m)) == NULL) {
-			WG_TRACE("queued first packet; init handshake");
-			wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE);
-		} else {
-			membar_acquire();
-			WG_TRACE("first packet already queued, dropping");
-		}
-		goto out1;
-	}
-
-	/* There's an established session.  Toss it in the queue.  */
+	/* Toss it in the queue.  */
 #ifdef ALTQ
 	if (altq) {
 		mutex_enter(ifp->if_snd.ifq_lock);
@@ -4278,7 +4271,7 @@ wg_output(struct ifnet *ifp, struct mbuf
 		mutex_exit(ifp->if_snd.ifq_lock);
 		if (m == NULL) {
 			wg_start(ifp);
-			goto out2;
+			goto out1;
 		}
 	}
 #endif
@@ -4289,17 +4282,16 @@ wg_output(struct ifnet *ifp, struct mbuf
 		WGLOG(LOG_ERR, "%s: pktq full, dropping\n",
 		    if_name(&wg->wg_if));
 		error = ENOBUFS;
-		goto out3;
+		goto out2;
 	}
 	m = NULL;		/* consumed */
 	error = 0;
-out3:	kpreempt_enable();
+out2:	kpreempt_enable();
 
 #ifdef ALTQ
-out2:
+out1:
 #endif
-	wg_put_session(wgs, &wgs_psref);
-out1:	wg_put_peer(wgp, &wgp_psref);
+	wg_put_peer(wgp, &wgp_psref);
 out0:	m_freem(m);
 	curlwp_bindx(bound);
 	return error;

Reply via email to