From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (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 80D3D37C90E for ; Thu, 2 Apr 2026 06:24:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111075; cv=none; b=Lj5TyotfeH/Y3BmfS8vDsQ6msp5dM+qxdkkrjM05O5rYZVk8O5eJze1Nheb0wxIskStLIsEAAav0Gl5645qZymGx07X7h6zAqVMu6mVqlMhsL7XiBcwo/c5egyHMPc8PfebKnLR9ti4Dgd5iCoZT/PO1USwLrVkOVOokkVI/zP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111075; c=relaxed/simple; bh=AUm3gnfSO4aj/PFs0jYHaVA8JR9/Dwk/ruvgFviQV9I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uegCtMtdNDIMYV51uzToYOsgki6XfuRjwdsy4IWfn6XTwopMZe2ssjsFaIL2jm6gHCuimIQq230kZ9YcymiEeYxh4J/gV8KtrsVgK6yHccdGDr5RZNtn/ffHIV2YyjA0cH6ZLowvYeXgSEOQJ3THr3mP5KdwBFe4xJaBMBMConA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=D0e8uqJn; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="D0e8uqJn" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ca0eb21c5fso3434394eec.1 for ; Wed, 01 Apr 2026 23:24:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111073; x=1775715873; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=hyEaQHBBytCzXQjOPVRmX4B/IWDKMcKi4U0qg84H+fc=; b=D0e8uqJn8w9Wje92msTi0OjZAKUfuAalCnfHVSt/9K3qxlUpep9Etwj/tg/lBfbKmx v0cfZzB2EvUVHurIbVwbUuKY+zM0f7jDXEmVOCb6yGxxEfzjqQp6aW0ODo8FzeJkH1Zm ypWX4CrocbxSO8aF9u1J7jCAFSMwSzpH7zZ3hWJ9MTpPfY1rcWiGLXcnxy+0/t4//7R8 0u3wnd0t/Wc+VcxEklZXLCHlOmVFo0RATWm82Uic2+T+IyOVOd1/LFpY39iBJvBeYfE8 FYUF11VhAp/jxRz1SUvgiiCP7ClKsC7xTjjUwxRWm1ekPS7CNq9Jd0XA/bHOuCHyIcl5 9dnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111073; x=1775715873; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=hyEaQHBBytCzXQjOPVRmX4B/IWDKMcKi4U0qg84H+fc=; b=XbwJ97N67y4iYKW3lEuGjn+SPgl3K1keS0DB24yipfqPqe66TsWeMcAi9qDW9YPBHE Uta34R+igjaa8lehF5Eatsko3rDYpvmUsRAmzhce78DeElB/K2nLkS44w9dHngMG1cEX BmZdQwEyfgmssdbjntecSEeEkeZ0YB8fSkuJHrRIJAvQXUb5hcKyYXE8PPGLZbcKwIj8 0l/vhrHnIj8xA0cyUrVX4ANAqAeRWZyzhJyX1XQbg+E9CEaROU+rOg8DOR0TUquxOLHW zjWXOw/NcID4747CrwukIvrcH+mK2evo6GbDoW61WvRACOSVARaHk3WPLLaWZfnd6oOp Oz0g== X-Forwarded-Encrypted: i=1; AJvYcCUYCRjd0O25qkHdQK6IDXWuF/Qt7YA9Yl65Q4DNgaX9JxcrxLQB3A8N0LplYqXg8sP5lq9XDItto9t8vf4=@vger.kernel.org X-Gm-Message-State: AOJu0Yx0tKQMHGdtZ+5bbc/YWP4APfB3GXuAPRGmp4eeadJYnjcQSAn2 pZzaX5+WjdRdYHDqb9WvVJ6KrmDK9iMMuII3RWYyE3NtcERAOG1ixp2WksfYdosyxZksRzzApKa 6nSboiqfVdA8dYBuD7up0Rg== X-Received: from dybmh14.prod.google.com ([2002:a05:7301:f0e:b0:2c7:8472:f365]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:cc12:b0:2c6:31f0:a018 with SMTP id 5a478bee46e88-2c9328a5ff7mr3758816eec.22.1775111072413; Wed, 01 Apr 2026 23:24:32 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:14 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=3691; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=AUm3gnfSO4aj/PFs0jYHaVA8JR9/Dwk/ruvgFviQV9I=; b=lhF3ubuJ24hAxbdWVytZQQKAUOCPC9M0Efj69ETG8G+BErrYD9sJOMc44qiZDI6SGbD6kcAu3 ivbcDXGcktnA/jxPgEASAyBHRWkKAP+TxcT95L2XBabA1wZUe4+q2bF X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-1-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 1/7] net: team: Annotate reads and writes for mixed lock accessed values From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The team_port's "index" and the team's "en_port_count" are read in the hot transmit path, but are only written to when holding the rtnl lock. Use READ_ONCE() for all lockless reads of these values, and use WRITE_ONCE() for all writes. Signed-off-by: Marc Harvey --- Changes in v3: - None Changes in v2: - None --- drivers/net/team/team_core.c | 11 ++++++----- drivers/net/team/team_mode_random.c | 2 +- include/linux/if_team.h | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index 566a5d102c23..becd066279a6 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -938,7 +938,8 @@ static void team_port_enable(struct team *team, { if (team_port_enabled(port)) return; - port->index =3D team->en_port_count++; + WRITE_ONCE(port->index, team->en_port_count); + WRITE_ONCE(team->en_port_count, team->en_port_count + 1); hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); team_adjust_ops(team); @@ -958,7 +959,7 @@ static void __reconstruct_port_hlist(struct team *team,= int rm_index) for (i =3D rm_index + 1; i < team->en_port_count; i++) { port =3D team_get_port_by_index(team, i); hlist_del_rcu(&port->hlist); - port->index--; + WRITE_ONCE(port->index, port->index - 1); hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); } @@ -973,8 +974,8 @@ static void team_port_disable(struct team *team, team->ops.port_disabled(team, port); hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); - port->index =3D -1; - team->en_port_count--; + WRITE_ONCE(port->index, -1); + WRITE_ONCE(team->en_port_count, team->en_port_count - 1); team_queue_override_port_del(team, port); team_adjust_ops(team); team_lower_state_changed(port); @@ -1245,7 +1246,7 @@ static int team_port_add(struct team *team, struct ne= t_device *port_dev, netif_addr_unlock_bh(dev); } =20 - port->index =3D -1; + WRITE_ONCE(port->index, -1); list_add_tail_rcu(&port->list, &team->port_list); team_port_enable(team, port); netdev_compute_master_upper_features(dev, true); diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mo= de_random.c index 53d0ce34b8ce..169a7bc865b2 100644 --- a/drivers/net/team/team_mode_random.c +++ b/drivers/net/team/team_mode_random.c @@ -16,7 +16,7 @@ static bool rnd_transmit(struct team *team, struct sk_buf= f *skb) struct team_port *port; int port_index; =20 - port_index =3D get_random_u32_below(team->en_port_count); + port_index =3D get_random_u32_below(READ_ONCE(team->en_port_count)); port =3D team_get_port_by_index_rcu(team, port_index); if (unlikely(!port)) goto drop; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index ccb5327de26d..06f4d7400c1e 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -77,7 +77,7 @@ static inline struct team_port *team_port_get_rcu(const s= truct net_device *dev) =20 static inline bool team_port_enabled(struct team_port *port) { - return port->index !=3D -1; + return READ_ONCE(port->index) !=3D -1; } =20 static inline bool team_port_txable(struct team_port *port) @@ -272,7 +272,7 @@ static inline struct team_port *team_get_port_by_index_= rcu(struct team *team, struct hlist_head *head =3D team_port_index_hash(team, port_index); =20 hlist_for_each_entry_rcu(port, head, hlist) - if (port->index =3D=3D port_index) + if (READ_ONCE(port->index) =3D=3D port_index) return port; return NULL; } --=20 2.53.0.1185.g05d4b7b318-goog From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (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 6DCC137CD2F for ; Thu, 2 Apr 2026 06:24:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111076; cv=none; b=CTFbvfFyvxQ5CrNHRZVmJKPZ7pRWcfQIu6qG4lwuhu4zSLleO9RMKuWHqIkDkSJXQkTK0/lr/W6Fqq6FT5o9WCOuN0OV8sWArUju1JZeBjnWT7dhVoE8nt1GHb/Kws4j8ovCkpbtpthBkGPrFo3pfyd69Pym6rrH6OqT7p01iY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111076; c=relaxed/simple; bh=hQyAafXwfvclv1praLESWjnpnN1FJSrHoMT32xvYXEs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=IXbLIQ3wswezVrOgByBUyiADNMk4lCys7r74V0h4TOF10qXB5shjHAOQrzpQ01sVQLrtLtUgLCUPR8blExC/0q2HjI8JZGAKROX4Ihvc6+C8EI0OK+OMWCO5+8EUXn7TUUso4ZKDcf3dZxT+rWGb5VIZNScBuy26776TaPP8axk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=NYLBrqhr; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="NYLBrqhr" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ba9a744f7dso873271eec.0 for ; Wed, 01 Apr 2026 23:24:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111075; x=1775715875; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Hn/zFHSnD52wLKA/jhKTIedusd6T1QUhMICqYQwpj2U=; b=NYLBrqhrpkKwp8pphD1JY1LXo3f6fM7IaB/bdTN/F823sJF/lzy/rwc87x3JgTzXuI kKabTrRn/OIZiATnuhXJfmwDm7KL6A7xaaKXJr5es+ma3ll4o70zeyiTHScFbJ2q95u9 lJ+SPwwoXgf3NMMzi86gzBw3mFa5WERnhCUvymeEIZcndjIWlz+buXghZfiyBqow/QI3 Un6C/PYb9iPySYWkgGXPc086G8sqJt3zJiZbkz8nx7H0YdLbZDRiT+Zyg7qgev0IPZqg ictIpwuQT621MJnQNXzUE8vQsfPFMyrk0UP31qmxpqSJYDW1hjXNV7doYaXkJtXcGdOG wbCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111075; x=1775715875; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Hn/zFHSnD52wLKA/jhKTIedusd6T1QUhMICqYQwpj2U=; b=GzK7pqnLeBWLCjfo/3CSxFegA4sXuES1QHvdZXBQGTv95KIBcSQiuD9HmVEzqopwgV sCj1amfAYv5Hi59pC8Z9NrFiuluQ3j69LtwfNifUuyomCq4ZV3XZk2iXvBXalQhohp4B NZG7bNMDb1J9GjiQt6as8A+YuQq+BL7NycDPRvJdYYl6br5t9WLx9hxcxrbVGoGCIbx6 hi4/uIGk8HuW/GgvQLRB9qx5NhLHNgIFwOeMV2zOMj2mIQOiDr/B+pQG6QF8wda0mxlS x7qmGyvLQ3oMXsZB/m6vAjqO8mpSie+JwrQ9+iEWkJgF/VBMEWObm/i7VtxJppIqTAzw ts2A== X-Forwarded-Encrypted: i=1; AJvYcCWKpTyWv2L6OM26ub8M19hF5915TafHRv/Rjfy0IG68CIr54LeD6N+Qhajbz6W3v6UlHgtuyzL/EO5TaQU=@vger.kernel.org X-Gm-Message-State: AOJu0YyEjX44DGDJ+wOcaPkfMdk4Jchh/N8wOsO5zIAQTeNwjPKtL+5/ fQm7/becEQRX94dncYflNyFN8Bus+xy6XPUnWGSIsAaMQGLlT8YQaIJkSJGYj/PxhngLqbsFmAt q81oLnl2A00Fm6J0fewRtUw== X-Received: from dlae2.prod.google.com ([2002:a05:701b:2302:b0:128:cff2:6560]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:171c:b0:12b:ee92:a60e with SMTP id a92af1059eb24-12bee92a6a0mr566583c88.8.1775111074277; Wed, 01 Apr 2026 23:24:34 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:15 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=1435; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=hQyAafXwfvclv1praLESWjnpnN1FJSrHoMT32xvYXEs=; b=uSJdDKf70qhFB82v+W3HK5hU8cu23biKM6MOjmheDadzn6X17KpL/XkNWi286FPSNwd48DGFW NEVSRyl3+xmDfMXh3C4AkN3M71o1QF8fZcDlPs+7Dz/8AHDWE6+1Xb7 X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-2-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 2/7] net: team: Remove unused team_mode_op, port_enabled From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This team_mode_op wasn't used by any of the team modes, so remove it. Signed-off-by: Marc Harvey --- Changes in v3: - None Changes in v2: - None --- drivers/net/team/team_core.c | 2 -- include/linux/if_team.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index becd066279a6..e54bd21bd068 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -944,8 +944,6 @@ static void team_port_enable(struct team *team, team_port_index_hash(team, port->index)); team_adjust_ops(team); team_queue_override_port_add(team, port); - if (team->ops.port_enabled) - team->ops.port_enabled(team, port); team_notify_peers(team); team_mcast_rejoin(team); team_lower_state_changed(port); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 06f4d7400c1e..a761f5282bcf 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -121,7 +121,6 @@ struct team_mode_ops { int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); void (*port_change_dev_addr)(struct team *team, struct team_port *port); - void (*port_enabled)(struct team *team, struct team_port *port); void (*port_disabled)(struct team *team, struct team_port *port); }; =20 --=20 2.53.0.1185.g05d4b7b318-goog From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (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 EFE4D3806AC for ; Thu, 2 Apr 2026 06:24:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111079; cv=none; b=hc9Yhw6RiRGdkGImbzOEH+6+gANsLH67pPYgGI2ZrqzXmOqlIryEwN+pgah0s20XE8TYCaylM4tOTKtOBdWcZ/kD+a5wlkQqjkYcA3Gof8gBgi6y17kn013cl5ao3Hr9/A8zIdpuMLAqLpUQEOfz9JTvCxCTFUzOb7V+nc1HaCA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111079; c=relaxed/simple; bh=HQjOdpWVp0pioXC2IZRDM9wty7V0fQbV6ok41MLNUd0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=nAlKeN81wi+h51S7lzOnKsB3H8+LA2BlZnb0v2gV+TAHVR3WGt2Tt7mip6yoaKjUnY863rpWCh6e0jTdUZJrCECwEpn5a2Jfl388Uw7mZ7colcKvkiMVP8xyEI/G3+EhmSi6WCCh93vGE5dG4qovW/uqLUfYq+BMFp6BjbwnTh8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=fHslOeID; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="fHslOeID" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bda35eab74so521143eec.0 for ; Wed, 01 Apr 2026 23:24:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111076; x=1775715876; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=CgsfKORANRJz+p1LPldcLkTl0RXQtetjHTccrdSgIh4=; b=fHslOeID+NKCsI8e1KfyRYhIHOty+ic0GZx/UUlKtSky63kOOmDyj+5QuPA+uEfU8C uEsaFP8MhHA4ETFYj59Ac47cEaAPNpgy24npIQp7gLfAlDWYoeug+V2LonH0IH8K5O3Z g2XU7ZJWNktiY5vxePzZ9Euds5B3Pw4z6ORWtzYQLTyMgJJ2iUzSF7vU5kzbGfyHFUiP Yicl8Lyw484kvadpWJP35aHa7OxGpu+BTu4F9XE94J86e2La8p6KteyMhzqPr8g2Rp6B 23trA1+ayUzx9uiUmSxcfoDUwYK3O23/0Cv6fncES9yRzrV1mE5/e3NL0rBluraqUtnm Fkiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111076; x=1775715876; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=CgsfKORANRJz+p1LPldcLkTl0RXQtetjHTccrdSgIh4=; b=BOMW7tWGEm9RnUzE4Rteg5LTcKUPjA03ImadC6/F1bs414G7pCzK6sxuK5nrxK2vCo OnDyM3o6ee3lCZKoGc2L7iXKh//rBfRLbxjsgoBwoVb8YgFK3d4+MrPZtBtpPZTh8fK5 dmhLn/+QoNRe+Rh/Bp4wecCAJnnW58XdKV43nybNuGKMYg3oML4ON2W/Fa43Aj669swz wYcnEbuUo60gOoGjL1DDKM+X5EeYZ0/4Tqu7jPRFgZ4+tMwyUgDWxg36pjmUGy3j/SX3 a9+JLMU70gYH7ZSuk1VlV42UtSE8lLUIRZg5DK1oW1gE2e+6Pf+Og4UfRtZobOCR+nmY h83Q== X-Forwarded-Encrypted: i=1; AJvYcCWo8gYtXmPXJnioqO+z8Bt/eRylTZ7RLEo2BJolkyUG6+o8JQX05QTbhr97h1/8348UmlWR4R4PNODPQjk=@vger.kernel.org X-Gm-Message-State: AOJu0YzC0Tz14at8HtUAWzwiwSPWnqw5h62WtHloXcQ0/Lgtm68zW8/W uad5roeTZN+qnO6Ixlq1OstYOPfnRCIrY42360g3nMgeXmnYUpFsYA1Fl/oTgVey3wx/Be9u2S3 D1qr9ouVi9jnk5sm2CU99Ew== X-Received: from dybmd43.prod.google.com ([2002:a05:7301:182b:b0:2ca:4c73:9998]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:693c:2c14:b0:2be:ca4:e114 with SMTP id 5a478bee46e88-2cad6fca9dfmr478824eec.9.1775111075992; Wed, 01 Apr 2026 23:24:35 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:16 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=2559; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=HQjOdpWVp0pioXC2IZRDM9wty7V0fQbV6ok41MLNUd0=; b=1EaJAA4bTmSBIRbv9Jbm0RlvkzzUF+Xp1V4ol+DxjjYozQyx+p/QF7F3rAJ0nYKWhUewTiuOU GFyrIj6olI7B4rq/DWW6kTIixrdSjtWCm1i929UMQ+0MwoyJZYInHn2 X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-3-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 3/7] net: team: Rename port_disabled team mode op to port_tx_disabled From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This team mode op is only used by the load balance mode, and it only uses it in the tx path. Signed-off-by: Marc Harvey --- Changes in v3: - None Changes in v2: - None --- drivers/net/team/team_core.c | 4 ++-- drivers/net/team/team_mode_loadbalance.c | 4 ++-- include/linux/if_team.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index e54bd21bd068..2ce31999c99f 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -968,8 +968,8 @@ static void team_port_disable(struct team *team, { if (!team_port_enabled(port)) return; - if (team->ops.port_disabled) - team->ops.port_disabled(team, port); + if (team->ops.port_tx_disabled) + team->ops.port_tx_disabled(team, port); hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); WRITE_ONCE(port->index, -1); diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/te= am_mode_loadbalance.c index 684954c2a8de..840f409d250b 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -655,7 +655,7 @@ static void lb_port_leave(struct team *team, struct tea= m_port *port) free_percpu(lb_port_priv->pcpu_stats); } =20 -static void lb_port_disabled(struct team *team, struct team_port *port) +static void lb_port_tx_disabled(struct team *team, struct team_port *port) { lb_tx_hash_to_port_mapping_null_port(team, port); } @@ -665,7 +665,7 @@ static const struct team_mode_ops lb_mode_ops =3D { .exit =3D lb_exit, .port_enter =3D lb_port_enter, .port_leave =3D lb_port_leave, - .port_disabled =3D lb_port_disabled, + .port_tx_disabled =3D lb_port_tx_disabled, .receive =3D lb_receive, .transmit =3D lb_transmit, }; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index a761f5282bcf..740cb3100dfc 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -121,7 +121,7 @@ struct team_mode_ops { int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); void (*port_change_dev_addr)(struct team *team, struct team_port *port); - void (*port_disabled)(struct team *team, struct team_port *port); + void (*port_tx_disabled)(struct team *team, struct team_port *port); }; =20 extern int team_modeop_port_enter(struct team *team, struct team_port *por= t); --=20 2.53.0.1185.g05d4b7b318-goog From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (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 AA978382289 for ; Thu, 2 Apr 2026 06:24:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111080; cv=none; b=bESet9EH4baPFEfgJr0GdixOc0dTa3PDPPXmXJyykLygWUfWl6sYmCIfyNwNopqGPU9p1lOciAmISFEO3jTxnO0CC2Xz9IMWVq2hQcVqsHjvUtPlZyQZZbZQ0kcZ/WurgsD0/w8Ih34dZYRjA3Zs0bmbgczmvEIDVDkyIAJ0or0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111080; c=relaxed/simple; bh=Yor0H1t3BMosm24tgScW+yC9g8CagotZRMbvNtPSw5w=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AJO+vdKu/kfRdGunCGVHI4XwDLBsz3WWjds5fTjzS8RcVuhsXuhp2nva//kuAn3rZhIlLAujPwAoaUlmeejOiqCIUsHTwilJoae6/mxImgdXG/1DwE9j1EIfp7D8Hr/5x76ja14xiQJThpgGVvqB2hbHDH9GyrK5WUqyk46lOVw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=oqNozyOy; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="oqNozyOy" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12737f276a2so855459c88.1 for ; Wed, 01 Apr 2026 23:24:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111078; x=1775715878; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=5XD/lLR+4XR1M02kFZdqEgCMcSpn/ZA1E7ilaV2GtSA=; b=oqNozyOyPp//wwdcq0244trUSu/uHGHlxRzDykyK/3GNS1rN3rgSrdjKpKqgXnFp7t tUWYHYtlHr6ivv0Hwu7nNe8aAU3KhMi3ln91huiPYFjbZ6rANmoxoIiQag8eq2bHJ4i/ EQJ/9chnPf/FgcdHiZBaCItyOrVuAxU2ZZzXYcFn/DD1GRA1cPS2E/Wo4G5lyT9XQL4z 5s0SjhUAYEriPYRedZNrcMr6RUzln1N9YVvLEnMbwbepXTMaa58R2DfoM+t8FVrupTmk VwTS/gy7g58yCh8zNrCGLxGF2hMC/zwjEk9Pc4VQ28KUXWyi1aPjatPKzgngv5TZAucd WRjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111078; x=1775715878; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=5XD/lLR+4XR1M02kFZdqEgCMcSpn/ZA1E7ilaV2GtSA=; b=l9ca9u0D3cbSqYleEaqkOnig+kMihUolTkdmSl/oyv8TYGxkB9B3enMqRV2V1OslTR Zd+bCncR4ll+ytykff0KyunhUHZtyNiU9YvQ6gOicGoIUqY7U1pwZ48vknEsKMnK4lLm phPjHLWH+55z0lWYOzhToLXFr/palu//2d5dNBfILyNG31evdQQbZz36wmtb35wnC/pl adfeQ2oqPgc5zh1aY9bwgjagbcygxhWPTyc0jjihs8InCgNAXrg3UPSVOFPeu/iVV45A 5zPcaUZo0BqqJYvPBJBShxEJ0ffQ7n2HFP3awwI6IaD+P/UVZEWQVVUrUDVrX1wkZ0C7 oakg== X-Forwarded-Encrypted: i=1; AJvYcCXjEeHpLErE1LXb3lNXHqO1rx1vPaElOiTY3DomhuqiTAQtLM9tmyuoFvCyy5JrF/+vg9rLp2S+YIenUe4=@vger.kernel.org X-Gm-Message-State: AOJu0YwteHkdifWnGC5fW01YKN5YLCSUoKgm3Qv1FXbOYRj19+84E72g 17y5E8hppoENhVJx7ClNEae57svtp2nLYUEdTSeMvrX8YnfOpv61/sMtqNKVwaN+pNBHAXCIQZ2 PH0BMy3u41YiTn1n2Dc9RQg== X-Received: from dleb5-n2.prod.google.com ([2002:a05:701b:4245:20b0:12a:ba56:cfe4]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:226:b0:12a:6c4b:9d1b with SMTP id a92af1059eb24-12be6592075mr3217691c88.31.1775111077497; Wed, 01 Apr 2026 23:24:37 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:17 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=10785; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=Yor0H1t3BMosm24tgScW+yC9g8CagotZRMbvNtPSw5w=; b=ZbCegjUnEBQNmGekCrTtaWvv5wRRpOiRSWF33x/sCwyC28I/jfpBruCXfKhlhXQJff53+MLdr i0zrbc0or47C6AmiqSv/H0vtJG3Li25u3aLWMMugFxkK/fPO8fI1lFm X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-4-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 4/7] selftests: net: Add tests for failover of team-aggregated ports From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable There are currently no kernel tests that verify the effect of setting the enabled team driver option. In a followup patch, there will be changes to this option, so it will be important to make sure it still behaves as it does now. The test verifies that tcp continues to work across two different team devices in separate network namespaces, even when member links are manually disabled. Signed-off-by: Marc Harvey --- Changes in v3: - None Changes in v2: - Fix shellcheck failures. - Remove dependency on net forwarding lib and pipe viewer tools. - Use iperf3 for tcp instead of netcat. - Link to v1: https://lore.kernel.org/all/20260331053353.2504254-5-marcharv= ey@google.com/ --- tools/testing/selftests/drivers/net/team/Makefile | 2 + tools/testing/selftests/drivers/net/team/config | 4 + .../testing/selftests/drivers/net/team/team_lib.sh | 115 ++++++++++++++++ .../drivers/net/team/transmit_failover.sh | 151 +++++++++++++++++= ++++ 4 files changed, 272 insertions(+) diff --git a/tools/testing/selftests/drivers/net/team/Makefile b/tools/test= ing/selftests/drivers/net/team/Makefile index 02d6f51d5a06..777da2e0429e 100644 --- a/tools/testing/selftests/drivers/net/team/Makefile +++ b/tools/testing/selftests/drivers/net/team/Makefile @@ -7,9 +7,11 @@ TEST_PROGS :=3D \ options.sh \ propagation.sh \ refleak.sh \ + transmit_failover.sh \ # end of TEST_PROGS =20 TEST_INCLUDES :=3D \ + team_lib.sh \ ../bonding/lag_lib.sh \ ../../../net/forwarding/lib.sh \ ../../../net/in_netns.sh \ diff --git a/tools/testing/selftests/drivers/net/team/config b/tools/testin= g/selftests/drivers/net/team/config index 5d36a22ef080..8f04ae419c53 100644 --- a/tools/testing/selftests/drivers/net/team/config +++ b/tools/testing/selftests/drivers/net/team/config @@ -6,4 +6,8 @@ CONFIG_NETDEVSIM=3Dm CONFIG_NET_IPGRE=3Dy CONFIG_NET_TEAM=3Dy CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=3Dy +CONFIG_NET_TEAM_MODE_BROADCAST=3Dy CONFIG_NET_TEAM_MODE_LOADBALANCE=3Dy +CONFIG_NET_TEAM_MODE_RANDOM=3Dy +CONFIG_NET_TEAM_MODE_ROUNDROBIN=3Dy +CONFIG_VETH=3Dy diff --git a/tools/testing/selftests/drivers/net/team/team_lib.sh b/tools/t= esting/selftests/drivers/net/team/team_lib.sh new file mode 100644 index 000000000000..fce421869381 --- /dev/null +++ b/tools/testing/selftests/drivers/net/team/team_lib.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Create a team interface inside of a given network namespace with a given +# mode, members, and IP address. +# Arguments: +# namespace - Network namespace to put the team interface into. +# team - The name of the team interface to setup. +# mode - The team mode of the interface. +# ip_address - The IP address to assign to the team interface. +# prefix_length - The prefix length for the IP address subnet. +# $@ - members - The member interfaces of the aggregation. +setup_team() +{ + local namespace=3D$1 + local team=3D$2 + local mode=3D$3 + local ip_address=3D$4 + local prefix_length=3D$5 + shift 5 + local members=3D("$@") + + # Prerequisite: team must have no members + for member in "${members[@]}"; do + ip -n "${namespace}" link set "${member}" nomaster + done + + # Prerequisite: team must have no address in order to set it + # shellcheck disable=3DSC2086 + ip -n "${namespace}" addr del "${ip_address}/${prefix_length}" \ + ${NODAD} dev "${team}" + + echo "Setting team in ${namespace} to mode ${mode}" + + if ! ip -n "${namespace}" link set "${team}" down; then + echo "Failed to bring team device down" + return 1 + fi + if ! ip netns exec "${namespace}" teamnl "${team}" setoption mode \ + "${mode}"; then + echo "Failed to set ${team} mode to '${mode}'" + return 1 + fi + + # Aggregate the members into teams. + for member in "${members[@]}"; do + ip -n "${namespace}" link set "${member}" master "${team}" + done + + # Bring team devices up and give them addresses. + if ! ip -n "${namespace}" link set "${team}" up; then + echo "Failed to set ${team} up" + return 1 + fi + + # shellcheck disable=3DSC2086 + if ! ip -n "${namespace}" addr add "${ip_address}/${prefix_length}" \ + ${NODAD} dev "${team}"; then + echo "Failed to give ${team} IP address in ${namespace}" + return 1 + fi +} + +# This is global used to keep track of the sender's iperf3 process, so tha= t it +# can be terminated. +declare sender_pid + +# Start sending and receiving TCP traffic with iperf3. +# Globals: +# sender_pid - The process ID of the iperf3 sender process. Used to kill = it +# later. +start_listening_and_sending() +{ + ip netns exec "${NS2}" iperf3 -s -p 1234 --logfile /dev/null & + # Wait for server to become reachable before starting client. + slowwait 5 ip netns exec "${NS1}" iperf3 -c "${NS2_IP}" -p 1234 -t 1 \ + --logfile /dev/null + ip netns exec "${NS1}" iperf3 -c "${NS2_IP}" -p 1234 -b 1M -l 1K -t 0 \ + --logfile /dev/null & + sender_pid=3D$! +} + +# Stop sending TCP traffic with iperf3. +# Globals: +# sender_pid - The process IF of the iperf3 sender process. +stop_sending_and_listening() +{ + kill "${sender_pid}" && wait "${sender_pid}" 2>/dev/null || true +} + +# Read RX packet counters to determine if interface received traffic. +# Arguments: +# interface - The name of the interface to count packets for. +# namespace - The name of the namespace that the interface is in. +did_interface_receive() +{ + local interface=3D"$1" + local namespace=3D"$2" + local count1 count2 diff + + count1=3D$(ip netns exec "${namespace}" cat \ + "/sys/class/net/${interface}/statistics/rx_packets") + sleep 1 + count2=3D$(ip netns exec "${namespace}" cat \ + "/sys/class/net/${interface}/statistics/rx_packets") + diff=3D$((count2 - count1)) + + echo "Packet count for ${interface} was ${diff}" + + if [[ "${diff}" -gt 0 ]]; then + true + else + false + fi +} diff --git a/tools/testing/selftests/drivers/net/team/transmit_failover.sh = b/tools/testing/selftests/drivers/net/team/transmit_failover.sh new file mode 100755 index 000000000000..212d650caf66 --- /dev/null +++ b/tools/testing/selftests/drivers/net/team/transmit_failover.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# These tests verify the basic failover capability of the team driver via = the +# `enabled` team driver option across different team driver modes. This do= es not +# rely on teamd, and instead just uses teamnl to set the `enabled` option +# directly. +# +# Topology: +# +# +-------------------------+ NS1 +# | test_team1 | +# | + | +# | eth0 | eth1 | +# | +---+---+ | +# | | | | +# +-------------------------+ +# | | +# +-------------------------+ NS2 +# | | | | +# | +-------+ | +# | eth0 | eth1 | +# | + | +# | test_team2 | +# +-------------------------+ + +export ALL_TESTS=3D"team_test_failover" + +test_dir=3D"$(dirname "$0")" +# shellcheck disable=3DSC1091 +source "${test_dir}/../../../net/lib.sh" +# shellcheck disable=3DSC1091 +source "${test_dir}/team_lib.sh" + +NS1=3D"" +NS2=3D"" +export NODAD=3D"nodad" +PREFIX_LENGTH=3D"64" +NS1_IP=3D"fd00::1" +NS2_IP=3D"fd00::2" +NS1_IP4=3D"192.168.0.1" +NS2_IP4=3D"192.168.0.2" +MEMBERS=3D("eth0" "eth1") + +while getopts "4" opt; do + case $opt in + 4) + echo "IPv4 mode selected." + export NODAD=3D + PREFIX_LENGTH=3D"24" + NS1_IP=3D"${NS1_IP4}" + NS2_IP=3D"${NS2_IP4}" + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +# Create the network namespaces, veth pair, and team devices in the specif= ied +# mode. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +# Arguments: +# mode - The team driver mode to use for the team devices. +environment_create() +{ + trap cleanup_all_ns EXIT + setup_ns ns1 ns2 + NS1=3D"${NS_LIST[0]}" + NS2=3D"${NS_LIST[1]}" + + # Create the interfaces. + ip -n "${NS1}" link add eth0 type veth peer name eth0 netns "${NS2}" + ip -n "${NS1}" link add eth1 type veth peer name eth1 netns "${NS2}" + ip -n "${NS1}" link add test_team1 type team + ip -n "${NS2}" link add test_team2 type team + + # Set up the receiving network namespace's team interface. + setup_team "${NS2}" test_team2 roundrobin "${NS2_IP}" \ + "${PREFIX_LENGTH}" "${MEMBERS[@]}" +} + + +# Check that failover works for a specific team driver mode. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +# Arguments: +# mode - The mode to set the team interfaces to. +team_test_mode_failover() +{ + local mode=3D"$1" + export RET=3D0 + + # Set up the sender team with the correct mode. + setup_team "${NS1}" test_team1 "${mode}" "${NS1_IP}" \ + "${PREFIX_LENGTH}" "${MEMBERS[@]}" + check_err $? "Failed to set up sender team" + + start_listening_and_sending + + ### Scenario 1: All interfaces initially enabled. + did_interface_receive eth0 "${NS2}" + check_err $? "eth0 not transmitting when both links enabled" + did_interface_receive eth1 "${NS2}" + check_err $? "eth1 not transmitting when both links enabled" + + ### Scenario 2: One tx-side interface disabled. + ip netns exec "${NS1}" teamnl test_team1 setoption enabled false \ + --port=3Deth1 + slowwait 2 bash -c "ip netns exec ${NS1} teamnl test_team1 getoption \ + enabled --port=3Deth1 | grep -q false" + + did_interface_receive eth0 "${NS2}" + check_err $? "eth0 not transmitting when enabled" + did_interface_receive eth1 "${NS2}" + check_fail $? "eth1 IS transmitting when disabled" + + ### Scenario 3: The interface is re-enabled. + ip netns exec "${NS1}" teamnl test_team1 setoption enabled true \ + --port=3Deth1 + slowwait 2 bash -c "ip netns exec ${NS1} teamnl test_team1 getoption \ + enabled --port=3Deth1 | grep -q true" + + did_interface_receive eth0 "${NS2}" + check_err $? "eth0 not transmitting when both links enabled" + did_interface_receive eth1 "${NS2}" + check_err $? "eth1 not transmitting when both links enabled" + + log_test "Failover of '${mode}' test" + + # Clean up + stop_sending_and_listening +} + +team_test_failover() +{ + team_test_mode_failover broadcast + team_test_mode_failover roundrobin + team_test_mode_failover random + # Don't test `activebackup` or `loadbalance` modes, since they are too + # complicated for just setting `enabled` to work. They use more than + # the `enabled` option for transmit. +} + +require_command teamnl +require_command iperf3 +environment_create +tests_run +exit "${EXIT_STATUS}" --=20 2.53.0.1185.g05d4b7b318-goog From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (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 4CA1537FF76 for ; Thu, 2 Apr 2026 06:24:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111082; cv=none; b=PnCwpc+ZQsVQhwJRjg2Q1nwMArltJ+zCyzs/4uwkdXds/Ri+6tAT1AJtn23hPAUnWa9crpjxh0qQjjoeBCop0UA0SKO3laTTgEjPy9/r5MWdnWlhjHapAbW7K8BpQ59GTZ6GmpEIp5A6ccrw1UPOO2LCtsaWSmQcG4oP+pHUiA4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111082; c=relaxed/simple; bh=ssH/9/hjYB+fBqiWIN6t+y1AHb7DVBmvSY7H4JUUf5c=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=N5d5ifRtcNHkaPY66uxeQ4IVrFnQXce8w11Z1ive1V3xsXV5C7anZQ5VOnhKlQF5uf99Gq93pmbz3ePo2oO+XnDYr/eIgoZNQxy0PU7NveJKTHz26UhgFfax1eIcsVWgABDSo9T6ZgwxhXyntFf8ps1mmX26VrI/NZIJKuVFZag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=XRYvNIFq; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="XRYvNIFq" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-128d59030d0so2486820c88.0 for ; Wed, 01 Apr 2026 23:24:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111079; x=1775715879; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=v9oFgXGgQZPRqw6RYXyTCh6MIwqTVydlJPeU6ec/jKU=; b=XRYvNIFqwEtXzx39W7H65Jy55M3Y0qycnzWkMg5YukW/H7Qus6AyruRoivy4h79N06 YJafiTaWFeUnLJUuDRsZJZlk9RKfFjyGDD55BAjpjeYevlmrUpEj/P+f805F5yJbHDvo e/bGu+1CBuYv7cz9cuhUu4DBQ8bUGQ7nTfwLVtOUvUykFVXq4UaW6+Dk0A41feuWVN2c Mgqtpc0Qtwg5Gp/3cADKp5/uqdXJ/X5ByEf/wSEHetms+5RiVkQAnYGHUmLVQfDR0/Ny lV55xPkPAwOsscb4I6Q27g5uZlRphlsKIs4reSNZQ5F1c420BqL9rLdKwDH62+lNzuC/ c6Pw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111079; x=1775715879; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=v9oFgXGgQZPRqw6RYXyTCh6MIwqTVydlJPeU6ec/jKU=; b=dvIwPLLOjGhlzVB0GfOmUGOJfc4RZS0G86v762ZaCMuH+OHxONykm+Fs+Oy54fPQgp 32uhlkI8gc5/ik1mIybUyMdJ97bxy8BfEahE9Zm1NUHuc9+C1Pit76zoSuBr5yJ7iTUs Ljlq/F5+4AJydhY+RmYeEfSyfH6fI5U//oD7ZUGrsrstcLE/dRnrG5+0C8qn/sDka68U 0lzq+yiInRqSLgcJ+13AHZ7lVJt2Y71McPxL7oqUojwE6uQ64gYKSmDRdAaPZG4qEPNu PbAqh1dQV8f19cAJkWDj9K/ogWks0d2OcVtiCCHvMqgckq6y2f5Bjlb+ia+6nF3IPfTd UY+Q== X-Forwarded-Encrypted: i=1; AJvYcCUIgFvcq9x90A/Ow8ndMiJNi7XMYVPx+KlFSTCK9QgtJ3VE69z+5OqXFuIVzCFrr9qbz4MhH/yBHFbziz0=@vger.kernel.org X-Gm-Message-State: AOJu0YyZ2L/3BvENbjVQOpIS1ywqociEeS+SSzgxHRWdcw6i5345TxtG mwlwI2Io2TtFs8ylSG4fJ4mpLOqoZgJorpEWmhRlsscib0w2eiUentTFd+Z6WXjfLlbrfAZYirH SPdGK1C0dTc1pT2AzxeNf3g== X-Received: from dled9-n2.prod.google.com ([2002:a05:701b:42c9:20b0:12a:c265:1a7c]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:e1c:b0:123:2c98:f6af with SMTP id a92af1059eb24-12be644a2bemr3876893c88.14.1775111079188; Wed, 01 Apr 2026 23:24:39 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:18 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=8354; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=ssH/9/hjYB+fBqiWIN6t+y1AHb7DVBmvSY7H4JUUf5c=; b=J5ri/HEBXKzoQqu3xsVVVdBzpo+VHRSCCxE7WezYbk0rpAikZC4AG7yY62JwTeuhLAD+X4cDx sL3YUKTN0w4CXgbQnho5NJMETO0575L5OadaOwubctGrJggJFe8YDY7 X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-5-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 5/7] selftests: net: Add test for enablement of ports with teamd From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable There are no tests that verify enablement and disablement of team driver ports with teamd. This should work even with changes to the enablement option, so it is important to test. This test sets up an active-backup network configuration across two network namespaces, and tries to send traffic while changing which link is the active one. Signed-off-by: Marc Harvey --- Changed in v3: - Make test cleanup kill teamd instead of terminate. - Link to v2: https://lore.kernel.org/netdev/20260401-teaming-driver-intern= al-v2-5-f80c1291727b@google.com/ Changes in v2: - Fix shellcheck failures. - Remove dependency on net forwarding lib and pipe viewer tools. - Use iperf3 for tcp instead of netcat. - Link to v1: https://lore.kernel.org/all/20260331053353.2504254-6-marcharv= ey@google.com/ --- tools/testing/selftests/drivers/net/team/Makefile | 1 + .../drivers/net/team/teamd_activebackup.sh | 217 +++++++++++++++++= ++++ tools/testing/selftests/net/lib.sh | 13 ++ 3 files changed, 231 insertions(+) diff --git a/tools/testing/selftests/drivers/net/team/Makefile b/tools/test= ing/selftests/drivers/net/team/Makefile index 777da2e0429e..dab922d7f83d 100644 --- a/tools/testing/selftests/drivers/net/team/Makefile +++ b/tools/testing/selftests/drivers/net/team/Makefile @@ -7,6 +7,7 @@ TEST_PROGS :=3D \ options.sh \ propagation.sh \ refleak.sh \ + teamd_activebackup.sh \ transmit_failover.sh \ # end of TEST_PROGS =20 diff --git a/tools/testing/selftests/drivers/net/team/teamd_activebackup.sh= b/tools/testing/selftests/drivers/net/team/teamd_activebackup.sh new file mode 100755 index 000000000000..aa882e1ee3d8 --- /dev/null +++ b/tools/testing/selftests/drivers/net/team/teamd_activebackup.sh @@ -0,0 +1,217 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# These tests verify that teamd is able to enable and disable ports via the +# active backup runner. +# +# Topology: +# +# +-------------------------+ NS1 +# | test_team1 | +# | + | +# | eth0 | eth1 | +# | +---+---+ | +# | | | | +# +-------------------------+ +# | | +# +-------------------------+ NS2 +# | | | | +# | +-------+ | +# | eth0 | eth1 | +# | + | +# | test_team2 | +# +-------------------------+ + +export ALL_TESTS=3D"teamd_test_active_backup" + +test_dir=3D"$(dirname "$0")" +# shellcheck disable=3DSC1091 +source "${test_dir}/../../../net/lib.sh" +# shellcheck disable=3DSC1091 +source "${test_dir}/team_lib.sh" + +NS1=3D"" +NS2=3D"" +export NODAD=3D"nodad" +PREFIX_LENGTH=3D"64" +NS1_IP=3D"fd00::1" +NS2_IP=3D"fd00::2" +NS1_IP4=3D"192.168.0.1" +NS2_IP4=3D"192.168.0.2" +NS1_TEAMD_CONF=3D"" +NS2_TEAMD_CONF=3D"" +NS1_TEAMD_PID=3D"" +NS2_TEAMD_PID=3D"" + +while getopts "4" opt; do + case $opt in + 4) + echo "IPv4 mode selected." + export NODAD=3D + PREFIX_LENGTH=3D"24" + NS1_IP=3D"${NS1_IP4}" + NS2_IP=3D"${NS2_IP4}" + ;; + \?) + echo "Invalid option: -${OPTARG}" >&2 + exit 1 + ;; + esac +done + +teamd_config_create() +{ + local runner=3D$1 + local dev=3D$2 + local conf + + conf=3D$(mktemp) + + cat > "${conf}" <<-EOF + { + "device": "${dev}", + "runner": {"name": "${runner}"}, + "ports": { + "eth0": {}, + "eth1": {} + } + } + EOF + echo "${conf}" +} + +# Create the network namespaces, veth pair, and team devices in the specif= ied +# runner. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +# Arguments: +# runner - The Teamd runner to use for the Team devices. +environment_create() +{ + local runner=3D$1 + + echo "Setting up two-link aggregation for runner ${runner}" + trap environment_destroy EXIT + + setup_ns ns1 ns2 + NS1=3D"${NS_LIST[0]}" + NS2=3D"${NS_LIST[1]}" + + for link in $(seq 0 1); do + ip -n "${NS1}" link add "eth${link}" type veth peer name \ + "eth${link}" netns "${NS2}" + check_err $? "Failed to create veth pair" + done + + NS1_TEAMD_CONF=3D$(teamd_config_create "${runner}" "test_team1") + NS2_TEAMD_CONF=3D$(teamd_config_create "${runner}" "test_team2") + echo "Conf files are ${NS1_TEAMD_CONF} and ${NS2_TEAMD_CONF}" + + ip netns exec "${NS1}" teamd -d -f "${NS1_TEAMD_CONF}" + check_err $? "Failed to create team device in ${NS1}" + NS1_TEAMD_PID=3D$(pgrep -f "teamd -d -f ${NS1_TEAMD_CONF}") + + ip netns exec "${NS2}" teamd -d -f "${NS2_TEAMD_CONF}" + check_err $? "Failed to create team device in ${NS2}" + NS2_TEAMD_PID=3D$(pgrep -f "teamd -d -f ${NS2_TEAMD_CONF}") + + echo "Created team devices" + + rm "${NS1_TEAMD_CONF}" + rm "${NS2_TEAMD_CONF}" + NS1_TEAMD_CONF=3D"" + NS2_TEAMD_CONF=3D"" + + for link in $(seq 0 1); do + in_all_ns "ip link set eth${link} up" + check_err $? "Failed to set eth${link} up" + done + + ip -n "${NS1}" link set test_team1 up + check_err $? "Failed to set test_team1 up in ${NS1}" + ip -n "${NS2}" link set test_team2 up + check_err $? "Failed to set test_team2 up in ${NS2}" + + ip -n "${NS1}" addr add "${NS1_IP}/${PREFIX_LENGTH}" "${NODAD}" dev \ + test_team1 + check_err $? "Failed to add address to team device in ${NS1}" + ip -n "${NS2}" addr add "${NS2_IP}/${PREFIX_LENGTH}" "${NODAD}" dev \ + test_team2 + check_err $? "Failed to add address to team device in ${NS2}" + + slowwait 2 timeout 0.5 ip netns exec "${NS1}" ping -W 1 -c 1 "${NS2_IP}" +} + +# Tear down the environment: kill teamd and delete network namespaces. +environment_destroy() +{ + echo "Tearing down two-link aggregation" + if [ -n "${NS1_TEAMD_PID}" ]; then + kill -9 "${NS1_TEAMD_PID}" 2>/dev/null + fi + if [ -n "${NS2_TEAMD_PID}" ]; then + kill -9 "${NS2_TEAMD_PID}" 2>/dev/null + fi + cleanup_all_ns +} + +# Change the active port for an active-backup mode team. +# Arguments: +# namespace - The network namespace that the team is in. +# team - The name of the team. +# active_port - The port to make active. +set_active_port() +{ + local namespace=3D$1 + local team=3D$2 + local active_port=3D$3 + + ip netns exec "${namespace}" teamdctl "${team}" state item set \ + runner.active_port "${active_port}" + slowwait 2 bash -c "ip netns exec ${namespace} teamdctl ${team} state \ + item get runner.active_port | grep -q ${active_port}" +} + +# Test that active backup runner can change active ports. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +teamd_test_active_backup() +{ + export RET=3D0 + + start_listening_and_sending + + ### Scenario 1: Don't manually set active port, just make sure team + # works. + did_interface_receive test_team2 "${NS2}" + check_err $? "Traffic did not reach team interface in NS2." + + ### Scenario 2: Choose active port. + set_active_port "${NS1}" test_team1 eth1 + set_active_port "${NS2}" test_team2 eth1 + + did_interface_receive eth0 "${NS2}" + check_fail $? "eth0 IS transmitting when disabled" + did_interface_receive eth1 "${NS2}" + check_err $? "eth1 not transmitting when enabled" + + ### Scenario 3: Change active port. + set_active_port "${NS1}" test_team1 eth0 + set_active_port "${NS2}" test_team2 eth0 + + did_interface_receive eth0 "${NS2}" + check_err $? "eth0 not transmitting when enabled" + did_interface_receive eth1 "${NS2}" + check_fail $? "eth1 IS transmitting when disabled" + + log_test "teamd active backup runner test" + + stop_sending_and_listening +} + +require_command teamd +require_command teamdctl +require_command iperf3 +environment_create activebackup +tests_run +exit "${EXIT_STATUS}" diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/n= et/lib.sh index b40694573f4c..992342b1dd7c 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -224,6 +224,19 @@ setup_ns() NS_LIST+=3D("${ns_list[@]}") } =20 +in_all_ns() +{ + local ret=3D0 + local ns_list=3D("${NS_LIST[@]}") + + for ns in "${ns_list[@]}"; do + ip netns exec "${ns}" bash -c "$@" + (( ret =3D ret || $? )) + done + + return "${ret}" +} + # Create netdevsim with given id and net namespace. create_netdevsim() { local id=3D"$1" --=20 2.53.0.1185.g05d4b7b318-goog From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (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 92FD1384242 for ; Thu, 2 Apr 2026 06:24:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111084; cv=none; b=s0NaQlpwLGWNMV1p+2W385buskJVveUzd120spnCTQmpwsDofhhozvhL6/CQdww79GKYDGVHB6xQ4tSv4kg/WpmOPEo3uNBlENhD529YJoW9Xa3bn/YCt/QKomyzJxHg0KKPnzK1haO7Pg8kLkLCAuQz5594FUB8K6rpVGHDSuQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111084; c=relaxed/simple; bh=XEekqRWRagMi4BiefCMzZBn3yV5nBbKSUX/5yOBM6Rs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=fK2PMirB0nGxj8g45PlfBaLgjI1srVCZnznOn8IJYxnq+AotC86F/Zkfy2u/cABNSnU7hvTiS4e9njxLAzjrSlf01D2xIVhWz7COxNZgsIxRMvLy5VKqtOYMGPVWhwlA3AzZRq845Ny+OO8hrXgnmsFsWpD5vz/MDnP8riYAh04= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=iMGV3lx1; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="iMGV3lx1" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c895e7de52so3037252eec.0 for ; Wed, 01 Apr 2026 23:24:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111081; x=1775715881; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=o6i9j0Zc4yAt+OiPAIey4weuRM6g1CylA1XWG1gYKfw=; b=iMGV3lx19D0XWVX+1kS07081etznCvlh4bsiDZ1dtz+TM2CwOR1YjzuyKNL8eEGdz2 IoQ9WMSiGpWuzTMqEs50+hcl+j06y/VzhWT9/cLanB8N9FcbCkAF7BTjhyJvilx+AcnE V1JH7hxjPaYuAi3jYQ3GkloMMKtMwcMBIyWFcnSoryAuooiPdgT/p5mFE2xJ72lVDhHv H3sKhCi8mkq7fVM3ZfHikQaeYpcsRNdIUwt1ta/FnFCZTCnf4PN1VK4l6id938ixqPba KIr6ziecWnDpmAWaP9iMLtqz7fUqKtE7JfSd8d2t5EMOdniMBQ3zVuDV9eUO97sGhAS6 xR9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111081; x=1775715881; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=o6i9j0Zc4yAt+OiPAIey4weuRM6g1CylA1XWG1gYKfw=; b=CTVdzH0Kgm50x27rmdInoicZVIBjkanUOCHhz2cUag+L7zIZxToOL0R4p7Nvn4gS8m CDa3z31glHHyofR2hLFTuNv4q2fMtDf74wKgoKYK4FYlcRakdKmHuhWmCIlI+BRV1eFA FrFtgMgljOdAVqz3O9Mgr8JK8IvDE7KZCJe+Kx7pWn7kE8IEFGOih5bJ8nzb8xNetkxy 3Eablq8DT7xeYYw77KgGkpgTBQ4cEowNgrfOiQH5e75O2KyRyP0C13iK6827gcqK7pAh YDlW+6RpIOAAfUS2X/qsfXHy4XsOt6YBJT+LlHFo0jGwyvgtf6jKYrb2Ob3DIgoUOM/P gFfg== X-Forwarded-Encrypted: i=1; AJvYcCUzlrZ5WAHAqU+clu29Ku0PvrfSJgp+XhbeHfWMBVegFNwalHNicOoWjH55Hthj74IBKIm4REv4tRJmbLM=@vger.kernel.org X-Gm-Message-State: AOJu0YwpXWvDYJ5d3FnxAfVmhhNrOZAy3KUpfiYh4I/O/Jk6/WtWyja5 tHQ0CdiNurcTluq8EuJ2ys/VzXsba3+NKZ1xKyA2VFqMFsKCrgSKNPpI3dm91ZyE7fEGGGVi2EY ZzlW0uKC6UTLjEIb0deKDfQ== X-Received: from dybry17.prod.google.com ([2002:a05:7301:4d11:b0:2c8:9aa5:8024]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:4605:b0:2cb:3113:771b with SMTP id 5a478bee46e88-2cb31137933mr128643eec.15.1775111080663; Wed, 01 Apr 2026 23:24:40 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:19 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=18342; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=XEekqRWRagMi4BiefCMzZBn3yV5nBbKSUX/5yOBM6Rs=; b=NoVrdwPibHwKTNUMBj5drOwdqA9xJq7ettAOGs12wK32yxgSxpRtPNgrCVKF9Rb89bT9aZX3I 3/WMlKfgFRXCRP4eqcAyB3szFauVpZrV9cMxodxX0qED8urGzQY2p1t X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-6-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 6/7] net: team: Decouple rx and tx enablement in the team driver From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable There are use cases where an aggregated port should send traffic, but not receive traffic, and vice versa. For example, in the IEEE 802.3ad-2000 specification, there is an optional "Independent Control" version of the mux machine. Currently there is no way create implementations like this for the team driver. Separate the existing "enabled" per-port option into tx and rx specific enablement options, but do so without breaking the existing "enabled" option. The existing "enabled" option is now defined as (rx_enabled AND tx_enabled), so if one is independently disabled, then the old "enabled" option will also not be enabled. However, setting the old "enabled" option affects both the rx_enabled and tx_enabled options. This is the first case where setting an option can affect another option. Note that teamd and any other software that exclusively uses "coupled" enablement will continue to work without any changes. Signed-off-by: Marc Harvey --- Changes in v3: - None Changes in v2: - None --- drivers/net/team/team_core.c | 238 ++++++++++++++++++++++++++-= ---- drivers/net/team/team_mode_loadbalance.c | 4 +- drivers/net/team/team_mode_random.c | 4 +- drivers/net/team/team_mode_roundrobin.c | 2 +- include/linux/if_team.h | 60 +++++--- 5 files changed, 245 insertions(+), 63 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index 2ce31999c99f..308ff7cd4b15 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -87,7 +87,7 @@ static void team_lower_state_changed(struct team_port *po= rt) struct netdev_lag_lower_state_info info; =20 info.link_up =3D port->linkup; - info.tx_enabled =3D team_port_enabled(port); + info.tx_enabled =3D team_port_tx_enabled(port); netdev_lower_state_changed(port->dev, &info); } =20 @@ -532,13 +532,13 @@ static void team_adjust_ops(struct team *team) * correct ops are always set. */ =20 - if (!team->en_port_count || !team_is_mode_set(team) || + if (!team->tx_en_port_count || !team_is_mode_set(team) || !team->mode->ops->transmit) team->ops.transmit =3D team_dummy_transmit; else team->ops.transmit =3D team->mode->ops->transmit; =20 - if (!team->en_port_count || !team_is_mode_set(team) || + if (!team->rx_en_port_count || !team_is_mode_set(team) || !team->mode->ops->receive) team->ops.receive =3D team_dummy_receive; else @@ -734,7 +734,7 @@ static rx_handler_result_t team_handle_frame(struct sk_= buff **pskb) =20 port =3D team_port_get_rcu(skb->dev); team =3D port->team; - if (!team_port_enabled(port)) { + if (!team_port_rx_enabled(port)) { if (is_link_local_ether_addr(eth_hdr(skb)->h_dest)) /* link-local packets are mostly useful when stack receives them * with the link they arrive on. @@ -831,7 +831,7 @@ static bool team_queue_override_port_has_gt_prio_than(s= truct team_port *port, return true; if (port->priority > cur->priority) return false; - if (port->index < cur->index) + if (port->tx_index < cur->tx_index) return true; return false; } @@ -876,7 +876,7 @@ static void __team_queue_override_enabled_check(struct = team *team) static void team_queue_override_port_prio_changed(struct team *team, struct team_port *port) { - if (!port->queue_id || !team_port_enabled(port)) + if (!port->queue_id || !team_port_tx_enabled(port)) return; __team_queue_override_port_del(team, port); __team_queue_override_port_add(team, port); @@ -887,7 +887,7 @@ static void team_queue_override_port_change_queue_id(st= ruct team *team, struct team_port *port, u16 new_queue_id) { - if (team_port_enabled(port)) { + if (team_port_tx_enabled(port)) { __team_queue_override_port_del(team, port); port->queue_id =3D new_queue_id; __team_queue_override_port_add(team, port); @@ -927,58 +927,172 @@ static bool team_port_find(const struct team *team, return false; } =20 +static void __team_port_enable_rx(struct team *team, + struct team_port *port) +{ + team->rx_en_port_count++; + WRITE_ONCE(port->rx_enabled, true); +} + +static void __team_port_disable_rx(struct team *team, + struct team_port *port) +{ + team->rx_en_port_count--; + WRITE_ONCE(port->rx_enabled, false); +} + +static void team_port_enable_rx(struct team *team, + struct team_port *port) +{ + if (team_port_rx_enabled(port)) + return; + + __team_port_enable_rx(team, port); + + team_adjust_ops(team); + + team_notify_peers(team); + team_mcast_rejoin(team); +} + +static void team_port_disable_rx(struct team *team, + struct team_port *port) +{ + if (!team_port_rx_enabled(port)) + return; + + __team_port_disable_rx(team, port); + team_adjust_ops(team); +} + +/* + * Enable just TX on the port by adding to tx-enabled port hashlist and + * setting port->tx_index (Might be racy so reader could see incorrect + * ifindex when processing a flying packet, but that is not a problem). + * Write guarded by RTNL. + */ +static void __team_port_enable_tx(struct team *team, + struct team_port *port) +{ + WRITE_ONCE(port->tx_index, team->tx_en_port_count); + WRITE_ONCE(team->tx_en_port_count, team->tx_en_port_count + 1); + hlist_add_head_rcu(&port->tx_hlist, + team_tx_port_index_hash(team, port->tx_index)); +} + +static void team_port_enable_tx(struct team *team, + struct team_port *port) +{ + if (team_port_tx_enabled(port)) + return; + + __team_port_enable_tx(team, port); + team_adjust_ops(team); + team_queue_override_port_add(team, port); + + /* Don't rejoin multicast, since this port might not be receiving. */ + team_notify_peers(team); + team_lower_state_changed(port); +} + /* - * Enable/disable port by adding to enabled port hashlist and setting - * port->index (Might be racy so reader could see incorrect ifindex when - * processing a flying packet, but that is not a problem). Write guarded - * by RTNL. + * Enable TX AND RX on the port. */ static void team_port_enable(struct team *team, struct team_port *port) { + bool rx_was_enabled; + bool tx_was_enabled; + if (team_port_enabled(port)) return; - WRITE_ONCE(port->index, team->en_port_count); - WRITE_ONCE(team->en_port_count, team->en_port_count + 1); - hlist_add_head_rcu(&port->hlist, - team_port_index_hash(team, port->index)); + + rx_was_enabled =3D team_port_rx_enabled(port); + tx_was_enabled =3D team_port_tx_enabled(port); + + if (!rx_was_enabled) + __team_port_enable_rx(team, port); + if (!tx_was_enabled) { + __team_port_enable_tx(team, port); + team_queue_override_port_add(team, port); + } + team_adjust_ops(team); - team_queue_override_port_add(team, port); team_notify_peers(team); - team_mcast_rejoin(team); - team_lower_state_changed(port); + + if (!rx_was_enabled) + team_mcast_rejoin(team); + if (!tx_was_enabled) + team_lower_state_changed(port); } =20 static void __reconstruct_port_hlist(struct team *team, int rm_index) { - int i; + struct hlist_head *tx_port_index_hash; struct team_port *port; + int i; =20 - for (i =3D rm_index + 1; i < team->en_port_count; i++) { - port =3D team_get_port_by_index(team, i); - hlist_del_rcu(&port->hlist); - WRITE_ONCE(port->index, port->index - 1); - hlist_add_head_rcu(&port->hlist, - team_port_index_hash(team, port->index)); + for (i =3D rm_index + 1; i < team->tx_en_port_count; i++) { + port =3D team_get_port_by_tx_index(team, i); + hlist_del_rcu(&port->tx_hlist); + WRITE_ONCE(port->tx_index, port->tx_index - 1); + tx_port_index_hash =3D team_tx_port_index_hash(team, + port->tx_index); + hlist_add_head_rcu(&port->tx_hlist, tx_port_index_hash); } } =20 -static void team_port_disable(struct team *team, - struct team_port *port) +static void __team_port_disable_tx(struct team *team, + struct team_port *port) { - if (!team_port_enabled(port)) - return; if (team->ops.port_tx_disabled) team->ops.port_tx_disabled(team, port); - hlist_del_rcu(&port->hlist); - __reconstruct_port_hlist(team, port->index); - WRITE_ONCE(port->index, -1); - WRITE_ONCE(team->en_port_count, team->en_port_count - 1); + + hlist_del_rcu(&port->tx_hlist); + __reconstruct_port_hlist(team, port->tx_index); + + WRITE_ONCE(port->tx_index, -1); + WRITE_ONCE(team->tx_en_port_count, team->tx_en_port_count - 1); +} + +static void team_port_disable_tx(struct team *team, + struct team_port *port) +{ + if (!team_port_tx_enabled(port)) + return; + + __team_port_disable_tx(team, port); + team_queue_override_port_del(team, port); team_adjust_ops(team); team_lower_state_changed(port); } =20 +static void team_port_disable(struct team *team, + struct team_port *port) +{ + bool rx_was_enabled; + bool tx_was_enabled; + + if (!team_port_tx_enabled(port) && !team_port_rx_enabled(port)) + return; + + rx_was_enabled =3D team_port_rx_enabled(port); + tx_was_enabled =3D team_port_tx_enabled(port); + + if (tx_was_enabled) { + __team_port_disable_tx(team, port); + team_queue_override_port_del(team, port); + } + if (rx_was_enabled) + __team_port_disable_rx(team, port); + + team_adjust_ops(team); + + if (tx_was_enabled) + team_lower_state_changed(port); +} + static int team_port_enter(struct team *team, struct team_port *port) { int err =3D 0; @@ -1244,7 +1358,7 @@ static int team_port_add(struct team *team, struct ne= t_device *port_dev, netif_addr_unlock_bh(dev); } =20 - WRITE_ONCE(port->index, -1); + WRITE_ONCE(port->tx_index, -1); list_add_tail_rcu(&port->list, &team->port_list); team_port_enable(team, port); netdev_compute_master_upper_features(dev, true); @@ -1429,6 +1543,46 @@ static int team_port_en_option_set(struct team *team, return 0; } =20 +static void team_port_tx_en_option_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port =3D ctx->info->port; + + ctx->data.bool_val =3D team_port_tx_enabled(port); +} + +static int team_port_tx_en_option_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port =3D ctx->info->port; + + if (ctx->data.bool_val) + team_port_enable_tx(team, port); + else + team_port_disable_tx(team, port); + return 0; +} + +static void team_port_rx_en_option_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port =3D ctx->info->port; + + ctx->data.bool_val =3D team_port_rx_enabled(port); +} + +static int team_port_rx_en_option_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port =3D ctx->info->port; + + if (ctx->data.bool_val) + team_port_enable_rx(team, port); + else + team_port_disable_rx(team, port); + return 0; +} + static void team_user_linkup_option_get(struct team *team, struct team_gsetter_ctx *ctx) { @@ -1550,6 +1704,20 @@ static const struct team_option team_options[] =3D { .getter =3D team_port_en_option_get, .setter =3D team_port_en_option_set, }, + { + .name =3D "tx_enabled", + .type =3D TEAM_OPTION_TYPE_BOOL, + .per_port =3D true, + .getter =3D team_port_tx_en_option_get, + .setter =3D team_port_tx_en_option_set, + }, + { + .name =3D "rx_enabled", + .type =3D TEAM_OPTION_TYPE_BOOL, + .per_port =3D true, + .getter =3D team_port_rx_en_option_get, + .setter =3D team_port_rx_en_option_set, + }, { .name =3D "user_linkup", .type =3D TEAM_OPTION_TYPE_BOOL, @@ -1595,7 +1763,7 @@ static int team_init(struct net_device *dev) return -ENOMEM; =20 for (i =3D 0; i < TEAM_PORT_HASHENTRIES; i++) - INIT_HLIST_HEAD(&team->en_port_hlist[i]); + INIT_HLIST_HEAD(&team->tx_en_port_hlist[i]); INIT_LIST_HEAD(&team->port_list); err =3D team_queue_override_init(team); if (err) diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/te= am_mode_loadbalance.c index 840f409d250b..38a459649569 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -120,7 +120,7 @@ static struct team_port *lb_hash_select_tx_port(struct = team *team, { int port_index =3D team_num_to_port_index(team, hash); =20 - return team_get_port_by_index_rcu(team, port_index); + return team_get_port_by_tx_index_rcu(team, port_index); } =20 /* Hash to port mapping select tx port */ @@ -380,7 +380,7 @@ static int lb_tx_hash_to_port_mapping_set(struct team *= team, =20 list_for_each_entry(port, &team->port_list, list) { if (ctx->data.u32_val =3D=3D port->dev->ifindex && - team_port_enabled(port)) { + team_port_tx_enabled(port)) { rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash), port); return 0; diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mo= de_random.c index 169a7bc865b2..370e974f3dca 100644 --- a/drivers/net/team/team_mode_random.c +++ b/drivers/net/team/team_mode_random.c @@ -16,8 +16,8 @@ static bool rnd_transmit(struct team *team, struct sk_buf= f *skb) struct team_port *port; int port_index; =20 - port_index =3D get_random_u32_below(READ_ONCE(team->en_port_count)); - port =3D team_get_port_by_index_rcu(team, port_index); + port_index =3D get_random_u32_below(READ_ONCE(team->tx_en_port_count)); + port =3D team_get_port_by_tx_index_rcu(team, port_index); if (unlikely(!port)) goto drop; port =3D team_get_first_port_txable_rcu(team, port); diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/tea= m_mode_roundrobin.c index dd405d82c6ac..ecbeef28c221 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -27,7 +27,7 @@ static bool rr_transmit(struct team *team, struct sk_buff= *skb) =20 port_index =3D team_num_to_port_index(team, rr_priv(team)->sent_packets++); - port =3D team_get_port_by_index_rcu(team, port_index); + port =3D team_get_port_by_tx_index_rcu(team, port_index); if (unlikely(!port)) goto drop; port =3D team_get_first_port_txable_rcu(team, port); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 740cb3100dfc..3d21e06fda67 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -27,10 +27,11 @@ struct team; =20 struct team_port { struct net_device *dev; - struct hlist_node hlist; /* node in enabled ports hash list */ + struct hlist_node tx_hlist; /* node in tx-enabled ports hash list */ struct list_head list; /* node in ordinary list */ struct team *team; - int index; /* index of enabled port. If disabled, it's set to -1 */ + int tx_index; /* index of tx enabled port. If disabled, -1 */ + bool rx_enabled; =20 bool linkup; /* either state.linkup or user.linkup */ =20 @@ -75,14 +76,24 @@ static inline struct team_port *team_port_get_rcu(const= struct net_device *dev) return rcu_dereference(dev->rx_handler_data); } =20 +static inline bool team_port_rx_enabled(struct team_port *port) +{ + return READ_ONCE(port->rx_enabled); +} + +static inline bool team_port_tx_enabled(struct team_port *port) +{ + return READ_ONCE(port->tx_index) !=3D -1; +} + static inline bool team_port_enabled(struct team_port *port) { - return READ_ONCE(port->index) !=3D -1; + return team_port_rx_enabled(port) && team_port_tx_enabled(port); } =20 static inline bool team_port_txable(struct team_port *port) { - return port->linkup && team_port_enabled(port); + return port->linkup && team_port_tx_enabled(port); } =20 static inline bool team_port_dev_txable(const struct net_device *port_dev) @@ -190,10 +201,11 @@ struct team { const struct header_ops *header_ops_cache; =20 /* - * List of enabled ports and their count + * List of tx-enabled ports and counts of rx and tx-enabled ports. */ - int en_port_count; - struct hlist_head en_port_hlist[TEAM_PORT_HASHENTRIES]; + int tx_en_port_count; + int rx_en_port_count; + struct hlist_head tx_en_port_hlist[TEAM_PORT_HASHENTRIES]; =20 struct list_head port_list; /* list of all ports */ =20 @@ -237,41 +249,43 @@ static inline int team_dev_queue_xmit(struct team *te= am, struct team_port *port, return dev_queue_xmit(skb); } =20 -static inline struct hlist_head *team_port_index_hash(struct team *team, - int port_index) +static inline struct hlist_head *team_tx_port_index_hash(struct team *team, + int tx_port_index) { - return &team->en_port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)]; + unsigned int list_entry =3D tx_port_index & (TEAM_PORT_HASHENTRIES - 1); + + return &team->tx_en_port_hlist[list_entry]; } =20 -static inline struct team_port *team_get_port_by_index(struct team *team, - int port_index) +static inline struct team_port *team_get_port_by_tx_index(struct team *tea= m, + int tx_port_index) { + struct hlist_head *head =3D team_tx_port_index_hash(team, tx_port_index); struct team_port *port; - struct hlist_head *head =3D team_port_index_hash(team, port_index); =20 - hlist_for_each_entry(port, head, hlist) - if (port->index =3D=3D port_index) + hlist_for_each_entry(port, head, tx_hlist) + if (port->tx_index =3D=3D tx_port_index) return port; return NULL; } =20 static inline int team_num_to_port_index(struct team *team, unsigned int n= um) { - int en_port_count =3D READ_ONCE(team->en_port_count); + int tx_en_port_count =3D READ_ONCE(team->tx_en_port_count); =20 - if (unlikely(!en_port_count)) + if (unlikely(!tx_en_port_count)) return 0; - return num % en_port_count; + return num % tx_en_port_count; } =20 -static inline struct team_port *team_get_port_by_index_rcu(struct team *te= am, - int port_index) +static inline struct team_port *team_get_port_by_tx_index_rcu(struct team = *team, + int tx_port_index) { + struct hlist_head *head =3D team_tx_port_index_hash(team, tx_port_index); struct team_port *port; - struct hlist_head *head =3D team_port_index_hash(team, port_index); =20 - hlist_for_each_entry_rcu(port, head, hlist) - if (READ_ONCE(port->index) =3D=3D port_index) + hlist_for_each_entry_rcu(port, head, tx_hlist) + if (READ_ONCE(port->tx_index) =3D=3D tx_port_index) return port; return NULL; } --=20 2.53.0.1185.g05d4b7b318-goog From nobody Sun Jun 14 09:53:30 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (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 3C1D03815D8 for ; Thu, 2 Apr 2026 06:24:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111084; cv=none; b=eELkxzVV1a3alsUIqdqCLo6ajQn3q6RExU4S8NN0ijUwtxh6F77rprzqqOz+rZtMp5MxnlEcUyKFtMlF0aUiC4F0g3x90pA8x72wlkYuYAxjEwX9OfSKvNv0tppy9a3Bh8SvuQMjvvaqKafwkqPZglY9/8JlMKN/9rf3N1CYLU8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775111084; c=relaxed/simple; bh=VlP7KCCmCqgAytGjzFELSVN/ZRu7AUVxx10WyoU5m+U=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=o+LwVsAJI5g2vOu8Tlxq9+tM0yF14lci4AXs6vwsIsNIGYmN6QK1MT5cANsQi7w+WXRfzCjtEu2ekES8cT5ZGPX0zr0ol9qVzbEIlK2s/FYo4mWRbWQWt6UwMfXbeL3FCEnOltgA4q8qOSlYbAi5GN7dlk1UcmkKj7pBmrgLkMA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Rkh/D5u+; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--marcharvey.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Rkh/D5u+" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c16233ee11so869393eec.1 for ; Wed, 01 Apr 2026 23:24:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775111082; x=1775715882; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=xhzFvy72ssCn2O3NoHeM6hn0rGVxPSe1zjhrn8cpSME=; b=Rkh/D5u+Tz2/WT1eQbw1IBBkETlEP18ivAmaJ+9Bowl4HgMvBD5PQBDOZXzFr1qaog M5rUs14uBMiBvjFP4FI/2gWmw5LYzbN/FF6aT6kFFZ4pIeCL+AmiEELiawBI2zum7B/c PsjwK7Wh4e2jA0FHjKm7vTg1sbmHyJVX35Lc5h9ek/Sdw2BPWlChMg+rXJiI98XJLcDR LiU3x9YKHiEfwmBEIxLHHsAgWgFTU2o5CtBXXYljYIl1lPE0fuAoxJkVX1LR2F3Fzr2H 2Qn3yowA22iPRTBI5cPOBo6pu/yhKetw1TNi2bJPhVdNY0UhAmaZnFvTPz93UzOB+4I0 RnHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775111082; x=1775715882; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=xhzFvy72ssCn2O3NoHeM6hn0rGVxPSe1zjhrn8cpSME=; b=DKNmVdu+ucZBN9NaXmD0GDO/PuMSq9jZlOjGnzGCPJ2y4C8lFPtqZAPGOQaUz9kmlW 4c3b4P7zdlM13bxthSynbP9be7xhBlZpnBwGLHDdce6me4FyPYdul0vpWFmUfu3TTI2U pF0dENj2il9YaqrSXUBTbwZ+fWNmdh1jf1EgeQ7OZVAXvcib/42z4mIYceqbZ+oLPlek fRTuIK29WH96ae7Q+Mia5J96DMmH0c84v2PJyOC39ycpIBWgwy5lO7DQKt871u6o3J8f 9CuZtOrvl20T5QVKf3T1ykqOqc2Ga4w2vb91ZlbuOkw+qUDUQlAsNfvIuUITIF33+1Tf aXDA== X-Forwarded-Encrypted: i=1; AJvYcCVSxbyeZ8RKNfCc1viQ+Wec0M/LWoM2h/woaZsbvGD3PwV6fktTzyowQ39o2IYqNvJVZ5BFpB7tt9eW3M0=@vger.kernel.org X-Gm-Message-State: AOJu0YzN63nsiY7g3Z3/osXTKpTMBPiJoMj0CHJcPQa3U42b14fmkF4o +CCXUTrWJhkCeMtw82aQYvTUu3pvuozLj/d5K5Wo0vu1Z44H1b97tmPKsS6ddaR52ZMu9aiXlto y5lN4ALif4DrYCSOqJ6qL2Q== X-Received: from dldoa9.prod.google.com ([2002:a05:701a:ca89:b0:12a:7182:6cb]) (user=marcharvey job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:1a81:b0:128:d786:8ac8 with SMTP id a92af1059eb24-12bee4d4ef8mr1191422c88.0.1775111082120; Wed, 01 Apr 2026 23:24:42 -0700 (PDT) Date: Thu, 02 Apr 2026 06:24:20 +0000 In-Reply-To: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260402-teaming-driver-internal-v3-0-e8cfdec3b5c2@google.com> X-Developer-Key: i=marcharvey@google.com; a=ed25519; pk=OzOeciadbfF5Bug/4/hyEAwfrruSY4tn0Q0LocyYUL0= X-Developer-Signature: v=1; a=ed25519-sha256; t=1775111069; l=12865; i=marcharvey@google.com; s=20260401; h=from:subject:message-id; bh=VlP7KCCmCqgAytGjzFELSVN/ZRu7AUVxx10WyoU5m+U=; b=h/uE+37BhQv2ufHd1kj9w1HmWMKXvlOTk+Y9Z3sVg4D0cbH4kusUKiu74LVXwDjG8UnD16Jhh UKVx/CGsaahCsrSVn60YBQjS0+FOWoZWV5hs6ftPo9vebWS3z3e2bi4 X-Mailer: b4 0.14.3 Message-ID: <20260402-teaming-driver-internal-v3-7-e8cfdec3b5c2@google.com> Subject: [PATCH net-next v3 7/7] selftests: net: Add tests for team driver decoupled tx and rx control From: Marc Harvey To: Jiri Pirko , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Shuah Khan , Simon Horman Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Marc Harvey Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Use ping and tcpdump to verify that independent rx and tx enablement of team driver member interfaces works as intended. Signed-off-by: Marc Harvey --- Changes in v3: - None Changes in v2: - Fix shellcheck failures. - Link to v1: https://lore.kernel.org/all/20260331053353.2504254-8-marcharv= ey@google.com/ --- tools/testing/selftests/drivers/net/team/Makefile | 1 + .../drivers/net/team/decoupled_enablement.sh | 249 +++++++++++++++++= ++++ .../testing/selftests/drivers/net/team/options.sh | 99 +++++++- 3 files changed, 348 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/team/Makefile b/tools/test= ing/selftests/drivers/net/team/Makefile index dab922d7f83d..7c58cf82121e 100644 --- a/tools/testing/selftests/drivers/net/team/Makefile +++ b/tools/testing/selftests/drivers/net/team/Makefile @@ -2,6 +2,7 @@ # Makefile for net selftests =20 TEST_PROGS :=3D \ + decoupled_enablement.sh \ dev_addr_lists.sh \ non_ether_header_ops.sh \ options.sh \ diff --git a/tools/testing/selftests/drivers/net/team/decoupled_enablement.= sh b/tools/testing/selftests/drivers/net/team/decoupled_enablement.sh new file mode 100755 index 000000000000..e2944af02d57 --- /dev/null +++ b/tools/testing/selftests/drivers/net/team/decoupled_enablement.sh @@ -0,0 +1,249 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# These tests verify the decoupled RX and TX enablement of team driver mem= ber +# interfaces. +# +# Topology +# +# +---------------------+ NS1 +# | test_team1 | +# | | | +# | eth0 | +# | | | +# | | | +# +---------------------+ +# | +# +---------------------+ NS2 +# | | | +# | | | +# | eth0 | +# | | | +# | test_team2 | +# +---------------------+ + +export ALL_TESTS=3D" + team_test_tx_enablement + team_test_rx_enablement +" + +test_dir=3D"$(dirname "$0")" +# shellcheck disable=3DSC1091 +source "${test_dir}/../../../net/lib.sh" +# shellcheck disable=3DSC1091 +source "${test_dir}/team_lib.sh" + +NS1=3D"" +NS2=3D"" +export NODAD=3D"nodad" +PREFIX_LENGTH=3D"64" +NS1_IP=3D"fd00::1" +NS2_IP=3D"fd00::2" +NS1_IP4=3D"192.168.0.1" +NS2_IP4=3D"192.168.0.2" +MEMBERS=3D("eth0") +PING_COUNT=3D5 +PING_TIMEOUT_S=3D1 +PING_INTERVAL=3D0.1 + +while getopts "4" opt; do + case $opt in + 4) + echo "IPv4 mode selected." + export NODAD=3D + PREFIX_LENGTH=3D"24" + NS1_IP=3D"${NS1_IP4}" + NS2_IP=3D"${NS2_IP4}" + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +# This has to be sourced after opts are gathered... +export REQUIRE_MZ=3Dno +export NUM_NETIFS=3D0 +# shellcheck disable=3DSC1091 +source "${test_dir}/../../../net/forwarding/lib.sh" + +# Create the network namespaces, veth pair, and team devices in the specif= ied +# mode. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +# Arguments: +# mode - The team driver mode to use for the team devices. +environment_create() +{ + trap cleanup_all_ns EXIT + setup_ns ns1 ns2 + NS1=3D"${NS_LIST[0]}" + NS2=3D"${NS_LIST[1]}" + + # Create the interfaces. + ip -n "${NS1}" link add eth0 type veth peer name eth0 netns "${NS2}" + ip -n "${NS1}" link add test_team1 type team + ip -n "${NS2}" link add test_team2 type team + + # Set up the receiving network namespace's team interface. + setup_team "${NS2}" test_team2 roundrobin "${NS2_IP}" \ + "${PREFIX_LENGTH}" "${MEMBERS[@]}" +} + +# Set a particular option value for team or team port. +# Arguments: +# namespace - The namespace name that has the team. +# option_name - The option name to set. +# option_value - The value to set the option to. +# team_name - The name of team to set the option for. +# member_name - The (optional) optional name of the member port. +set_option_value() +{ + local namespace=3D"$1" + local option_name=3D"$2" + local option_value=3D"$3" + local team_name=3D"$4" + local member_name=3D"$5" + local port_flag=3D"--port=3D${member_name}" + + ip netns exec "${namespace}" teamnl "${team_name}" setoption \ + "${option_name}" "${option_value}" "${port_flag}" + return $? +} + +# Send some pings and return the ping command return value. +try_ping() +{ + ip netns exec "${NS1}" ping -i "${PING_INTERVAL}" -c "${PING_COUNT}" \ + "${NS2_IP}" -W "${PING_TIMEOUT_S}" +} + +# Checks tcpdump output from net/forwarding lib, and checks if there are a= ny +# ICMP(4 or 6) packets. +# Arguments: +# interface - The interface name to search for. +# ip_address - The destination IP address (4 or 6) to search for. +did_interface_receive_icmp() +{ + local interface=3D"$1" + local ip_address=3D"$2" + local packet_count + + packet_count=3D$(tcpdump_show "$interface" | grep -c \ + "> ${ip_address}: ICMP") + echo "Packet count for ${interface} was ${packet_count}" + + if [[ "$packet_count" -gt 0 ]]; then + true + else + false + fi +} + +# Test JUST tx enablement with a given mode. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +# Arguments: +# mode - The mode to set the team interfaces to. +team_test_mode_tx_enablement() +{ + local mode=3D"$1" + export RET=3D0 + + # Set up the sender team with the correct mode. + setup_team "${NS1}" test_team1 "${mode}" "${NS1_IP}" \ + "${PREFIX_LENGTH}" "${MEMBERS[@]}" + check_err $? "Failed to set up sender team" + + ### Scenario 1: Member interface initially enabled. + # Expect ping to pass + try_ping + check_err $? "Ping failed when TX enabled" + + ### Scenario 2: Once tx-side interface disabled. + # Expect ping to fail. + set_option_value "${NS1}" tx_enabled false test_team1 eth0 + check_err $? "Failed to disable TX" + tcpdump_start eth0 "${NS2}" + try_ping + check_fail $? "Ping succeeded when TX disabled" + tcpdump_stop eth0 + # Expect no packets to be transmitted, since TX is disabled. + did_interface_receive_icmp eth0 "${NS2_IP}" + check_fail $? "eth0 IS transmitting when TX disabled" + tcpdump_cleanup eth0 + + ### Scenario 3: The interface has tx re-enabled. + # Expect ping to pass. + set_option_value "${NS1}" tx_enabled true test_team1 eth0 + check_err $? "Failed to reenable TX" + try_ping + check_err $? "Ping failed when TX reenabled" + + log_test "TX failover of '${mode}' test" +} + +# Test JUST rx enablement with a given mode. +# Globals: +# RET - Used by test infra, set by `check_err` functions. +# Arguments: +# mode - The mode to set the team interfaces to. +team_test_mode_rx_enablement() +{ + local mode=3D"$1" + export RET=3D0 + + # Set up the sender team with the correct mode. + setup_team "${NS1}" test_team1 "${mode}" "${NS1_IP}" \ + "${PREFIX_LENGTH}" "${MEMBERS[@]}" + check_err $? "Failed to set up sender team" + + ### Scenario 1: Member interface initially enabled. + # Expect ping to pass + try_ping + check_err $? "Ping failed when RX enabled" + + ### Scenario 2: Once rx-side interface disabled. + # Expect ping to fail. + set_option_value "${NS1}" rx_enabled false test_team1 eth0 + check_err $? "Failed to disable RX" + tcpdump_start eth0 "${NS2}" + try_ping + check_fail $? "Ping succeeded when RX disabled" + tcpdump_stop eth0 + # Expect packets to be transmitted, since only RX is disabled. + did_interface_receive_icmp eth0 "${NS2_IP}" + check_err $? "eth0 not transmitting when RX disabled" + tcpdump_cleanup eth0 + + ### Scenario 3: The interface has rx re-enabled. + # Expect ping to pass. + set_option_value "${NS1}" rx_enabled true test_team1 eth0 + check_err $? "Failed to reenable RX" + try_ping + check_err $? "Ping failed when RX reenabled" + + log_test "RX failover of '${mode}' test" +} + +team_test_tx_enablement() +{ + team_test_mode_tx_enablement broadcast + team_test_mode_tx_enablement roundrobin + team_test_mode_tx_enablement random +} + +team_test_rx_enablement() +{ + team_test_mode_rx_enablement broadcast + team_test_mode_rx_enablement roundrobin + team_test_mode_rx_enablement random +} + +require_command teamnl +require_command tcpdump +require_command ping +environment_create +tests_run +exit "${EXIT_STATUS}" diff --git a/tools/testing/selftests/drivers/net/team/options.sh b/tools/te= sting/selftests/drivers/net/team/options.sh index 44888f32b513..c87cf998a427 100755 --- a/tools/testing/selftests/drivers/net/team/options.sh +++ b/tools/testing/selftests/drivers/net/team/options.sh @@ -11,10 +11,14 @@ if [[ $# -eq 0 ]]; then exit $? fi =20 -ALL_TESTS=3D" +export ALL_TESTS=3D" team_test_options + team_test_enabled_implicit_changes + team_test_rx_enabled_implicit_changes + team_test_tx_enabled_implicit_changes " =20 +# shellcheck disable=3DSC1091 source "${test_dir}/../../../net/lib.sh" =20 TEAM_PORT=3D"team0" @@ -176,12 +180,105 @@ team_test_options() team_test_option mcast_rejoin_count 0 5 team_test_option mcast_rejoin_interval 0 5 team_test_option enabled true false "${MEMBER_PORT}" + team_test_option rx_enabled true false "${MEMBER_PORT}" + team_test_option tx_enabled true false "${MEMBER_PORT}" team_test_option user_linkup true false "${MEMBER_PORT}" team_test_option user_linkup_enabled true false "${MEMBER_PORT}" team_test_option priority 10 20 "${MEMBER_PORT}" team_test_option queue_id 0 1 "${MEMBER_PORT}" } =20 +team_test_enabled_implicit_changes() +{ + export RET=3D0 + + attach_port_if_specified "${MEMBER_PORT}" + check_err $? "Couldn't attach ${MEMBER_PORT} to master" + + # Set enabled to true. + set_and_check_get enabled true "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'enabled' to true" + + # Show that both rx enabled and tx enabled are true. + get_and_check_value rx_enabled true "--port=3D${MEMBER_PORT}" + check_err $? "'Rx_enabled' wasn't implicitly set to true" + get_and_check_value tx_enabled true "--port=3D${MEMBER_PORT}" + check_err $? "'Tx_enabled' wasn't implicitly set to true" + + # Set enabled to false. + set_and_check_get enabled false "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'enabled' to true" + + # Show that both rx enabled and tx enabled are false. + get_and_check_value rx_enabled false "--port=3D${MEMBER_PORT}" + check_err $? "'Rx_enabled' wasn't implicitly set to false" + get_and_check_value tx_enabled false "--port=3D${MEMBER_PORT}" + check_err $? "'Tx_enabled' wasn't implicitly set to false" + + log_test "'Enabled' implicit changes" +} + +team_test_rx_enabled_implicit_changes() +{ + export RET=3D0 + + attach_port_if_specified "${MEMBER_PORT}" + check_err $? "Couldn't attach ${MEMBER_PORT} to master" + + # Set enabled to true. + set_and_check_get enabled true "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'enabled' to true" + + # Set rx_enabled to false. + set_and_check_get rx_enabled false "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'rx_enabled' to false" + + # Show that enabled is false. + get_and_check_value enabled false "--port=3D${MEMBER_PORT}" + check_err $? "'enabled' wasn't implicitly set to false" + + # Set rx_enabled to true. + set_and_check_get rx_enabled true "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'rx_enabled' to true" + + # Show that enabled is true. + get_and_check_value enabled true "--port=3D${MEMBER_PORT}" + check_err $? "'enabled' wasn't implicitly set to true" + + log_test "'Rx_enabled' implicit changes" +} + +team_test_tx_enabled_implicit_changes() +{ + export RET=3D0 + + attach_port_if_specified "${MEMBER_PORT}" + check_err $? "Couldn't attach ${MEMBER_PORT} to master" + + # Set enabled to true. + set_and_check_get enabled true "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'enabled' to true" + + # Set tx_enabled to false. + set_and_check_get tx_enabled false "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'tx_enabled' to false" + + # Show that enabled is false. + get_and_check_value enabled false "--port=3D${MEMBER_PORT}" + check_err $? "'enabled' wasn't implicitly set to false" + + # Set tx_enabled to true. + set_and_check_get tx_enabled true "--port=3D${MEMBER_PORT}" + check_err $? "Failed to set 'tx_enabled' to true" + + # Show that enabled is true. + get_and_check_value enabled true "--port=3D${MEMBER_PORT}" + check_err $? "'enabled' wasn't implicitly set to true" + + log_test "'Tx_enabled' implicit changes" +} + + require_command teamnl setup tests_run --=20 2.53.0.1185.g05d4b7b318-goog