From nobody Sat Sep 26 12:28:44 2026 Received: from sender4-of-o54.zoho.com (sender4-of-o54.zoho.com [136.143.188.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C4023404BD5 for ; Mon, 24 Aug 2026 10:28:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.54 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787567290; cv=pass; b=ArBbGb3Z3wFoN2G/Hv59y2PqeWTU9kfu4C9XTASamiNeL6fgOdWwEZuh76QoZ0pPMtViR05BA99dPxttg2WY0GHWg1s3mKBFom0Wpebeit/lTQQgQ2ZzvIpS0lQqLStsr29RkLmj8hSGNjwUJ71fDjz0lIkl28zMAL6zdprFqig= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787567290; c=relaxed/simple; bh=A8ODt8hnUNHxWnP+1A6DmcXgFl2zKgJe2zv2sN/Mtd8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IwHhYMnZ3arVoTE3VxAuvmTeReyD3TKU/pF6rKdHtKrlnsrKsHU7bXPi1TFEkf0vqREJhCha79jMP+LbIQqcNuhTCGv+jOf9226HId4koFoS0+v38NUuqpWg21lEQYwVYjqTJIME+6+5464f1A+9EtkYOTukejX37edB1IvyGNA= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com; spf=pass smtp.mailfrom=mpiricsoftware.com; dkim=fail (0-bit key) header.d=mpiricsoftware.com header.i=kalpan.jani@mpiricsoftware.com header.b=UTbs7/Yy reason="key not found in DNS"; arc=pass smtp.client-ip=136.143.188.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="key not found in DNS" (0-bit key) header.d=mpiricsoftware.com header.i=kalpan.jani@mpiricsoftware.com header.b="UTbs7/Yy" ARC-Seal: i=1; a=rsa-sha256; t=1787567275; cv=none; d=zohomail.com; s=zohoarc; b=MAKQGYbQHEfc2YXCO4BYwSimxqkVnyfEIsCNtEarOtDHSlwdTYKugeBh1SW0FvdQPwmD0fdOlUG+soes+XWKxfT1P7ET1rDwqi5dO3DUcSm4ukWivvTgieGxnvuAb2MJcrhTMDFK1hU6riZ1CA21/fwEMhvS6IZPr8lkXRv5mSY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787567275; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=lXJFg2gh8PxRiyEL7FK+MaQC+pYD19sEKaw9+gNQ4k0=; b=geOOpxcq6V5fn168w2aPYHq5W2ZeDXkusJCLiW91wYowWJFlpmPFh3NIXsNlcNpHH4ZGlFDwCoutfFH4uiSaGgkGV6A6ttZPM4fCcymIn6MJTAv4gujYYNUzblj7gxHv3/wYVwnz2t2Mpqob1/PR0eAo8JObp+exTCiQQ9bNdNM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=mpiricsoftware.com; spf=pass smtp.mailfrom=kalpan.jani@mpiricsoftware.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1787567275; s=mpiric; d=mpiricsoftware.com; i=kalpan.jani@mpiricsoftware.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=lXJFg2gh8PxRiyEL7FK+MaQC+pYD19sEKaw9+gNQ4k0=; b=UTbs7/YyxB1KHZsvirc4rFPHhjC5rE+Ugdbu/Cv4BUWyoojM2ru2woH56hrzrsqZ OURYVmeewtSVUODgtOuf3YfNGE7zEOr3F345N1bGk8qIHHLCXP/3lQesI0GC1XJHyJN 6iOOJoVaD+/f3Ib80H1lm+ncfSpurVgKcCrOyUSI= Received: by mx.zohomail.com with SMTPS id 1787567274159883.0568675579015; Mon, 24 Aug 2026 03:27:54 -0700 (PDT) From: Kalpan Jani To: mptcp@lists.linux.dev Cc: matttbe@kernel.org, martineau@kernel.org, pabeni@redhat.com, shardul.b@mpiricsoftware.com, janak@mpiric.us, kalpanjani009@gmail.com Subject: [PATCH mptcp-next v2 1/2] mptcp: sched: add subflow avoid flag and enforce it in core Date: Mon, 24 Aug 2026 15:57:40 +0530 Message-ID: <20260824102741.347492-2-kalpan.jani@mpiricsoftware.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260824102741.347492-1-kalpan.jani@mpiricsoftware.com> References: <20260824102741.347492-1-kalpan.jani@mpiricsoftware.com> Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" The core benches a subflow via the "stale" bit after net.mptcp.stale_loss_cnt retransmission intervals without progress. That bit belongs to the core and is tightly coupled to the RTO: it gets cleared as soon as a packet is acked on the subflow (mptcp_subflow_active() calls mptcp_subflow_set_active() once rcv_tstamp moves forward). "scheduled" doesn't cover this either. The core clears it right after every send/retrans pass the scheduler set it in, so there's no way to express "leave this subflow alone across future rounds" with it, and nothing outside the scheduler that set it ever sees the value. Add a scheduler-owned "avoid" bit to mptcp_subflow_context: - only mptcp_subflow_set_avoid() touches it, never the core. It's exposed to BPF struct_ops schedulers as a kfunc with the same registration and filter as mptcp_subflow_set_scheduled(), so only a scheduler can call it; - it sits in the existing __unused:8 padding of the flag bitfield rather than as its own bool, so the struct doesn't grow; - it lives in the struct_group(reset) block, so it clears on subflow reset and a re-established path starts fresh; - mptcp_subflow_get_send() and mptcp_subflow_get_retrans() -- the default in-kernel subflow picker -- now skip an avoided subflow next to the existing active-subflow check. This runs regardless of whether a custom scheduler is loaded, so the flag has real weight on core selection and isn't only meaningful to whatever BPF program happens to read it back. avoid defaults to false and only an explicit set_avoid() call flips it, so this is a no-op for anyone who doesn't use it; - it's surfaced through MPTCP diag as MPTCP_SUBFLOW_FLAG_AVOID, alongside the existing BKUP/FULLY_ESTABLISHED flags. set_avoid() has no C callers in-tree, unlike set_scheduled() which the core send/retrans/close paths call directly -- mark it __bpf_kfunc so it survives LTO/dead-code-elimination before resolve_btfids looks for it in BTF. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/349 Suggested-by: Paolo Abeni Signed-off-by: Kalpan Jani --- Changes since v1: - moved avoid from a standalone bool into the existing __unused:8 bitfield padding: the extra bool pushed lent_mem_frag onto a 4-byte-aligned boundary, adding a 4-byte hole. The bitfield slot was already reserved and unused. - marked mptcp_subflow_set_avoid() __bpf_kfunc: no C callers, unlike set_scheduled(), so it could be dropped under LTO before resolve_btfids finds it in BTF. - switched subflow->avoid access from WRITE_ONCE()/READ_ONCE() to plain assignment/read: it's a bitfield member now, and {READ,WRITE}_ONCE() require taking its address, which bitfields don't allow. - mptcp_subflow_get_send() and mptcp_subflow_get_retrans() now skip a subflow with avoid set, next to the existing active-subflow check: previously nothing in the core consulted the flag. - exposed avoid via MPTCP diag as MPTCP_SUBFLOW_FLAG_AVOID, next to the existing BKUP_LOC/FULLY_ESTABLISHED flags. v1: https://lore.kernel.org/all/20260715101148.2601045-2-kalpan.jani@mpiric= software.com/ --- include/uapi/linux/mptcp.h | 1 + net/mptcp/bpf.c | 1 + net/mptcp/diag.c | 2 ++ net/mptcp/protocol.c | 4 ++-- net/mptcp/protocol.h | 5 ++++- net/mptcp/sched.c | 6 ++++++ 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index 72a5d030154e..b4ca97e07445 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -22,6 +22,7 @@ #define MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED _BITUL(6) #define MPTCP_SUBFLOW_FLAG_CONNECTED _BITUL(7) #define MPTCP_SUBFLOW_FLAG_MAPVALID _BITUL(8) +#define MPTCP_SUBFLOW_FLAG_AVOID _BITUL(9) =20 #define MPTCP_PM_CMD_GRP_NAME "mptcp_pm_cmds" #define MPTCP_PM_EV_GRP_NAME "mptcp_pm_events" diff --git a/net/mptcp/bpf.c b/net/mptcp/bpf.c index 0845061ddc65..ca6e3e2c3c60 100644 --- a/net/mptcp/bpf.c +++ b/net/mptcp/bpf.c @@ -318,6 +318,7 @@ BTF_KFUNCS_START(bpf_mptcp_common_kfunc_ids) BTF_ID_FLAGS(func, bpf_mptcp_subflow_ctx, KF_RET_NULL) BTF_ID_FLAGS(func, bpf_mptcp_subflow_tcp_sock, KF_RET_NULL) BTF_ID_FLAGS(func, mptcp_subflow_set_scheduled) +BTF_ID_FLAGS(func, mptcp_subflow_set_avoid) BTF_ID_FLAGS(func, mptcp_subflow_active) BTF_ID_FLAGS(func, mptcp_set_timeout) BTF_ID_FLAGS(func, mptcp_wnd_end) diff --git a/net/mptcp/diag.c b/net/mptcp/diag.c index 70cf9ebce833..3e98125a9c0c 100644 --- a/net/mptcp/diag.c +++ b/net/mptcp/diag.c @@ -49,6 +49,8 @@ static int subflow_get_info(struct sock *sk, struct sk_bu= ff *skb, bool net_admin flags |=3D MPTCP_SUBFLOW_FLAG_BKUP_LOC; if (READ_ONCE(sf->fully_established)) flags |=3D MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED; + if (sf->avoid) + flags |=3D MPTCP_SUBFLOW_FLAG_AVOID; if (sf->conn_finished) flags |=3D MPTCP_SUBFLOW_FLAG_CONNECTED; if (sf->map_valid) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index ffcf5a1788f6..3de919eee3ec 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1582,7 +1582,7 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock= *msk) =20 trace_mptcp_subflow_get_send(subflow); ssk =3D mptcp_subflow_tcp_sock(subflow); - if (!mptcp_subflow_active(subflow)) + if (!mptcp_subflow_active(subflow) || subflow->avoid) continue; =20 tout =3D max(tout, mptcp_timeout_from_subflow(subflow)); @@ -2501,7 +2501,7 @@ struct sock *mptcp_subflow_get_retrans(struct mptcp_s= ock *msk) mptcp_for_each_subflow(msk, subflow) { struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); =20 - if (!__mptcp_subflow_active(subflow)) + if (!__mptcp_subflow_active(subflow) || subflow->avoid) continue; =20 /* still data outstanding at TCP level? skip this */ diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index da40c6f3705f..b754e26be134 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -584,7 +584,8 @@ struct mptcp_subflow_context { is_mptfo : 1, /* subflow is doing TFO */ close_event_done : 1, /* has done the post-closed part */ mpc_drop : 1, /* the MPC option has been dropped in a rtx */ - __unused : 8; + avoid : 1, /* pkt scheduler: skip subflow if possible */ + __unused : 7; bool data_avail; bool scheduled; bool pm_listener; /* a listener managed by the kernel PM? */ @@ -897,6 +898,8 @@ static inline bool __mptcp_subflow_active(struct mptcp_= subflow_context *subflow) =20 void mptcp_subflow_set_active(struct mptcp_subflow_context *subflow); =20 +void mptcp_subflow_set_avoid(struct mptcp_subflow_context *subflow, bool a= void); + bool mptcp_subflow_active(struct mptcp_subflow_context *subflow); =20 void mptcp_subflow_drop_ctx(struct sock *ssk); diff --git a/net/mptcp/sched.c b/net/mptcp/sched.c index 1e59072d478c..0b85f1932d57 100644 --- a/net/mptcp/sched.c +++ b/net/mptcp/sched.c @@ -165,6 +165,12 @@ void mptcp_subflow_set_scheduled(struct mptcp_subflow_= context *subflow, WRITE_ONCE(subflow->scheduled, scheduled); } =20 +__bpf_kfunc void mptcp_subflow_set_avoid(struct mptcp_subflow_context *sub= flow, + bool avoid) +{ + subflow->avoid =3D avoid; +} + int mptcp_sched_get_send(struct mptcp_sock *msk) { struct mptcp_subflow_context *subflow; --=20 2.43.0 From nobody Sat Sep 26 12:28:44 2026 Received: from sender5-of-o54.zoho.com (sender5-of-o54.zoho.com [165.173.182.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 29839404BC8 for ; Mon, 24 Aug 2026 10:28:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=165.173.182.54 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787567300; cv=pass; b=rPZrsXHatX4UwDd0PnqgnktVw/mdZ7gScl6nZdZTU/utvX0eDGDRMs+VXObWjQ4MxO+5wgcNsB4fmxVp19Pu/dCiuLt08C9ff27d5m+6x0ONBoZIK9kA6jPFuTkYPaQaD34A3gg/JsjAE/XVxQGcjyqUUqWNLxg6ptPERq8lQ+Y= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787567300; c=relaxed/simple; bh=1Z5+xxcnoJcC32azKn3P0nuFZZAAVcWuD9CVatLywWY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kLzqo2hmNb8oRh16xP3EiOkoYuV13xuNgrerWanA/cCBUHJ/NijR7ndtOB6s1xbM0QPn7bp7prSIfptqc7ir5ooQ34A6JqReaSwVGYpyiU2rv8Zs5j7IIC5WjFgBytG/NdP5wmlMtkTS97UCFQmtyqt2ydRGH4RceyjqG5U4tGg= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com; spf=pass smtp.mailfrom=mpiricsoftware.com; dkim=fail (0-bit key) header.d=mpiricsoftware.com header.i=kalpan.jani@mpiricsoftware.com header.b=mWSUE50j reason="key not found in DNS"; arc=pass smtp.client-ip=165.173.182.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="key not found in DNS" (0-bit key) header.d=mpiricsoftware.com header.i=kalpan.jani@mpiricsoftware.com header.b="mWSUE50j" ARC-Seal: i=1; a=rsa-sha256; t=1787567281; cv=none; d=zohomail.com; s=zohoarc; b=lt0bInv4TubSfzrFnkRUaC8t9M0uYxmZJZU5R9ISxvLLPDd8R4TGvepmA0ZdR+A837BeNB56izPxfTLPdKsMiDx5yhcaLhNbCQGvVMKw0o1Lr1tqthuv8GcMkXtCZJq6Y0FYXvnIm/XorjO8Y74/iECtyfel9xWErz85uAKQls8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787567281; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=j92S/EE1GFFkJM9Jc4mdetdIYeBSMBwiydkkH3Kj1Nw=; b=nQnb0S+W6f0xae1kD+/WsFGBk5FfosOeQ67QoDaBrq3fAOPJ9YFb2wDEUL1Rl/hMv6QL6tTGRRrtaI8FSijcNb+jnoTsiS0oJjv+zph6dXWyE+RWRX2CTt0ykeiZYMV9LTpw32gBvj5IBFVPjQLhTKSk7r0Wxxai8DBpwfCfiDo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=mpiricsoftware.com; spf=pass smtp.mailfrom=kalpan.jani@mpiricsoftware.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1787567281; s=mpiric; d=mpiricsoftware.com; i=kalpan.jani@mpiricsoftware.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=j92S/EE1GFFkJM9Jc4mdetdIYeBSMBwiydkkH3Kj1Nw=; b=mWSUE50jdxPp31nP4PuznXNeVdv0Z3rpg57mZ6U+w5hgArnYcdkP+7F4ZsPpKL5N llmgDz7vJ8i1xjtlopiTwKHj+aSlTEwSq2ZPAyg0kp91etnoItzW5ylvDCcAmCop8XK kZfN9a+UKmgy+6D0ycCbxT8mQ4xAFrkHFz91UU1E= Received: by mx.zohomail.com with SMTPS id 1787567278504810.7279472492908; Mon, 24 Aug 2026 03:27:58 -0700 (PDT) From: Kalpan Jani To: mptcp@lists.linux.dev Cc: matttbe@kernel.org, martineau@kernel.org, pabeni@redhat.com, shardul.b@mpiricsoftware.com, janak@mpiric.us, kalpanjani009@gmail.com Subject: [PATCH mptcp-next v2 2/2] selftests: mptcp: bpf: exercise the subflow avoid flag across rounds Date: Mon, 24 Aug 2026 15:57:41 +0530 Message-ID: <20260824102741.347492-3-kalpan.jani@mpiricsoftware.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260824102741.347492-1-kalpan.jani@mpiricsoftware.com> References: <20260824102741.347492-1-kalpan.jani@mpiricsoftware.com> Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Add a BPF struct_ops scheduler selftest for mptcp_subflow_set_avoid(), and add the required extern declarations for it and for mptcp_subflow_active(), both declared locally in this file rather than in the shared mptcp_bpf.h. mptcp_subflow_active()'s extern was missing here even though the other BPF scheduler progs already have it (see mptcp_bpf_burst.c). "scheduled" gets cleared by the core after every single send/retrans pass, so a scheduler has to set it again every round. This test's scheduler does the opposite on purpose: it marks a subflow avoided exactly once, the first time get_send() runs, and never calls set_avoid() again after that. Every later round skips the marked subflow purely by reading back state set earlier. A counter of get_send() invocations, asserted greater than one once the transfer is done, makes sure the test actually ran more than one round instead of just assuming it from how much data got sent. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/349 Suggested-by: Paolo Abeni Signed-off-by: Kalpan Jani --- Changes since v1: - reworked the scheduler to set avoid once, on the first get_send() call, and rely on reading it back on every later call rather than re-asserting it: the previous version set and read the flag within the same call, which doesn't exercise standing state across scheduling rounds. Added a get_send_calls counter, asserted >1, so the test enforces multiple rounds actually occurred. - added the missing extern __ksym declaration for mptcp_subflow_active(), matching the existing pattern in mptcp_bpf_burst.c. - declared the mptcp_subflow_set_avoid() extern in this file instead of mptcp_bpf.h: checkpatch flags new externs in shared headers, and this kfunc has only one caller. v1: https://lore.kernel.org/all/20260715101148.2601045-3-kalpan.jani@mpiric= software.com/ --- .../testing/selftests/bpf/prog_tests/mptcp.c | 16 +++++ .../selftests/bpf/progs/mptcp_bpf_avoid.c | 70 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcp_bpf_avoid.c diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing= /selftests/bpf/prog_tests/mptcp.c index 7f48fd9e94e1..8ad09e94335a 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -17,6 +17,7 @@ #include "mptcp_bpf_bkup.skel.h" #include "mptcp_bpf_rr.skel.h" #include "mptcp_bpf_red.skel.h" +#include "mptcp_bpf_avoid.skel.h" #include "mptcp_bpf_burst.skel.h" =20 #define NS_TEST "mptcp_ns" @@ -813,6 +814,19 @@ static void test_burst(void) mptcp_bpf_burst__destroy(skel); } =20 +static void test_avoid(void) +{ + struct mptcp_bpf_avoid *skel; + + skel =3D mptcp_bpf_avoid__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load: avoid")) + return; + + test_bpf_sched(skel->maps.avoid, "avoid", WITH_DATA, WITHOUT_DATA); + ASSERT_GT(skel->bss->get_send_calls, 1, "get_send_calls"); + mptcp_bpf_avoid__destroy(skel); +} + void test_mptcp(void) { if (test__start_subtest("base")) @@ -835,4 +849,6 @@ void test_mptcp(void) test_red(); if (test__start_subtest("burst")) test_burst(); + if (test__start_subtest("avoid")) + test_avoid(); } diff --git a/tools/testing/selftests/bpf/progs/mptcp_bpf_avoid.c b/tools/te= sting/selftests/bpf/progs/mptcp_bpf_avoid.c new file mode 100644 index 000000000000..2c2553195d7f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/mptcp_bpf_avoid.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026, MPTCP. */ + +#include "mptcp_bpf.h" +#include + +extern bool mptcp_subflow_active(struct mptcp_subflow_context *subflow) __= ksym; +extern void mptcp_subflow_set_avoid(struct mptcp_subflow_context *subflow, + bool avoid) __ksym; + +char _license[] SEC("license") =3D "GPL"; + +/* set once, on the first scheduling round, never touched again -- this + * is what makes "avoid" different from "scheduled": nothing here + * re-asserts the decision, and the core doesn't clear it either. + */ +bool avoid_marked; +__u32 get_send_calls; + +SEC("struct_ops") +void BPF_PROG(mptcp_sched_avoid_init, struct mptcp_sock *msk) +{ + avoid_marked =3D false; + get_send_calls =3D 0; +} + +SEC("struct_ops") +void BPF_PROG(mptcp_sched_avoid_release, struct mptcp_sock *msk) +{ +} + +SEC("struct_ops") +int BPF_PROG(bpf_avoid_get_send, struct mptcp_sock *msk) +{ + struct mptcp_subflow_context *subflow; + + get_send_calls++; + + if (!avoid_marked) { + int i =3D 0; + + /* bench every subflow but the first, once */ + bpf_for_each(mptcp_subflow, subflow, (struct sock *)msk) { + if (i++ > 0) + mptcp_subflow_set_avoid(subflow, true); + } + avoid_marked =3D true; + } + + /* every round -- including this one -- just reads back state set + * at most once above. no re-assertion, ever. + */ + bpf_for_each(mptcp_subflow, subflow, (struct sock *)msk) { + if (subflow->avoid) + continue; + if (mptcp_subflow_active(subflow)) { + mptcp_subflow_set_scheduled(subflow, true); + break; + } + } + return 0; +} + +SEC(".struct_ops.link") +struct mptcp_sched_ops avoid =3D { + .init =3D (void *)mptcp_sched_avoid_init, + .release =3D (void *)mptcp_sched_avoid_release, + .get_send =3D (void *)bpf_avoid_get_send, + .name =3D "bpf_avoid", +}; --=20 2.43.0