From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.177 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794077085; b=aB3zXBuUIFBmeBOmM0ci1DW3O03/HUaJDPZuuwpoNrdOXWh/LZmotQ9agKv2hm09vQ2p FeY7QLPyxgcAaTierdQbuhAHmWPrGR2KZdGuhQLlIDbtOs+fc7X3BBC3Hl+TvfLEp+gau 2FfDEEUMYbA9zIAz9Fty7u+LkJdIzOLmfBZPhXY8kYssRG0V/rJxi1JSaaNru0ngDvqND SvOk0vkW68lh5At5EE966UEcLLgn4gsI4RC2H19oPUYgs0Z5hZexNzhuHenaDRipqLkRX 7rKur2e+JrPotLRcj2FcPcRqiL/lWwpqTloXwmvn4QiG/HgQ1T84g4LmDJOQIWdH66A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794077085; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: From:To:Cc:Subject:Date:Message-ID:X-Mailer:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding; bh=KZ+ukhZnuLhEoTcJCFbadpKzgvv+xdEMUoSf7p2jX1E=; b=i1mrc/ZKcCAFBLwJBdku/9+jzwA6VHhocMC+Q9Ou8P/mRKCGK79i+9WNMkUvAVvyEFCs iFaK7CmusCpZvhnPV/L2UjLvZZRqVPf/eLYQbQT1NdWOUwDUC0ZR0h7+J3QjB2FrA5fhp 1zri5DM32GIo/irLSu0FSruIdOfPKzaCdVknxF66VwuDXERjxV1D+OfcRhhAoHXm/sw11 hjSsuxjoU4rMHQqTvou/Oli640RLOK4tWaRs9+wXYT2OBjKOu677QugvpwaG28TzdJ4tU BlM8kBQywN0X8EDwzZLjvHpBjeGHxRl7okwy6fSjq8F8sn9sy91rACXICUpUBQeFKJA== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=blockcast.net; spf=pass smtp.mailfrom=blockcast.net; dkim=pass header.d=blockcast.net header.i=@blockcast.net header.a=rsa-sha256 header.s=google header.b=fdYjPVwt; arc=none smtp.remote-ip=74.125.82.177 Received: from mail-dy1-f177.google.com (mail-dy1-f177.google.com [74.125.82.177]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0A37606D806 for ; Sat, 7 Nov 2026 18:44:44 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org C0A37606D806 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=blockcast.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=blockcast.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=blockcast.net header.i=@blockcast.net header.a=rsa-sha256 header.s=google header.b=fdYjPVwt Received: by mail-dy1-f177.google.com with SMTP id 5a478bee46e88-36fc0a3fe93so2804904eec.0 for ; Sat, 07 Nov 2026 10:44:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=blockcast.net; s=google; t=1794077084; x=1794681884; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=KZ+ukhZnuLhEoTcJCFbadpKzgvv+xdEMUoSf7p2jX1E=; b=fdYjPVwtfjRAngxFK3IxmrpzQEm2aqrueU/2fJxIXkKkWai7Ca9ceI+Dd+aC0HgP7n c7rNS+7+YoDUspPUIoPtYaMZUTRAM3dLADmi7ux24KzCWfCIn5e0GdzOC89vgAa2Ec/C bcH1HhSDEb2X6FK6vB5FwqZfoLgPGVYWyolzUPrq4cwjxmQkMahUnWcYstEXTtwxEt2R EBx81T9GsUQDt5qEOd2WYvTwOfZGomaJMRvInTtkp9wRahOqVcVyz+kZESdAyia+my59 kzhKDcvSYp9jG8sD6PbEFaqVOXqQroPJl7LUnt7/temfn9UuJ+AgziLsF6ZROVe3isBK b9MQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794077084; x=1794681884; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=KZ+ukhZnuLhEoTcJCFbadpKzgvv+xdEMUoSf7p2jX1E=; b=h9/3GQFl2shDF4IfnnJz+LY2lXnN28GRc7PSeW6V9i2lMPDW+XgfXc2xqWV4+3SMzM Mpn0/cVLrphrLr0P+Cp7ktFsRkx7PeQjwgs35eWxq1dPZpXApsgi4ViFYF6kbIk0mutX Qwe9yrA88GeH/r4Laz09FjM2Z8uYmbuXlHIVE7Y4AMaDQjuualkSm1EEUSwf5VJ93SyX q5VTxb+ewLKooj7mWaiiNO9tYKKOYuSY13afVtgrYEsjn3uTsWMcwgvry/pow352YUeZ F5Y7/PfdlpaPx5FeEaz4ZrgUqfX/y8lOLm4bHQ9CVEY4bdDXRuqcbGnGNH9OhgnkiTmz ymXg== X-Forwarded-Encrypted: i=1; AL+hErEsVWPAn6Mj+YvQCLIp8TlR2jnran3wKUewC5oaNK8OeNEqasQ/cPeRj+VR8HY41DEeiNPzIZoTEt+fQ50=@vger.kernel.org X-Gm-Message-State: APuU4Z/+LbGQu3rD9QvsJHAuuAPSsDb7cvFajRYjpy3vdUd0R8oMupfM MuodfYytregmO9coxTw4VBP8HmsclT/jijPVKhFB1NemayztL9Zz7s/QxiUSV+zpBCk= X-Gm-Gg: AYBFou0p+uSkv47sCJR88XvldWmABspqqXFAL8BElvv9QaeS2yobWD5n/qzVsaCBJMq N9eXvFihf6cYzmVxfQfocJZxZXrbVuv1jSN4tU8sK6XLr7RWxpUT+sja67WYyubBMoDKoYg7e0T vFQIIemUYqtBGl0vFjKakwninC4lS7ZzQwQcfgtVTrAHc64hOhbRALaUs+e89k+c7CVlO9utQS0 khC6hVOKMcaJoebC+1d8GDihOfrTrxjID/RWOFQXDY5ynTNoGXUGAZw+CftBKwQjf4jlhRebQ5J gQepjSIK2J9Nmu4R7xwe5ZZOQULgO+/SRNOyJ2rbc+PqUqDRicRXPMMDyZgDNIPGzi3sCvTqchC +hVB3bJWkm4merqLBGplXqktoRKX/HwhjexC5wSV+RXQH+xCYJ+ukczMTCbd0EceUfk8SWEh1di 7WuG7z21auB9tpEE05DE6tK8beoOZEIdD1Ir2sKlwLO3CapTKXlKML6KZkDZGBCZ09oJWOtdX9Z AAJhWf5nMMoICLnwKllgLS68x+Gykyszh8A5QI5RZ5rEHeeNIaeH4F3zT9YP/st3riTGVHmuWBo twU0c2+Fz0vtchzujn64P9fTb0ltqBqJV0QI2wXw1kkw1Ij1wxGChifyFeyWqBW6OXyKxm9dnjh ZL+6Wa+B5FG/+0I0P X-Received: by 2002:a05:7301:f0b:b0:366:83dd:46f7 with SMTP id 5a478bee46e88-36f75a26838mr8205255eec.17.1794077083543; Sat, 07 Nov 2026 10:44:43 -0800 (PST) Received: from devbox.ts.blockcast.net ([2602:f74d:1::32]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36fa8c4f28dsm17286897eec.9.2026.11.07.10.44.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 07 Nov 2026 10:44:42 -0800 (PST) From: Omar Ramadan To: Taehee Yoo , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, michael.chan@broadcom.com, pavan.chebbi@broadcom.com, saeedm@nvidia.com, leon@kernel.org, tariqt@nvidia.com, mbloch@nvidia.com, brett.creeley@amd.com, sgoutham@marvell.com, anthony.l.nguyen@intel.com, przemyslaw.kitszel@intel.com, intel-wired-lan@lists.osuosl.org, Ido Schimmel , Satish Kharat , Andrew Lunn , Simon Horman , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , linux-kselftest@vger.kernel.org, linux-hardening@vger.kernel.org Subject: [RFC PATCH net-next 1/9] amt: add the relay replication offload provider header Date: Sat, 7 Nov 2026 18:44:29 +0000 Message-ID: <20261107184437.2783174-2-omar@blockcast.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261107184437.2783174-1-omar@blockcast.net> References: <20261107184437.2783174-1-omar@blockcast.net> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit include/net/amt_offload.h: provider caps, opaque provider-owned endpoint and group handles, struct amt_offload_ops, TX/INGRESS paths, FALLBACK/CONSUMED/BUSY results, the per-binding u64 epoch, and the amt_offload_register()/amt_offload_unregister() entry points. An in-kernel interface with no ABI promise; nothing touches struct net_device or net_device_ops. Assisted-by: LLM Signed-off-by: Omar Ramadan --- include/net/amt_offload.h | 263 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 include/net/amt_offload.h diff --git a/include/net/amt_offload.h b/include/net/amt_offload.h new file mode 100644 index 000000000..3be5d129c --- /dev/null +++ b/include/net/amt_offload.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * AMT relay replication offload: the contract between the amt driver and + * a provider that replicates relay multicast data to gateways in hardware + * (or, for testing, in a simulated device). + * + * This is an IN-KERNEL INTERFACE WITH NO ABI PROMISE. It may change in any + * release, like every other in-kernel API; out-of-tree providers get no + * stability guarantee. + * + * Registration is keyed by the lower netdev a relay transmits on (its + * IFLA_AMT_LINK stream device). Nothing here adds a field or an ndo to + * struct net_device. + */ +#ifndef _NET_AMT_OFFLOAD_H_ +#define _NET_AMT_OFFLOAD_H_ + +#include +#include +#include + +struct net_device; +struct sk_buff; + +/* Outer transport families, for &amt_offload_caps.families. */ +#define AMT_OFFLOAD_FAMILY_V4 BIT(0) +#define AMT_OFFLOAD_FAMILY_V6 BIT(1) + +/* + * Reserved: (S,G)-keyed forwarding. v1 forwards per (family, group) exactly + * as the software path does, so this bit has NO v1 semantics. A provider + * must not set it; amt ignores it. + */ +#define AMT_OFFLOAD_F_SOURCE_FILTER BIT(0) + +/** + * enum amt_offload_path - where a provider replicates + * @AMT_OFFLOAD_PATH_TX: amt hands the provider each relay data packet of + * a group whose commit it has published (&amt_offload_ops.group_commit), + * before its software fan-out (&amt_offload_ops.xmit), while the binding + * admits packets and amt has not lost its desired state. amt withdraws a + * group without a callback when a member leaves, until it publishes the + * recommit. Every other packet goes straight to software. + * @AMT_OFFLOAD_PATH_INGRESS: the provider replicates on ingress and the + * host only sees punted packets. Semantics only until a real provider + * exists; no simulated provider implements it. + */ +enum amt_offload_path { + AMT_OFFLOAD_PATH_TX, + AMT_OFFLOAD_PATH_INGRESS, +}; + +/** + * struct amt_offload_caps - what a bound provider can do + * @families: AMT_OFFLOAD_FAMILY_* outer families it can encapsulate. A + * relay whose outer family is missing stays unbound (counted), with the + * outcome of a failed &amt_offload_ops.bind, except that amt first calls + * &amt_offload_ops.unbind, since that bind succeeded. + * @paths: BIT(enum amt_offload_path) it implements. + * @op_timeout_ms: longest a programming op may take before amt treats the + * provider as wedged, resets it and replays from desired state. + * @features: AMT_OFFLOAD_F_* bits. None has v1 semantics. + * @max_endpoints: endpoint table size; 0 means unbounded. Informational: + * amt does not read it in this version. A provider out of room fails an + * &amt_offload_ops.endpoint_add for a key it does not hold, by + * convention with -ENOSPC. + * @max_groups: group table size; 0 means unbounded. Informational: amt + * does not read it in this version. A provider out of room fails the + * &amt_offload_ops.group_update that needs a new group, by convention + * with -ENOSPC. + * + * Filled by &amt_offload_ops.bind. + */ +struct amt_offload_caps { + u32 families; + u32 paths; + u32 op_timeout_ms; + u32 features; + u32 max_endpoints; + u32 max_groups; +}; + +/** + * struct amt_offload_binding - one relay device bound to one provider + * @lower: the registered lower netdev (the relay's stream device). + * @dev: the amt relay netdev. + * @epoch: monotonic per relay device, bumped on every bind, reset and + * teardown. A completion carrying an older epoch is stale and ignored. + * @priv: provider-owned; amt never touches it. + * + * Allocated and freed by amt. Valid from &amt_offload_ops.bind until + * &amt_offload_ops.unbind returns. + */ +struct amt_offload_binding { + struct net_device *lower; + struct net_device *dev; + u64 epoch; + void *priv; +}; + +/** + * struct amt_offload_endpoint_key - one gateway's outer endpoint + * @family: AF_INET or AF_INET6, the relay's outer family. + * @addr: gateway outer address. + * @addr.ip4: IPv4 address when @family is AF_INET. + * @addr.ip6: IPv6 address when @family is AF_INET6. + * @port: gateway outer UDP port (tunnel identity is address + port). + */ +struct amt_offload_endpoint_key { + u8 family; + union { + __be32 ip4; + struct in6_addr ip6; + } addr; + __be16 port; +}; + +/** + * struct amt_offload_group_key - one replicated group + * @family: AF_INET or AF_INET6, the INNER group family. + * @group: group address. + * @group.ip4: IPv4 group when @family is AF_INET. + * @group.ip6: IPv6 group when @family is AF_INET6. + * + * Forwarding is per (family, group), never per source: a gateway holding + * an INCLUDE(S1) membership still receives S2, exactly as the software + * fan-out does today. + */ +struct amt_offload_group_key { + u8 family; + union { + __be32 ip4; + struct in6_addr ip6; + } group; +}; + +/* Provider-owned table entries. amt only stores the pointers. */ +struct amt_offload_endpoint; +struct amt_offload_group; + +/** + * enum amt_offload_result - outcome of &amt_offload_ops.xmit + * @AMT_OFFLOAD_FALLBACK: skb untouched; amt runs its software fan-out. + * @AMT_OFFLOAD_CONSUMED: the provider handled every copy of this packet + * under the generation it was given and owns the skb; amt emits none and + * retries none. A copy that failed is the provider's to count, except one + * amt_offload_encap_xmit() could not copy or route, which amt has already + * counted in the relay's tx_dropped. + * @AMT_OFFLOAD_BUSY: skb untouched; amt runs one synchronous software pass + * and counts it. Never NETDEV_TX_BUSY: the relay is IFF_NO_QUEUE. + * + * Exactly one actor handles each packet, so no packet is duplicated. + */ +enum amt_offload_result { + AMT_OFFLOAD_FALLBACK, + AMT_OFFLOAD_CONSUMED, + AMT_OFFLOAD_BUSY, +}; + +/** + * struct amt_offload_stats - provider-side counters + * @tx_packets: copies the provider emitted. + * @tx_bytes: bytes of those copies on the wire. + * @tx_dropped: copies the provider failed to emit. + */ +struct amt_offload_stats { + u64 tx_packets; + u64 tx_bytes; + u64 tx_dropped; +}; + +/** + * struct amt_offload_ops - provider callbacks + * @bind: a relay transmitting on the registered lower device starts using + * this provider. Fill @caps. Process context, RTNL held. Nonzero leaves + * the relay unbound (counted): in software in auto mode; in required + * mode refused if it is being created, else without carrier. + * @unbind: last call for a binding. Every endpoint and group has already + * been deleted or disabled. Free @b->priv. RTNL held. + * @endpoint_add: program one gateway endpoint. Idempotent: an identical + * key returns the existing handle. 0 means accepted and durable, not yet + * forwarding. Process context, never under amt's spinlocks. + * @endpoint_del: remove an endpoint no group references any more. + * @group_update: stage the member set of one group under generation @gen, + * allocating *@grp on first use. Not forwarding until @group_commit. + * @group_commit: start forwarding generation @gen of @grp. amt hands @gen + * to @xmit only after this returns 0, and may never do so: an overrun + * resets the provider, and on TX a member leaving meanwhile withholds + * the commit until the next one. Until amt publishes @gen, any @xmit for + * @grp carries an earlier generation this provider committed, and a call + * already in flight may carry any of those or @gen itself; the provider + * serves such a generation or declines it. A provider must never emit + * one packet to one endpoint under two generations. On failure the group + * stays in software and previously added endpoints stay valid. + * @group_disable: stop forwarding @grp and free it. + * @xmit: TX path. Replicate @skb for @grp under @gen or decline; see + * &enum amt_offload_result. Softirq context, under rcu_read_lock(). + * @flush: drop every endpoint and group (reset after a timeout). amt then + * replays from its own desired state under a new epoch, except when + * the op that overran was a @group_disable or @endpoint_del of amt + * giving everything back -- at teardown, where @unbind follows, or + * after its desired state was lost: then nothing is replayed and the + * epoch does not move. + * @stats: fill @st. Optional; amt does not call it in this version. + * + * Teardown order, on unregistration of the lower device and on + * amt_offload_unregister(): amt stops admitting packets to @xmit, waits for + * in-flight @xmit calls, cancels its programming work, calls @group_disable + * for every group, @endpoint_del for every endpoint, then @unbind, then + * synchronize_rcu(); the relay is back in software afterwards. If one of + * those @group_disable/@endpoint_del calls overruns op_timeout_ms, amt + * calls @flush once instead of the rest, then @unbind, with no replay. + */ +struct amt_offload_ops { + int (*bind)(struct amt_offload_binding *b, + struct amt_offload_caps *caps); + void (*unbind)(struct amt_offload_binding *b); + int (*endpoint_add)(struct amt_offload_binding *b, + const struct amt_offload_endpoint_key *key, + struct amt_offload_endpoint **ep); + void (*endpoint_del)(struct amt_offload_binding *b, + struct amt_offload_endpoint *ep); + int (*group_update)(struct amt_offload_binding *b, + const struct amt_offload_group_key *key, + struct amt_offload_endpoint *const *members, + unsigned int nr_members, u64 gen, + struct amt_offload_group **grp); + int (*group_commit)(struct amt_offload_binding *b, + struct amt_offload_group *grp, u64 gen); + void (*group_disable)(struct amt_offload_binding *b, + struct amt_offload_group *grp); + enum amt_offload_result (*xmit)(struct amt_offload_binding *b, + struct amt_offload_group *grp, + struct sk_buff *skb, u64 gen); + void (*flush)(struct amt_offload_binding *b); + void (*stats)(struct amt_offload_binding *b, + struct amt_offload_stats *st); +}; + +/** + * amt_offload_register - offer a provider for relays on @lower + * @lower: the netdev the relays transmit on. + * @ops: provider callbacks; must outlive the registration. + * + * Every relay already using @lower in auto or required mode tries to bind + * now. Caller holds RTNL. + * + * Return: 0, -EEXIST if @lower already has a provider, or -ENOMEM. + */ +int amt_offload_register(struct net_device *lower, + const struct amt_offload_ops *ops); + +/** + * amt_offload_unregister - withdraw the provider for @lower + * @lower: as passed to amt_offload_register(). + * + * Tears every binding down in the documented order and returns with every + * relay on @lower in software. Caller holds RTNL. A no-op if @lower has no + * provider, which is the case after @lower itself was unregistered. + */ +void amt_offload_unregister(struct net_device *lower); + +#endif /* _NET_AMT_OFFLOAD_H_ */ -- 2.43.0