From nobody Sat Sep 26 10:03:04 2026 Received: from mail-oi1-f175.google.com (mail-oi1-f175.google.com [209.85.167.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 42E0F4534A9 for ; Wed, 2 Sep 2026 15:11:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788361898; cv=none; b=dI1GPNazh1O3cdDnQqGKEICMfd3/qbfXZbdBpoo19Pd7/dTUyBap2o/Jt7cJ15pAsOtKfWOrIAj0Qxa+FWySRSy5dWI87aI1rZnrJvmjTFuhsGIp4kyZPm3CtpAHcNWu7fOaAkhu4atknYxuOBTJRO/27KQL3WmxSMWjh7euiLA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788361898; c=relaxed/simple; bh=gasmKhY5QdVetN3HDkPDfGtkZpKw5F4W6g75o0Q/i88=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=fL/LpER2u0rQmc9D7ct096U0ZXGmbnlCNI1N0h+zzKtjWS68vtBD7fcYnUEO0vffLdKFAkYd2+pbSFDrdiqUQE7y8b1Dh080tJ2qlOfcx5sKkfSFTsyjDcf+fZiuWQp5H3pMvbSZ2tY0Gp/SJvxkZSRx9MsPtSDd6l6/DPNLaiU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cloudflare.com; spf=pass smtp.mailfrom=cloudflare.com; dkim=pass (2048-bit key) header.d=cloudflare.com header.i=@cloudflare.com header.b=NO5yNFam; arc=none smtp.client-ip=209.85.167.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cloudflare.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cloudflare.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cloudflare.com header.i=@cloudflare.com header.b="NO5yNFam" Received: by mail-oi1-f175.google.com with SMTP id 5614622812f47-4ab89cff9c7so417347b6e.2 for ; Wed, 02 Sep 2026 08:11:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cloudflare.com; s=google09082023; t=1788361894; x=1788966694; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=6d93cyMkw6EMFToYvRlpelXx7oHrvV3y/wNVC9ldZm0=; b=NO5yNFamOQ6Pttf0nW7mE8+rKgtxV0qy1bbOpsBroOmKjhBIYtm5t8iH/EzHb8FBUB qro1YIOd7fYbhpLFdiM3Z8uIPoETf5KGM025b6X9HrfvYaYy97DlnaXcpUUd3aVTkpq+ iWrnFwPguea8q+2v3P488gDhjRan/nds+VhMBMzBwoSA6yz8Ueo/ceiAThA8mZF0fKfd 47Fi0N39qDeIdaCxdCa7g94C2hmKZAswah3++YFi2u95fPLfjZE1JJMXn8106hqhZOCH pCDG0shvMMdyUanAVm4fHAUFva5XhAnoj1uF9RoRxixDjK3pp5b0jQ8of20/L2vXDJ4Y j/Xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788361894; x=1788966694; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=6d93cyMkw6EMFToYvRlpelXx7oHrvV3y/wNVC9ldZm0=; b=fYBREnEdo11YbjxJpP9nbeOnyAW1BegU9Mg1gKdVUv65MIc8LbGg8WMLdBaBr9XzY9 y/28/9SdEGTxXObdS4X5VV7pVQYl+SZ+6NE6PsqX3A3W6KGsjD/8Nd0I1dgSU0fogaL3 li6Hlh07r16WLbtetAJEcn7i89dv5vL31Q65qyEmqzFUnzeQSzAKcMeNdEoo9amUa3r+ gDik6lmXIMqMAeHHmC6go4X5qD8oCkyBifL6qMphcGo8uQ6z//SGBHe1gvR92PA7QY4+ dVZMLpfLRY1p8lXwyCOoaGFS7ssgn+r8mo7EhD150/Qw0WiGjWOdjnOjTl2YP0kJ/x4d CfVA== X-Forwarded-Encrypted: i=1; AHgh+Rou9WbqjhpKSZz9ZGrmg4lll53uRBbJN4nnKsTzmw1QT3MJzDQvDJ+jR4C9bznJEPjFoBC9BRhIBHyDgdc=@vger.kernel.org X-Gm-Message-State: AFuF++mzDcib+5b0n0XSJiBJpCviHCOpeZPQCpPHDv/c8rg7ElgHmk8C QQ1Vg0lFk9mJZN8mrXNVIHSu0v3giYj/nq7Geb/WqH9TXc+BE5djpLKj55aJnWUBO+lTCRrqeC7 6WyS8rvR9wQ== X-Gm-Gg: AR+sD11tuRZnTzhPPgB+gXW91gM0EJoPHIex5axc6Uh8rJzYiMSrTs+5kQwBxrY2RZf RdaI+G9TR/qKBH5ojcUOoMHLQdMpLdT+e6i3LHidRdkFG2QwwUOnUrNuM0sedQDA7kedIURdJK6 ALQtjM3dSg7IJO4OoPVn+hhhgr4Q5r6xyuxextydlywHVrXl286mpxeQZDmWX6ZPNYJ1JxviMp1 PQgWROdTwCL+s49s/e71/lGuJRA80uCJ4wsypAYOhMg/goN0vvpXGol977MJZFKV2rtk8P6I5df 4PMnoD8PjPVklc3YVY2UlcpvIzGnRB3lzsjnDRVAv4rlGe5URytUjqt1J+cSTbRC95aA1WuVehv PNeuu09x+OqQ421141pP/AYSg1VSGssby/OkUDTYd7EvKjLOlHOPq+PgQUbQH/P2139I9y9fUdv cGLQUg1dl1j/s885DSnkgEuG/fKSj6Vws0sPxX405PM2cGEa+GVQ== X-Received: by 2002:a05:6808:1982:b0:4ae:f5be:fe29 with SMTP id 5614622812f47-4b6bc45eb12mr4432026b6e.5.1788361894525; Wed, 02 Sep 2026 08:11:34 -0700 (PDT) Received: from [127.0.1.1] ([2a09:bac6:bf21:2e28::499:53]) by smtp.gmail.com with ESMTPSA id 5614622812f47-4b698a38f62sm2044063b6e.7.2026.09.02.08.11.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2026 08:11:32 -0700 (PDT) From: Chris J Arges Date: Wed, 02 Sep 2026 10:11:25 -0500 Subject: [PATCH net] wireguard: wait for per-peer crypto during removal Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260902-fix-wg-peer-removal-v1-1-7a0190f5cdb1@cloudflare.com> X-B4-Tracking: v=1; b=H4sIAJw8mGoC/yWM0Q6CMBAEf4Xcs5dcqTGBXzE+YFnwDBZyrWhC+ HerPs5mZzZKMEWittrIsGrSORZwh4rCrYsjWPvCVEt9kkYcD/rm18gLYGx4zGs38dEPwTfSi3e OirkYyu1XPVNEpst/TM/rHSF/e7TvH90glU58AAAA X-Change-ID: 20260901-fix-wg-peer-removal-43fc390d0311 To: "Jason A. Donenfeld" , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni Cc: wireguard@lists.zx2c4.com, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@cloudflare.com, Chris J Arges X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=openssh-sha256; t=1788361892; l=5927; i=carges@cloudflare.com; h=from:subject:message-id; bh=gasmKhY5QdVetN3HDkPDfGtkZpKw5F4W6g75o0Q/i88=; b=U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgaxY1IIT5oTohBZJmhnVgJo2HsM7Sv 9I0LdJCgpeGX6gAAAAGcGF0YXR0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5AAAA QKpZG43U/VRAV+Prr2sW0jg3cNIEuCbnlX7R7/cMb4Ldr5itEqZVxNPbqMC463wRbQvSqRslVYr rxLj1VgRkpgE= X-Developer-Key: i=carges@cloudflare.com; a=openssh; fpr=SHA256:Cun99EBiH0EV7wvmfTBF9eDrld2NJx+aD4ScWZ45Q5M Calling peer_remove_after_dead() currently flushes device-wide packet crypto and handshake workqueues while holding RTNL. This is problematic as unrelated peers can continue adding work onto those queues blocking other tasks that want to take the RTNL lock. Instead, this patch proposes tracking pending crypto handoffs for each peer using a counter. After marking the peer dead, synchronize_net() prevents new submissions; wait for pending crypto workers to schedule TX work or RX NAPI. Then flush only the peer's transmit packet and handshake work. This scopes teardown synchronization to the removed peer and prevents unrelated peers from extending the RTNL hold time. Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") Signed-off-by: Chris J Arges --- drivers/net/wireguard/peer.c | 30 ++++++++++++++++-------------- drivers/net/wireguard/peer.h | 1 + drivers/net/wireguard/queueing.h | 6 ++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireguard/peer.c b/drivers/net/wireguard/peer.c index 1cb502a932e0..f7a9c437b5b8 100644 --- a/drivers/net/wireguard/peer.c +++ b/drivers/net/wireguard/peer.c @@ -14,6 +14,7 @@ #include #include #include +#include =20 static struct kmem_cache *peer_cache; static atomic64_t peer_counter =3D ATOMIC64_INIT(0); @@ -49,6 +50,8 @@ struct wg_peer *wg_peer_create(struct wg_device *wg, INIT_WORK(&peer->transmit_packet_work, wg_packet_tx_worker); wg_prev_queue_init(&peer->tx_queue); wg_prev_queue_init(&peer->rx_queue); + /* Keep this above zero until teardown prevents new packet handoffs. */ + atomic_set(&peer->packet_crypt_pending, 1); rwlock_init(&peer->endpoint_lock); kref_init(&peer->refcount); skb_queue_head_init(&peer->staged_packet_queue); @@ -105,28 +108,27 @@ static void peer_remove_after_dead(struct wg_peer *pe= er) */ wg_timers_stop(peer); =20 - /* The transition between packet encryption/decryption queues isn't - * guarded by is_dead, but each reference's life is strictly bounded by - * two generations: once for parallel crypto and once for serial - * ingestion, so we can simply flush twice, and be sure that we no - * longer have references inside these queues. + /* Lookup removal and is_dead prevent new packets from entering the + * parallel crypto queues after synchronize_net() waits for pre-existing + * submission paths. Drop the initial count and wait for existing packets + * to finish scheduling their serial TX work or RX NAPI processing. */ + atomic_dec(&peer->packet_crypt_pending); + wait_var_event(&peer->packet_crypt_pending, + !atomic_read_acquire(&peer->packet_crypt_pending)); + + flush_work(&peer->transmit_packet_work); =20 - /* a) For encrypt/decrypt. */ - flush_workqueue(peer->device->packet_crypt_wq); - /* b.1) For send (but not receive, since that's napi). */ - flush_workqueue(peer->device->packet_crypt_wq); - /* b.2.1) For receive (but not send, since that's wq). */ napi_disable(&peer->napi); - /* b.2.1) It's now safe to remove the napi struct, which must be done + /* It's now safe to remove the napi struct, which must be done * here from process context. */ netif_napi_del(&peer->napi); =20 - /* Ensure any workstructs we own (like transmit_handshake_work or - * clear_peer_work) no longer are in use. + /* clear_peer_work was flushed by wg_timers_stop(). Ensure the remaining + * peer-owned handshake work is no longer in use. */ - flush_workqueue(peer->device->handshake_send_wq); + flush_work(&peer->transmit_handshake_work); =20 /* After the above flushes, a peer might still be active in a few * different contexts: 1) from xmit(), before hitting is_dead and diff --git a/drivers/net/wireguard/peer.h b/drivers/net/wireguard/peer.h index 718fb42bdac7..64412c67f413 100644 --- a/drivers/net/wireguard/peer.h +++ b/drivers/net/wireguard/peer.h @@ -37,6 +37,7 @@ struct endpoint { struct wg_peer { struct wg_device *device; struct prev_queue tx_queue, rx_queue; + atomic_t packet_crypt_pending; struct sk_buff_head staged_packet_queue; int serial_work_cpu; bool is_dead; diff --git a/drivers/net/wireguard/queueing.h b/drivers/net/wireguard/queue= ing.h index 79b6d70de236..fdd34f0f15a6 100644 --- a/drivers/net/wireguard/queueing.h +++ b/drivers/net/wireguard/queueing.h @@ -11,6 +11,7 @@ #include #include #include +#include #include =20 struct wg_device; @@ -161,6 +162,7 @@ static inline int wg_queue_enqueue_per_device_and_peer( */ if (unlikely(!wg_prev_queue_enqueue(peer_queue, skb))) return -ENOSPC; + atomic_inc(&PACKET_PEER(skb)->packet_crypt_pending); =20 /* Then we queue it up in the device queue, which consumes the * packet as soon as it can. @@ -182,6 +184,8 @@ static inline void wg_queue_enqueue_per_peer_tx(struct = sk_buff *skb, enum packet atomic_set_release(&PACKET_CB(skb)->state, state); queue_work_on(wg_cpumask_choose_online(&peer->serial_work_cpu, peer->inte= rnal_id), peer->device->packet_crypt_wq, &peer->transmit_packet_work); + if (atomic_dec_and_test(&peer->packet_crypt_pending)) + wake_up_var(&peer->packet_crypt_pending); wg_peer_put(peer); } =20 @@ -194,6 +198,8 @@ static inline void wg_queue_enqueue_per_peer_rx(struct = sk_buff *skb, enum packet =20 atomic_set_release(&PACKET_CB(skb)->state, state); napi_schedule(&peer->napi); + if (atomic_dec_and_test(&peer->packet_crypt_pending)) + wake_up_var(&peer->packet_crypt_pending); wg_peer_put(peer); } =20 --- base-commit: 70f3995830d3f1e79faa14eb0605914f778feca9 change-id: 20260901-fix-wg-peer-removal-43fc390d0311 Best regards, -- =20 Chris J Arges