From nobody Sat Jul 25 01:24:45 2026 Received: from mx0a-0016f401.pphosted.com (mx0a-0016f401.pphosted.com [67.231.148.174]) (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 61DC93BADA2; Tue, 21 Jul 2026 08:18:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.148.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621924; cv=none; b=op+83ZlJ1V+BYrVEmqtp/vKN9j7uAdCyKJ7/Gp8doxVgW82zx5Y8FICP9xhmYkAS97TTmeB8smExCVhZafd+HA3VqBzrdUckEUkTow7wQ/Ser2EJuDjGabElstN9a7OEyllvfM2Ku1I26fCWzk7rzDLmqkKP7Qaa2zd6PdzmD+M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621924; c=relaxed/simple; bh=ufvE2796D3nuGPETXS3fQ3xEmsiUrO6mzJBmlm1hUgY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HQD9VPpIHoEptzmMx2tpVhLXYFo68w3yz2HFLRs+VeyHsc/NAv5k7nms+dL081QBwXeplcR3QvPcqirkX6taA9h2rF2U0Gal/jFTPAEnsdC0osW+L///pE2fIoAvdY4Xp9lfsOKoeMLMAYK50KsUQrQ6wDXmFQQTt04Nw69JbKg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=F810KhYJ; arc=none smtp.client-ip=67.231.148.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="F810KhYJ" Received: from pps.filterd (m0431384.ppops.net [127.0.0.1]) by mx0a-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66KNcE9e3014043; Tue, 21 Jul 2026 01:18:34 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=1 RaxVCCST8gQ8REO8hRwPEqKn22142xTswKZTicf9Q8=; b=F810KhYJgnqXoC2HE g4HSltNs9iP8qo4huByhoLOulNdPHv05wuBcpt1Y7n7TBBPm6fh8wJDfW6y72YL1 ROln/Pf6SY4P3tF3Np7i3vIpy1blvnIEU/zSkc4aHCDokqstN1jlHX5FlxGzG740 zlrblmMXdKjmb1WDeZlh3xBq2qju5MKgNCPBcPTOZniK5h8KLLJbkQMJnhVeQxWI fX3tpj+l4SI87G5zze77qz5Yf7bL0sxLN66NuGLub10ocmK7tQdUIuNvsbQLg5eg EkYKgaNYqMKQ/aTUFFFOmtWciYaxD2D2sCT/vhoGf+fGh2rVt2py3ydUSf6ClrjJ UNkNQ== Received: from dc5-exch05.marvell.com ([199.233.59.128]) by mx0a-0016f401.pphosted.com (PPS) with ESMTPS id 4fhhevkt5a-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:33 -0700 (PDT) Received: from DC5-EXCH05.marvell.com (10.69.176.209) by DC5-EXCH05.marvell.com (10.69.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:32 -0700 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH05.marvell.com (10.69.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:32 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 7B38B3F7070; Tue, 21 Jul 2026 01:18:30 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 1/9] octeontx2-af: switch: Add AF to switch mbox and skeleton files Date: Tue, 21 Jul 2026 13:48:16 +0530 Message-ID: <20260721081824.1430607-2-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-GUID: U1WJwBVOxNEtWQ6r0xvU2qAycm3Jq4PO X-Proofpoint-ORIG-GUID: U1WJwBVOxNEtWQ6r0xvU2qAycm3Jq4PO X-Authority-Analysis: v=2.4 cv=PN0/P/qC c=1 sm=1 tr=0 ts=6a5f2b59 cx=c_pps a=rEv8fa4AjpPjGxpoe8rlIQ==:117 a=rEv8fa4AjpPjGxpoe8rlIQ==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=TtqV-g6YmW1Jfm2GSLaY:22 a=M5GUcnROAAAA:8 a=ANdAxFAa8r43KH1w-rAA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfXx8uA6eh73BUv ZX93XK5bUIWC+dhI25LVz25N1Ft90lJzQrMFWPbsCHpGvwTOpr/xPZo4MfqdhmeA9yxVoTNu2gI KmcZMq0HDNfI1ST+WctEuGMOsSxSjpCmYea7f272d9qm0n7I25R+IK4hXS2Y+IojALkBf07j1dM Bhu1wsFcqx5+LhRjqa3DxjyLNzfWbeALJlcHCdY4JN3haXtz6IX69NRsPVjOPpuBiIIayS8tj7g cDLo7DU/eSJmvsOX2n22zaVcs5QeYktH0LnSCQuvpOqwPzE5b6shRBu8OevSYmqFYxUUdaS6P5s B8JnSx3CsRreXUmg2w8HjZfwNqDYmGtm2Q6mQdIUEWzy20oUz1pAJXMCAouLI4vqLc0HRc0grdn RypUhNKdEpwhnYTHkUtAuJAfiAynnePxihYGsoLLgm1rNbD7H0WOyYKFDlqoES6A46OTf89Nzk7 4ZjGCCeMhROgHRKpScQ== X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfXwCUhtvwC/n+W BFT5mk3Vxe/pyoIOZS45DYBm3HJ0DTIE+XdajJzX+yiL6FqRkbcUsPh5fznVsVbw7EOuy89W+2W KEzAE9oDcB20YdXIlnQmVgDK143Io/I= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" The Marvell switch hardware runs on a Linux OS. This OS receives various messages, which are parsed to create flow rules that can be installed on HW. The switch is capable of accelerating both L2 and L3 flows. This commit adds mailbox messages used by the Linux OS (on arm64) to send events to the switch hardware, along with skeleton handler functions: fdb messages: Linux bridge FDB messages fib messages: Linux routing table messages fl messages: Flow acceleration tuple and actions status messages: Packet status updates sent to Host Linux to keep connection-tracked flows active fl_tuple defines the flow acceleration match tuple exchanged over the mailbox. It currently carries IPv4 five-tuple and L2 match fields only. IPv6 flow acceleration is not supported in this patch and will be added in a follow-up change extending the mailbox ABI. Signed-off-by: Ratheesh Kannoth --- .../ethernet/marvell/octeontx2/af/Makefile | 3 +- .../net/ethernet/marvell/octeontx2/af/mbox.h | 107 ++++++++++++++++++ .../marvell/octeontx2/af/switch/rvu_sw_fl.c | 21 ++++ .../marvell/octeontx2/af/switch/rvu_sw_fl.h | 11 ++ .../marvell/octeontx2/af/switch/rvu_sw_l2.c | 14 +++ .../marvell/octeontx2/af/switch/rvu_sw_l2.h | 11 ++ .../marvell/octeontx2/af/switch/rvu_sw_l3.c | 14 +++ .../marvell/octeontx2/af/switch/rvu_sw_l3.h | 11 ++ 8 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= _fl.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= _fl.h create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= _l2.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= _l2.h create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= _l3.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= _l3.h diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/n= et/ethernet/marvell/octeontx2/af/Makefile index 91b7d6e96a61..82dd387308c9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile @@ -3,7 +3,7 @@ # Makefile for Marvell's RVU Admin Function driver # =20 -ccflags-y +=3D -I$(src) +ccflags-y +=3D -I$(src) -I$(src)/switch/ obj-$(CONFIG_OCTEONTX2_MBOX) +=3D rvu_mbox.o obj-$(CONFIG_OCTEONTX2_AF) +=3D rvu_af.o =20 @@ -12,5 +12,6 @@ rvu_af-y :=3D cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \ rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \ rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \ rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \ + switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o\ rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \ cn20k/npa.o cn20k/npc.o diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net= /ethernet/marvell/octeontx2/af/mbox.h index f87cdf1b971d..2867da47d9f5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -164,6 +164,14 @@ M(PTP_GET_CAP, 0x00c, ptp_get_cap, msg_req, ptp_get_c= ap_rsp) \ M(GET_REP_CNT, 0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp) \ M(ESW_CFG, 0x00e, esw_cfg, esw_cfg_req, msg_rsp) \ M(REP_EVENT_NOTIFY, 0x00f, rep_event_notify, rep_event, msg_rsp) \ +M(FDB_NOTIFY, 0x010, fdb_notify, \ + fdb_notify_req, msg_rsp) \ +M(FIB_NOTIFY, 0x011, fib_notify, \ + fib_notify_req, msg_rsp) \ +M(FL_NOTIFY, 0x012, fl_notify, \ + fl_notify_req, msg_rsp) \ +M(FL_GET_STATS, 0x013, fl_get_stats, \ + fl_get_stats_req, fl_get_stats_rsp) \ /* CGX mbox IDs (range 0x200 - 0x3FF) */ \ M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \ M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \ @@ -1807,6 +1815,105 @@ struct rep_event { struct rep_evt_data evt_data; }; =20 +#define FDB_ADD BIT_ULL(0) +#define FDB_DEL BIT_ULL(1) +#define FIB_CMD BIT_ULL(2) +#define FL_ADD BIT_ULL(3) +#define FL_DEL BIT_ULL(4) +#define DP_ADD BIT_ULL(5) + +struct fdb_notify_req { + struct mbox_msghdr hdr; + u64 flags; + u8 mac[ETH_ALEN]; +}; + +struct fib_entry { + u64 cmd; + u64 gw_valid : 1; + u64 mac_valid : 1; + u64 vlan_valid: 1; + u64 host : 1; + u64 bridge : 1; + u64 ipv6 : 1; + __be16 vlan_tag; + u32 dst_len; + u8 dst6_plen; + u8 gw6_plen; + union { + __be32 dst; + __be32 dst6[4]; + }; + union { + __be32 gw; + __be32 gw6[4]; + }; + u16 port_id; + u8 nud_state; + u8 mac[ETH_ALEN]; +}; + +struct fib_notify_req { + struct mbox_msghdr hdr; + u16 cnt; + u16 rsvd[3]; /* explicit padding for entry[] 8-byte alignment */ + struct fib_entry entry[16]; +}; + +struct fl_tuple { + __be32 ip4src; + __be32 m_ip4src; + __be32 ip4dst; + __be32 m_ip4dst; + __be16 sport; + __be16 m_sport; + __be16 dport; + __be16 m_dport; + __be16 eth_type; + __be16 m_eth_type; + u8 proto; + u8 smac[6]; + u8 m_smac[6]; + u8 dmac[6]; + u8 m_dmac[6]; + u64 is_xdev_br : 1; + u64 is_indev_br : 1; + u64 uni_di : 1; + u16 in_pf; + u16 xmit_pf; + u16 rsvd; + u64 features; + struct { /* FLOW_ACTION_MANGLE */ + u8 offset; + u8 type; + u16 rsvd; + u32 mask; + u32 val; +#define MANGLE_ARR_SZ 9 + } mangle[MANGLE_ARR_SZ]; /* 2 for ETH, 1 for VLAN, 4 for IPv6, 2 for L4. = */ +#define MANGLE_LAYER_CNT 4 + u8 mangle_map[MANGLE_LAYER_CNT]; /* 1 for ETH, 1 for VLAN, 1 for L3, 1 f= or L4 */ + u8 mangle_cnt; +}; + +struct fl_notify_req { + struct mbox_msghdr hdr; + u64 cookie; + u64 flags; + u64 features; + struct fl_tuple tuple; +}; + +struct fl_get_stats_req { + struct mbox_msghdr hdr; + u64 cookie; +}; + +struct fl_get_stats_rsp { + struct mbox_msghdr hdr; + u64 pkts_diff; +}; + struct flow_msg { unsigned char dmac[6]; unsigned char smac[6]; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c new file mode 100644 index 000000000000..1f8b82a84a5d --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "rvu.h" + +int rvu_mbox_handler_fl_get_stats(struct rvu *rvu, + struct fl_get_stats_req *req, + struct fl_get_stats_rsp *rsp) +{ + return 0; +} + +int rvu_mbox_handler_fl_notify(struct rvu *rvu, + struct fl_notify_req *req, + struct msg_rsp *rsp) +{ + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h new file mode 100644 index 000000000000..cf3e5b884f77 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#ifndef RVU_SW_FL_H +#define RVU_SW_FL_H + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c new file mode 100644 index 000000000000..5f805bfa81ed --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "rvu.h" + +int rvu_mbox_handler_fdb_notify(struct rvu *rvu, + struct fdb_notify_req *req, + struct msg_rsp *rsp) +{ + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h new file mode 100644 index 000000000000..ff28612150c9 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#ifndef RVU_SW_L2_H +#define RVU_SW_L2_H + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c new file mode 100644 index 000000000000..2b798d5f0644 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "rvu.h" + +int rvu_mbox_handler_fib_notify(struct rvu *rvu, + struct fib_notify_req *req, + struct msg_rsp *rsp) +{ + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h new file mode 100644 index 000000000000..ac8c4f9ba5ac --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#ifndef RVU_SW_L3_H +#define RVU_SW_L3_H + +#endif --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 A91C73BAD9D; Tue, 21 Jul 2026 08:18:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621930; cv=none; b=Bweym2bBZa0PkYmyaH1+mDe2+3eK9gYYhBsYk+Q05Hsl36rKdH+GGXEhz+MGjJjkSVGzi6Xj1lPkk9KNUnaI3dP7LxtGrxKQffbTmNnTyA6I5rdVKgQV8Q/i9513FOelGqL7qBt39kyWLXLCLJK/z4uyfUP5nX9CEnbJlqYEWz0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621930; c=relaxed/simple; bh=bJ7nB6dpP/cHngUYhJ+Wod1Ch6Qi1Co15YkkMvEB8y8=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=EEHVvyU9ZwDbAzbijHD7iMQzd49v4ue3LZpIzwdaBvM6eUJB3elsjLe2b9M+GcXj4gG3iRIN8NCbzVql6XEj+e/7u+Eu9uDfnuDAV/qK3PjiVnfbjsHUujmf84VH0GiSgcbiR3E6FtYN31ZaYQbK4PllkF1pcOq3cgAp8A9fClo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=YOOLbjnq; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="YOOLbjnq" Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66L7BWeG1527583; Tue, 21 Jul 2026 01:18:38 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=N rL1E677P29j5sj/MGmArIh+d4VqVCSnSPC08BInJio=; b=YOOLbjnqbdUbgdUqR o/f6bWTqdSTgUCUX7Go424sue17wKdpqgaOnUhYvMJDoDFMrSaUSDFoHIorIjE0/ ZayvdcCxqF6kBsZgfwsq5AXxAZljGzPyYZc91q0fZtSohvPJX+guSeAQZlQo3KMk SldZAaXaBnruW2SzaVZ/1ShY5Fu5lYqdZ5xPRJgbJoWF5zjL+1vH6dPPN+xZeTn/ pOnAwO2HOVxDTN7sT1pADVuYj1eNI0AsI4gXNYDH/1hDXODxrjmAmjtycPt0xWPH NX0yjvrOBVD1mNxIv729ekce3nC6bxnzZ6wkD1TpeFax4UijOPdg4SQFA0G1E51Y 9hbXg== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fgd7dfsxy-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:37 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:36 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:36 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 65FC63F7070; Tue, 21 Jul 2026 01:18:33 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 2/9] octeontx2-af: switch: Add switch dev to AF mboxes Date: Tue, 21 Jul 2026 13:48:17 +0530 Message-ID: <20260721081824.1430607-3-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX7CcOxmCGc6DJ 5itZlOyxOH4/IHifgkd9RFF4bpTdWXgdQR4MQsVPQZFRdJkcKt3rZsEvy+ZcbWj/4n7R26BqwZ0 1+/4qgWOdltyv7H5/1f5uB9bdwq4z74= X-Proofpoint-ORIG-GUID: vDphsOIs7dF6CktMJVLzu5FU-3avSfOU X-Proofpoint-GUID: vDphsOIs7dF6CktMJVLzu5FU-3avSfOU X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfXxuZz0itsgf6p AZwihf2xgo2gAxtjVX/assMOndhTuy8baRs+8zRzgT6yOVwb4TahXqRDyWAwYjNCjEVCoCx77Y+ /UVyrYra8jrpDunNXDAQNefeLvKbsft72RmPTK8RYtus62XyqLoe2k6ezaGINBCKXOX752eZbPR TO5t3RPuMkv2cOBMc10u2665rPXuhRDwt1/8WCH955VsBDgLwVVedxu4ctSWYOstv8Bfwxjjqjm /DEqLTKTEGTxqPHENedo0N4/2Bg2CeSzLwv+R4nr/MZzIWbPlnWOU3gMCoIaQrnPW/hlMozYilb i3qCbgR/+ZHDYh8eQbt/kj1eb2cwblQJQSSV8Yy46uV4PRKZcY1c2WIowjQLZc1D86fthSr8s89 EKTjDilF8DYde0GnZv0cW67pK8thAAsib+sv2gstAOrgRWjBYybV86EVD1zF43yRaA5Zv/4s6Na Pk24ktOHxQceonRhzdg== X-Authority-Analysis: v=2.4 cv=I9tVgtgg c=1 sm=1 tr=0 ts=6a5f2b5e cx=c_pps a=gIfcoYsirJbf48DBMSPrZA==:117 a=gIfcoYsirJbf48DBMSPrZA==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=QXcCYyLzdtTjyudCfB6f:22 a=M5GUcnROAAAA:8 a=g7MGyehL4oteeCUlPQQA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" The Marvell switch hardware runs on a Linux OS. Switch needs various information from AF driver. These mboxes are defined to query those from AF driver. Signed-off-by: Ratheesh Kannoth --- .../ethernet/marvell/octeontx2/af/Makefile | 2 +- .../net/ethernet/marvell/octeontx2/af/mbox.h | 126 ++++++++++++++++++ .../net/ethernet/marvell/octeontx2/af/rvu.c | 126 ++++++++++++++++++ .../net/ethernet/marvell/octeontx2/af/rvu.h | 1 + .../ethernet/marvell/octeontx2/af/rvu_nix.c | 9 +- .../ethernet/marvell/octeontx2/af/rvu_npc.c | 111 +++++++++++++++ .../marvell/octeontx2/af/rvu_npc_fs.c | 11 ++ .../marvell/octeontx2/af/switch/rvu_sw.c | 15 +++ .../marvell/octeontx2/af/switch/rvu_sw.h | 11 ++ 9 files changed, 408 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= .c create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw= .h diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/n= et/ethernet/marvell/octeontx2/af/Makefile index 82dd387308c9..73f20a44f1a0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile @@ -12,6 +12,6 @@ rvu_af-y :=3D cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \ rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \ rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \ rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \ - switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o\ + switch/rvu_sw.o switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl= .o \ rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \ cn20k/npa.o cn20k/npc.o diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net= /ethernet/marvell/octeontx2/af/mbox.h index 2867da47d9f5..23bc66ed854e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -172,6 +172,10 @@ M(FL_NOTIFY, 0x012, fl_notify, \ fl_notify_req, msg_rsp) \ M(FL_GET_STATS, 0x013, fl_get_stats, \ fl_get_stats_req, fl_get_stats_rsp) \ +M(IFACE_GET_INFO, 0x014, iface_get_info, msg_req, \ + iface_get_info_rsp) \ +M(SWDEV2AF_NOTIFY, 0x015, swdev2af_notify, \ + swdev2af_notify_req, msg_rsp) \ /* CGX mbox IDs (range 0x200 - 0x3FF) */ \ M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \ M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \ @@ -317,6 +321,14 @@ M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idx= s, \ M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \ msg_req, \ npc_get_pfl_info_rsp) \ +M(NPC_MCAM_FLOW_DEL_N_FREE, 0x6020, npc_flow_del_n_free, \ + npc_flow_del_n_free_req, msg_rsp) \ +M(NPC_MCAM_GET_MUL_STATS, 0x6021, npc_mcam_mul_stats, \ + npc_mcam_get_mul_stats_req, \ + npc_mcam_get_mul_stats_rsp) \ +M(NPC_MCAM_GET_FEATURES, 0x6022, npc_mcam_get_features, \ + msg_req, \ + npc_mcam_get_features_rsp) \ /* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \ M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \ nix_lf_alloc_req, nix_lf_alloc_rsp) \ @@ -446,6 +458,12 @@ M(MCS_INTR_NOTIFY, 0xE00, mcs_intr_notify, mcs_intr_in= fo, msg_rsp) #define MBOX_UP_REP_MESSAGES \ M(REP_EVENT_UP_NOTIFY, 0xEF0, rep_event_up_notify, rep_event, msg_rsp) \ =20 +#define MBOX_UP_AF2SWDEV_MESSAGES \ +M(AF2SWDEV, 0xEF1, af2swdev_notify, af2swdev_notify_req, msg_rsp) + +#define MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES \ +M(AF2PF_FDB_REFRESH, 0xEF2, af2pf_fdb_refresh, af2pf_fdb_refresh_req, msg= _rsp) + enum { #define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name =3D _id, MBOX_MESSAGES @@ -453,6 +471,8 @@ MBOX_UP_CGX_MESSAGES MBOX_UP_CPT_MESSAGES MBOX_UP_MCS_MESSAGES MBOX_UP_REP_MESSAGES +MBOX_UP_AF2SWDEV_MESSAGES +MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES #undef M }; =20 @@ -1589,6 +1609,30 @@ struct npc_mcam_alloc_entry_rsp { u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES]; }; =20 +struct npc_flow_del_n_free_req { + struct mbox_msghdr hdr; + u16 cnt; + u16 entry[256]; /* Entry index to be freed */ +}; + +struct npc_mcam_get_features_rsp { + struct mbox_msghdr hdr; + u64 rx_features; + u64 tx_features; +}; + +struct npc_mcam_get_mul_stats_req { + struct mbox_msghdr hdr; + u16 cnt; + u16 entry[256]; /* mcam entry */ +}; + +struct npc_mcam_get_mul_stats_rsp { + struct mbox_msghdr hdr; + u16 cnt; + u64 stat[256]; /* counter stats */ +}; + struct npc_mcam_free_entry_req { struct mbox_msghdr hdr; u16 entry; /* Entry index to be freed */ @@ -1914,6 +1958,88 @@ struct fl_get_stats_rsp { u64 pkts_diff; }; =20 +struct af2swdev_notify_req { + struct mbox_msghdr hdr; + u64 flags; + u32 port_id; + u32 switch_id; + union { + struct { + u8 mac[6]; + }; + struct { + u8 cnt; + struct fib_entry entry[16]; + }; + + struct { + u64 cookie; + u64 features; + struct fl_tuple tuple; + }; + }; +}; + +struct af2pf_fdb_refresh_req { + struct mbox_msghdr hdr; + u16 pcifunc; + u8 mac[6]; +}; + +struct iface_info { + u8 is_vf : 1; + u8 is_sdp : 1; + u8 rsvd : 6; + u16 pcifunc; + u16 rx_chan_base; + u16 tx_chan_base; + u16 sq_cnt; + u16 cq_cnt; + u16 rq_cnt; + u8 rx_chan_cnt; + u8 tx_chan_cnt; + u8 tx_link; + u8 nix; +}; + +/* Max supported */ +#define IFACE_MAX (256 + 32) /* 32 PFs + 256 VFs */ + +struct iface_get_info_rsp { + struct mbox_msghdr hdr; + u16 cnt; + u8 truncated; + u8 rsvd[5]; + struct iface_info info[IFACE_MAX]; +}; + +struct fl_info { + u64 cookie; + u16 mcam_idx[2]; + u8 dis : 1; + u8 uni_di : 1; +}; + +struct swdev2af_notify_req { + struct mbox_msghdr hdr; + u64 msg_type; +#define SWDEV2AF_MSG_TYPE_FW_STATUS BIT_ULL(0) +#define SWDEV2AF_MSG_TYPE_REFRESH_FDB BIT_ULL(1) +#define SWDEV2AF_MSG_TYPE_REFRESH_FL BIT_ULL(2) + u16 pcifunc; + union { + bool fw_up; // FW_STATUS message + + u8 mac[ETH_ALEN]; // fdb refresh message + + struct { // fl refresh message + u8 cnt; + u8 rsvd[7]; + struct fl_info fl[64]; + }; + }; +}; + struct flow_msg { unsigned char dmac[6]; unsigned char smac[6]; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/= ethernet/marvell/octeontx2/af/rvu.c index ffba56ee8a60..a0ae0ccc1b2b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1990,6 +1990,132 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, s= truct msg_req *req, return 0; } =20 +static void rvu_iface_get_qcnts(struct rvu *rvu, struct rvu_pfvf *pfvf, + struct iface_info *info) +{ + mutex_lock(&rvu->rsrc_lock); + + info->sq_cnt =3D 0; + info->cq_cnt =3D 0; + info->rq_cnt =3D 0; + + /* Use each LF queue context size; bitmaps are sized to qsize longs. */ + if (pfvf->sq_ctx && pfvf->sq_bmap) + info->sq_cnt =3D bitmap_weight(pfvf->sq_bmap, pfvf->sq_ctx->qsize); + if (pfvf->cq_ctx && pfvf->cq_bmap) + info->cq_cnt =3D bitmap_weight(pfvf->cq_bmap, pfvf->cq_ctx->qsize); + if (pfvf->rq_ctx && pfvf->rq_bmap) + info->rq_cnt =3D bitmap_weight(pfvf->rq_bmap, pfvf->rq_ctx->qsize); + + mutex_unlock(&rvu->rsrc_lock); +} + +int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req, + struct iface_get_info_rsp *rsp) +{ + struct iface_info *info; + bool truncated =3D false; + struct rvu_pfvf *pfvf; + int pf, vf, numvfs; + int tot =3D 0; + u16 pcifunc; + u64 cfg; + + /* Read-only topology snapshot for switch software; any PF/VF may + * request it. Only channel and queue counts already visible to the + * requester through AF are reported. + */ + rsp->cnt =3D 0; + rsp->truncated =3D 0; + /* Preserve mbox_msghdr fields pre-filled by the mbox framework. */ + memset(rsp->info, 0, sizeof(rsp->info)); + info =3D rsp->info; + for (pf =3D 0; pf < rvu->hw->total_pfs; pf++) { + if (tot >=3D IFACE_MAX) { + truncated =3D true; + goto done; + } + + cfg =3D rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf)); + numvfs =3D (cfg >> 12) & 0xFF; + + /* Skip not enabled PFs */ + if (!(cfg & BIT_ULL(20))) + goto chk_vfs; + + /* If Admin function, check on VFs */ + if (cfg & BIT_ULL(21)) + goto chk_vfs; + + pcifunc =3D rvu_make_pcifunc(rvu->pdev, pf, 0); + pfvf =3D rvu_get_pfvf(rvu, pcifunc); + + /* Populate iff at least one Tx channel */ + if (!pfvf->tx_chan_cnt) + goto chk_vfs; + + info->is_vf =3D 0; + info->pcifunc =3D pcifunc; + info->rx_chan_base =3D pfvf->rx_chan_base; + info->rx_chan_cnt =3D pfvf->rx_chan_cnt; + info->tx_chan_base =3D pfvf->tx_chan_base; + info->tx_chan_cnt =3D pfvf->tx_chan_cnt; + info->tx_link =3D nix_get_tx_link(rvu, pcifunc); + if (is_sdp_pfvf(rvu, pcifunc)) + info->is_sdp =3D 1; + + rvu_iface_get_qcnts(rvu, pfvf, info); + + if (pfvf->nix_blkaddr =3D=3D BLKADDR_NIX0) + info->nix =3D 0; + else + info->nix =3D 1; + + info++; + tot++; + +chk_vfs: + for (vf =3D 0; vf < numvfs; vf++) { + if (tot >=3D IFACE_MAX) { + truncated =3D true; + goto done; + } + + pcifunc =3D rvu_make_pcifunc(rvu->pdev, pf, vf + 1); + pfvf =3D rvu_get_pfvf(rvu, pcifunc); + + if (!pfvf->tx_chan_cnt) + continue; + + info->is_vf =3D 1; + info->pcifunc =3D pcifunc; + info->rx_chan_base =3D pfvf->rx_chan_base; + info->rx_chan_cnt =3D pfvf->rx_chan_cnt; + info->tx_chan_base =3D pfvf->tx_chan_base; + info->tx_chan_cnt =3D pfvf->tx_chan_cnt; + info->tx_link =3D nix_get_tx_link(rvu, pcifunc); + if (is_sdp_pfvf(rvu, pcifunc)) + info->is_sdp =3D 1; + + rvu_iface_get_qcnts(rvu, pfvf, info); + + if (pfvf->nix_blkaddr =3D=3D BLKADDR_NIX0) + info->nix =3D 0; + else + info->nix =3D 1; + + info++; + + tot++; + } + } +done: + rsp->cnt =3D tot; + rsp->truncated =3D truncated; + + return 0; +} + int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req, struct free_rsrcs_rsp *rsp) { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/= ethernet/marvell/octeontx2/af/rvu.h index c5610f242687..73d2329b5c26 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -1158,6 +1158,7 @@ void rvu_program_channels(struct rvu *rvu); =20 /* CN10K NIX */ void rvu_nix_block_cn10k_init(struct rvu *rvu, struct nix_hw *nix_hw); +int nix_get_tx_link(struct rvu *rvu, u16 pcifunc); =20 /* CN10K RVU - LMT*/ void rvu_reset_lmt_map_tbl(struct rvu *rvu, u16 pcifunc); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/= net/ethernet/marvell/octeontx2/af/rvu_nix.c index 78667a0977c0..0c6b2b425534 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -32,7 +32,6 @@ static int nix_free_all_bandprof(struct rvu *rvu, u16 pci= func); static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_h= w, u32 leaf_prof); static const char *nix_get_ctx_name(int ctype); -static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc); =20 enum mc_tbl_sz { MC_TBL_SZ_256, @@ -906,6 +905,8 @@ static void nix_setup_lso(struct rvu *rvu, struct nix_h= w *nix_hw, int blkaddr) =20 static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf) { + mutex_lock(&rvu->rsrc_lock); + kfree(pfvf->rq_bmap); kfree(pfvf->sq_bmap); kfree(pfvf->cq_bmap); @@ -931,6 +932,8 @@ static void nix_ctx_free(struct rvu *rvu, struct rvu_pf= vf *pfvf) pfvf->rss_ctx =3D NULL; pfvf->nix_qints_ctx =3D NULL; pfvf->cq_ints_ctx =3D NULL; + + mutex_unlock(&rvu->rsrc_lock); } =20 static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr, @@ -2087,10 +2090,10 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int = blkaddr, rvu_write64(rvu, blkaddr, reg, 0x0); } =20 -static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc) +int nix_get_tx_link(struct rvu *rvu, u16 pcifunc) { - struct rvu_hwinfo *hw =3D rvu->hw; int pf =3D rvu_get_pf(rvu->pdev, pcifunc); + struct rvu_hwinfo *hw =3D rvu->hw; u8 cgx_id =3D 0, lmac_id =3D 0; =20 if (is_lbk_vf(rvu, pcifunc)) {/* LBK links */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/= net/ethernet/marvell/octeontx2/af/rvu_npc.c index 08b83de9beb4..9ba34c03db9c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -3544,6 +3544,46 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu = *rvu, return rc; } =20 +int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu, + struct npc_flow_del_n_free_req *mreq, + struct msg_rsp *rsp) +{ + struct npc_mcam_free_entry_req sreq =3D { 0 }; + struct npc_delete_flow_req dreq =3D { 0 }; + struct npc_delete_flow_rsp drsp =3D { 0 }; + u16 entry[256]; + int ret =3D 0, i; + bool err =3D false; + u16 cnt; + + sreq.hdr.pcifunc =3D mreq->hdr.pcifunc; + dreq.hdr.pcifunc =3D mreq->hdr.pcifunc; + + cnt =3D mreq->cnt; + if (!cnt || cnt > 256) { + dev_err(rvu->dev, "Invalid cnt=3D%u\n", cnt); + return -EINVAL; + } + + /* Snapshot shared mailbox memory before processing the request. */ + memcpy(entry, mreq->entry, cnt * sizeof(entry[0])); + + for (i =3D 0; i < cnt; i++) { + dreq.entry =3D entry[i]; + rvu_mbox_handler_npc_delete_flow(rvu, &dreq, &drsp); + + sreq.entry =3D entry[i]; + ret =3D rvu_mbox_handler_npc_mcam_free_entry(rvu, &sreq, rsp); + if (ret) { + dev_err(rvu->dev, "free entry error for i=3D%d entry=3D%d\n", + i, entry[i]); + err =3D true; + } + } + + return err ? -EINVAL : 0; +} + int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu, struct npc_mcam_read_entry_req *req, struct npc_mcam_read_entry_rsp *rsp) @@ -4398,6 +4438,77 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu= *rvu, return 0; } =20 +int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu, + struct npc_mcam_get_mul_stats_req *req, + struct npc_mcam_get_mul_stats_rsp *rsp) +{ + struct npc_mcam *mcam =3D &rvu->hw->mcam; + u16 req_cnt, index, cntr, mcam_entry; + u16 pcifunc =3D req->hdr.pcifunc; + int blkaddr, cnt =3D 0, i; + u16 entry[256]; + u64 regval; + u32 bank; + + req_cnt =3D req->cnt; + if (!req_cnt || req_cnt > 256) { + dev_err(rvu->dev, "%s invalid request cnt=3D%u\n", + __func__, req_cnt); + return -EINVAL; + } + + /* Snapshot shared mailbox memory before processing the request. */ + memcpy(entry, req->entry, req_cnt * sizeof(entry[0])); + + blkaddr =3D rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return NPC_MCAM_INVALID_REQ; + + mutex_lock(&mcam->lock); + + for (i =3D 0; i < req_cnt; i++) { + mcam_entry =3D npc_cn20k_vidx2idx(entry[i]); + + if (npc_mcam_verify_entry(mcam, pcifunc, mcam_entry)) { + mutex_unlock(&mcam->lock); + dev_err(rvu->dev, "%s invalid mcam index=3D%d\n", + __func__, entry[i]); + return -EINVAL; + } + + index =3D mcam_entry & (mcam->banksize - 1); + bank =3D npc_get_bank(mcam, mcam_entry); + + if (is_cn20k(rvu->pdev)) { + regval =3D rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index, + bank)); + rsp->stat[cnt] =3D regval; + cnt++; + continue; + } + + /* read MCAM entry STAT_ACT register */ + regval =3D rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, = bank)); + + if (!(regval & rvu->hw->npc_stat_ena)) { + rsp->stat[cnt] =3D 0; + cnt++; + continue; + } + + cntr =3D regval & 0x1FF; + + rsp->stat[cnt] =3D rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr)); + rsp->stat[cnt] &=3D BIT_ULL(48) - 1; + cnt++; + } + + rsp->cnt =3D cnt; + mutex_unlock(&mcam->lock); + return 0; +} + void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf) { struct npc_mcam *mcam =3D &rvu->hw->mcam; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drive= rs/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 91b5947dae06..09c7ee8571df 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1926,6 +1926,17 @@ static int npc_delete_flow(struct rvu *rvu, struct r= vu_npc_mcam_rule *rule, return rvu_mbox_handler_npc_mcam_dis_entry(rvu, &dis_req, &dis_rsp); } =20 +int rvu_mbox_handler_npc_mcam_get_features(struct rvu *rvu, + struct msg_req *req, + struct npc_mcam_get_features_rsp *rsp) +{ + struct npc_mcam *mcam =3D &rvu->hw->mcam; + + rsp->rx_features =3D mcam->rx_features; + rsp->tx_features =3D mcam->tx_features; + return 0; +} + int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu, struct npc_delete_flow_req *req, struct npc_delete_flow_rsp *rsp) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c new file mode 100644 index 000000000000..fe143ad3f944 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#include "rvu.h" + +int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu, + struct swdev2af_notify_req *req, + struct msg_rsp *rsp) +{ + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h new file mode 100644 index 000000000000..f28dba556d80 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#ifndef RVU_SWITCH_H +#define RVU_SWITCH_H + +#endif --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 BB0F63B9950; Tue, 21 Jul 2026 08:18:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621930; cv=none; b=pcjujBOhgpv+oGEN2xrUIddJZ3ks/zuPMZukaFNHFFVqwrYXYGdp3SAivAaTfi/lYDodpA1SD7ikOb7qtX8RSY6ub9IXXgMQvaaJQuUgNPmBN/wnYdIJovXZGKo5k25+KSO8nNp4FeZPvt/blgdru186rrRqs5IgyB6GySjVvgs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621930; c=relaxed/simple; bh=BfSvIy70qS3jR3ozQoDsogLt02uuULZuWC4wCWobQmE=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=eVaO0lH+Fw+N4zpykTkiCFqsYsukUlwHxJAtOYR8z7Z4mCss1y+DX7gX0j+pjEYhQOUi0L8k6/HEBIa65eKLJvp38QEXCzLlG8mtBWyKUmjhNjb3I31AkOsau7vMgwmIe4yw0cMuNWzF4mPRphdGakQNIPw3sLjJx2gV/nMezYw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=jJKXH2Vo; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="jJKXH2Vo" Received: from pps.filterd (m0431383.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66KNcWLb3069960; Tue, 21 Jul 2026 01:18:40 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=+ 87BJR41hP/wj7LyXw7z0tcdCQ0jjVOug+9qFxCrCLA=; b=jJKXH2VoPLw9DAPTy Y1UNSx2wKqW2VQOP0y4lm39B04Rhhg7HEUVP+5G4UXXKnohvCJHzUOYQajJ5uBpl iqW9Qw3UYWQ7rtqLmlDKHdDl2kE2zygVBUGJNkrIjw7XysZGHeSfgk1AbAQIVaqQ g2Iwl9AldR8wqojmzLDcYgxzVgeW83q/33ipleo44uz0uvoqMf/MrRnLiApgwN54 D5yLZVsMFS6APJUJHjnbSZw2t924wH+FU6UChv5fuBx62mx0WftNMX7bKxXABQ4I DF0iuHT2U6mnR9aiFKuJAawlCLIsnXhOLQAA3EduDZY3njgzffKjeBkEs3SfE2y8 CdyUg== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fhwdkh6ym-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:39 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:39 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:39 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 8292E3F7091; Tue, 21 Jul 2026 01:18:36 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 3/9] octeontx2-pf: switch: Add pf files hierarchy Date: Tue, 21 Jul 2026 13:48:18 +0530 Message-ID: <20260721081824.1430607-4-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: Skbs_FlJUVQi_6mFOBwyOTwUzgbAaTpw X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfXy4NPJiWY3jCf ZDIbBQcbnlzf5t8EwsU0GmF39w/g4MJhsNpBnae2Nqn32N+7L9NlDoENspvlQYcFr8fqjpzHS+J TyqCm6CGvR9eCyCXlKcq3bLpD+wmAcw= X-Authority-Analysis: v=2.4 cv=TrXWQjXh c=1 sm=1 tr=0 ts=6a5f2b5f cx=c_pps a=gIfcoYsirJbf48DBMSPrZA==:117 a=gIfcoYsirJbf48DBMSPrZA==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=qit2iCtTFQkLgVSMPQTB:22 a=M5GUcnROAAAA:8 a=Tbk3QSKcrk8eJG6noeMA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-GUID: Skbs_FlJUVQi_6mFOBwyOTwUzgbAaTpw X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX+kC1GydiN5X0 78a1ico+uUHUwo2Amwv4RWKSnPh1MDyOYh9jDSQ+YdybwN855ek2ZP2mt2k0vDflR4R5gO1Fnid 5G1rdKP265amwEf4tAk6yEM8dHhNZRjKvHtxtZKVkAeNN1VZcwcCaeG19vQz1SY1RwQuvlvUACn SYEPgb2gT5u7i0QCeG+lGnno0WIvJnqOpIQPHU0gVkqolSQJCOEwu6ZDmqXst5UsmfYie3rKknR h/ARW9XRC1+uYXC1ZTBXNPQoCg0YHynwhMnLZ6mqvT9jmDOp/zceXREIOrpuAwIFCyrXVZRzNch 8ngbOMvq4M7nKh75ozNGC0dZhbl422O5yEZ3cEG0g+qS1Kf8nkRstK05Zi+WTMLgPnRwKEJBhH8 WL2kidmb4IX5ppbc8vzebakXEdEUM6APDrJVmonxb5eALlZt9XLvMqIt7xH2NHHYFEJTScZJ8/C 4c3F+uOkh+8hahn7ZeQ== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" Adds CONFIG_OCTEONTX_SWITCH, links stub switch objects into the PF module, and introduces empty sw_* init/deinit and notifier hooks for later patches. Signed-off-by: Ratheesh Kannoth --- .../net/ethernet/marvell/octeontx2/Kconfig | 10 +++++++++ .../ethernet/marvell/octeontx2/nic/Makefile | 5 ++++- .../marvell/octeontx2/nic/switch/sw_fdb.c | 16 ++++++++++++++ .../marvell/octeontx2/nic/switch/sw_fdb.h | 13 ++++++++++++ .../marvell/octeontx2/nic/switch/sw_fib.c | 20 ++++++++++++++++++ .../marvell/octeontx2/nic/switch/sw_fib.h | 20 ++++++++++++++++++ .../marvell/octeontx2/nic/switch/sw_fl.c | 16 ++++++++++++++ .../marvell/octeontx2/nic/switch/sw_fl.h | 13 ++++++++++++ .../marvell/octeontx2/nic/switch/sw_nb.c | 21 +++++++++++++++++++ .../marvell/octeontx2/nic/switch/sw_nb.h | 20 ++++++++++++++++++ 10 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fd= b.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fd= b.h create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fi= b.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fi= b.h create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl= .c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl= .h create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb= .c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb= .h diff --git a/drivers/net/ethernet/marvell/octeontx2/Kconfig b/drivers/net/e= thernet/marvell/octeontx2/Kconfig index 47e549c581f0..e2fb6dd71078 100644 --- a/drivers/net/ethernet/marvell/octeontx2/Kconfig +++ b/drivers/net/ethernet/marvell/octeontx2/Kconfig @@ -28,6 +28,16 @@ config NDC_DIS_DYNAMIC_CACHING , NPA stack pages etc in NDC. Also locks down NIX SQ/CQ/RQ/RSS and NPA Aura/Pool contexts. =20 +config OCTEONTX_SWITCH + bool "Marvell OcteonTX2 switch driver" + depends on (64BIT && COMPILE_TEST) || ARM64 + depends on OCTEONTX2_PF + default n + help + This driver supports Marvell's OcteonTX2 switch. + Marvell SWITCH HW can offload L2, L3 flow. ARM core interacts + with Marvell SW HW thru mbox. + config OCTEONTX2_PF tristate "Marvell OcteonTX2 NIC Physical Function driver" select OCTEONTX2_MBOX diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/= net/ethernet/marvell/octeontx2/nic/Makefile index 883e9f4d601c..123b0af23abd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -9,7 +9,10 @@ obj-$(CONFIG_RVU_ESWITCH) +=3D rvu_rep.o =20 rvu_nicpf-y :=3D otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \ otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \ - otx2_devlink.o qos_sq.o qos.o otx2_xsk.o + otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \ + switch/sw_fdb.o switch/sw_fl.o +rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) +=3D switch/sw_nb.o switch/sw_fib.o + rvu_nicvf-y :=3D otx2_vf.o rvu_rep-y :=3D rep.o =20 diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c new file mode 100644 index 000000000000..6842c8d91ffc --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "sw_fdb.h" + +int sw_fdb_init(void) +{ + return 0; +} + +void sw_fdb_deinit(void) +{ +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h new file mode 100644 index 000000000000..d4314d6d3ee4 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#ifndef SW_FDB_H_ +#define SW_FDB_H_ + +void sw_fdb_deinit(void); +int sw_fdb_init(void); + +#endif // SW_FDB_H diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c new file mode 100644 index 000000000000..41a9c5fb58fa --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "sw_fib.h" + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + +int sw_fib_init(void) +{ + return 0; +} + +void sw_fib_deinit(void) +{ +} + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h new file mode 100644 index 000000000000..9b72e95f2dd3 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#ifndef SW_FIB_H_ +#define SW_FIB_H_ + +#include + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) +void sw_fib_deinit(void); +int sw_fib_init(void); +#else +static inline void sw_fib_deinit(void) {} +static inline int sw_fib_init(void) { return 0; } +#endif + +#endif /* SW_FIB_H_ */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c new file mode 100644 index 000000000000..36a2359a0a48 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "sw_fl.h" + +int sw_fl_init(void) +{ + return 0; +} + +void sw_fl_deinit(void) +{ +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h new file mode 100644 index 000000000000..cd018d770a8a --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#ifndef SW_FL_H_ +#define SW_FL_H_ + +void sw_fl_deinit(void); +int sw_fl_init(void); + +#endif // SW_FL_H diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c new file mode 100644 index 000000000000..243611835e3a --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include "sw_nb.h" + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + +int sw_nb_unregister(void) +{ + return 0; +} + +int sw_nb_register(void) +{ + return 0; +} + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h new file mode 100644 index 000000000000..73cc1e99b8ec --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#ifndef SW_NB_H_ +#define SW_NB_H_ + +#include + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) +int sw_nb_register(void); +int sw_nb_unregister(void); +#else +static inline int sw_nb_register(void) { return 0; } +static inline int sw_nb_unregister(void) { return 0; } +#endif + +#endif /* SW_NB_H_ */ --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 85EF43BADBD; Tue, 21 Jul 2026 08:18:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621932; cv=none; b=jxwCFX7bEK3b05zXcBq5wSDk+GXnWh/21zn7qoMWwOIzJv6g03k514w0fY8lto8fPIGYi4ppWJGMHolFrtGZgLgr/ZQm7zqlCbK7x8d6M+TzUYqNauJnmqHT5dbAvQno+yut9o1oEMDRzQ2O70/F0GswL1d8UzST+83Rvsc4YLg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621932; c=relaxed/simple; bh=qwMx4eHK2LjiQI9xB1u8RM/SQTyf/Gc36f4YFksHcSg=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=l9h/ObR7yO1/2iNJs3vgBrkt+FblI1aiKyBvyJNP0IYEyk4GrgdrgpMSkdufKJYnh7TNG4K4OC//3/wuG7SZJLKK5tH5vcx88RYQG3+GdJs21bplMGKxiA5voKh7w/mveG8qQZklPqwdx0DiADfQtuYj+Q41Q+1T1b1z+d3tPrY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=bpmGObzW; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="bpmGObzW" Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66KNd49h667097; Tue, 21 Jul 2026 01:18:43 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=F 8Yxgnvqcr4VTA175HlZ/YVdCfD1u5VUVlIFqitFFgk=; b=bpmGObzWwFNUkb4F3 E17Ud9Cuh+eh4RWxbJBKUBjvRBSj4438quYJXhCFLC8fc3zqFHC7MytoD1tSaLNt EGnbOA7ufkLx+9cdl+cSXdncurJ5+5iJnnTCvApsNr1bqsXJbX6EJJStv3XyY2Zi bH3+4IzPYfSBLliu6uEamZW8mBDjW2XRAK2pilrWC5myoOPeF0KQINigUxB2cAl3 rVB9L+pJqtwfWTafpMGIL7QNpYr54i0u+GXFCvNslS3ljkYittiBQdZ4iBt1koRb x3Bb20IMQ5Y+rOkvT/+GxX9oy3arSQ9Ysb0KbFx5p2+gePEOR1B+yfe6xh8ccGbF PkRIQ== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fgd7dfsy2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:42 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:42 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:42 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 6A94C3F7070; Tue, 21 Jul 2026 01:18:39 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 4/9] octeontx2-af: switch: Representor for switch port Date: Tue, 21 Jul 2026 13:48:19 +0530 Message-ID: <20260721081824.1430607-5-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX6etiO8Qhyo+n TIXwJEF6BCfZ+rApX92IsoQ66TihHUMBjLGIhzyl4k/KTjsyOUpM9xj7ZebWOnOy61DBG/jCac3 BSqoL1jJhgplAIavnnbNjGEBQOQOikE= X-Proofpoint-ORIG-GUID: n_ZkrP6d55qgkXF8-Ri__NgJGOhAM3Xo X-Proofpoint-GUID: n_ZkrP6d55qgkXF8-Ri__NgJGOhAM3Xo X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX6FzPrXx7mM4+ 4W7bdvkI8nrQ3pVANJmMU8iFJGUXvwf8CZ2b1Jed19ktIo7GTbqpAAikBXCS0/7YrBaC+IooVxi MUcpmcp0PbloPTBXXPByLD1vbPIfugwdCdNZpbc1Wau4BqrGjnAPg1IAEPiFidXllQ+lWuvLGxw BRRvpkwXshnymTrnJ47c56S9uPVD6LY0gDqW2q69JLJsDojerLckmsNIWk6+y2jhJuANok43vEr wxbXwhfYUtpuaU9rMQ1Z3HuwIr/2ov0eEqJ4IZRyRNk5NmVqwKko0S6TYQzxk4iiZ26WBvqVoXd G/RRUSxusIzg/nIly5fpN2vYM1gCK5QBI1vrbmxZzFZM4Z5Norwdpy2qfVN3MSdGryZ77U7L4tu 399OYkYe0jakS5syOoxGON521FRh5u5awu6ypHqeaomnN3URtHejxjmK447rKBGmHTwy1Ijd8qQ n5hwGS3QYvDkehgz59w== X-Authority-Analysis: v=2.4 cv=I9tVgtgg c=1 sm=1 tr=0 ts=6a5f2b62 cx=c_pps a=gIfcoYsirJbf48DBMSPrZA==:117 a=gIfcoYsirJbf48DBMSPrZA==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=QXcCYyLzdtTjyudCfB6f:22 a=M5GUcnROAAAA:8 a=vKW80V-kDxkLz87EuU0A:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" Extends esw_cfg with a devlink-derived switch id, copies it into rvu->rswitch on the AF, adds rvu_sw_port_id(), exports rvu_rep_get_vlan_id(). Signed-off-by: Ratheesh Kannoth --- .../net/ethernet/marvell/octeontx2/af/mbox.h | 1 + .../net/ethernet/marvell/octeontx2/af/rvu.h | 5 +++++ .../ethernet/marvell/octeontx2/af/rvu_rep.c | 15 ++++++++++++++- .../marvell/octeontx2/af/switch/rvu_sw.c | 19 +++++++++++++++++++ .../marvell/octeontx2/af/switch/rvu_sw.h | 5 +++++ .../net/ethernet/marvell/octeontx2/nic/rep.c | 4 ++++ 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net= /ethernet/marvell/octeontx2/af/mbox.h index 23bc66ed854e..cdfb5a8bafb9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -1834,6 +1834,7 @@ struct get_rep_cnt_rsp { struct esw_cfg_req { struct mbox_msghdr hdr; u8 ena; + unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; u64 rsvd; }; =20 diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/= ethernet/marvell/octeontx2/af/rvu.h index 73d2329b5c26..8cf1ad9ec749 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -576,6 +576,10 @@ struct rvu_switch { u16 *entry2pcifunc; u16 mode; u16 start_entry; + unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; +#define RVU_SWITCH_FLAG_FW_READY BIT_ULL(0) + u64 flags; + u16 pcifunc; }; =20 struct rep_evtq_ent { @@ -1197,4 +1201,5 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu); void rvu_rep_update_rules(struct rvu *rvu, u16 pcifunc, bool ena); int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable); int npc_mcam_verify_entry(struct npc_mcam *mcam, u16 pcifunc, int entry); +u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc); #endif /* RVU_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/= net/ethernet/marvell/octeontx2/af/rvu_rep.c index a2781e0f504e..0ee2fd935abf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c @@ -6,6 +6,7 @@ */ =20 #include +#include #include #include #include @@ -189,7 +190,7 @@ int rvu_mbox_handler_nix_lf_stats(struct rvu *rvu, return 0; } =20 -static u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc) +u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc) { int id; =20 @@ -429,6 +430,15 @@ int rvu_rep_pf_init(struct rvu *rvu) return 0; } =20 +static bool esw_cfg_req_has_switch_id(const struct esw_cfg_req *req) +{ + u16 msg_len =3D req->hdr.next_msgoff - + ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); + + return msg_len >=3D offsetof(struct esw_cfg_req, switch_id) + + MAX_PHYS_ITEM_ID_LEN; +} + int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct esw_cfg_req *req, struct msg_rsp *rsp) { @@ -436,6 +446,9 @@ int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct es= w_cfg_req *req, return 0; =20 rvu->rep_mode =3D req->ena; + if (esw_cfg_req_has_switch_id(req)) + memcpy(rvu->rswitch.switch_id, req->switch_id, + MAX_PHYS_ITEM_ID_LEN); =20 if (!rvu->rep_mode) rvu_npc_free_mcam_entries(rvu, req->hdr.pcifunc, -1); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c index fe143ad3f944..403d57870efe 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c @@ -5,7 +5,26 @@ * */ =20 +#include + #include "rvu.h" +#include "rvu_sw.h" + +u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc) +{ + u16 rep_id; + + if (!rvu->rep2pfvf_map || !rvu->rep_cnt) + return RVU_SW_INVALID_PORT_ID; + + rep_id =3D rvu_rep_get_vlan_id(rvu, pcifunc); + if (rep_id >=3D rvu->rep_cnt || + rvu->rep2pfvf_map[rep_id] !=3D pcifunc) + return RVU_SW_INVALID_PORT_ID; + + return FIELD_PREP(GENMASK_ULL(31, 16), rep_id) | + FIELD_PREP(GENMASK_ULL(15, 0), pcifunc); +} =20 int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu, struct swdev2af_notify_req *req, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h index f28dba556d80..e9ad32c84576 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h @@ -8,4 +8,9 @@ #ifndef RVU_SWITCH_H #define RVU_SWITCH_H =20 +/* RVU Switch */ +#define RVU_SW_INVALID_PORT_ID ((u32)~0U) + +u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc); + #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net= /ethernet/marvell/octeontx2/nic/rep.c index 0f5d5642d3f7..257a2ae6a53e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c @@ -399,8 +399,11 @@ static void rvu_rep_get_stats64(struct net_device *dev, =20 static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena) { + struct devlink_port_attrs attrs =3D {}; struct esw_cfg_req *req; =20 + rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id); + mutex_lock(&priv->mbox.lock); req =3D otx2_mbox_alloc_msg_esw_cfg(&priv->mbox); if (!req) { @@ -408,6 +411,7 @@ static int rvu_eswitch_config(struct otx2_nic *priv, u8= ena) return -ENOMEM; } req->ena =3D ena; + memcpy(req->switch_id, attrs.switch_id.id, attrs.switch_id.id_len); otx2_sync_mbox_msg(&priv->mbox); mutex_unlock(&priv->mbox.lock); return 0; --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 0BBFE3BBFBB; Tue, 21 Jul 2026 08:18:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621936; cv=none; b=ESDglEc2vl+4E6jX4WQDNQYRFuO0DSCcpX/l3TFJKVXXkh+DyNfj8BNbk0BbxuYPZKa9st12tIdRVMSW+v/ogFyUy4p52fVNBdPLhXcL1YNIWK/aJZMhWdejI6Dhla67brgpW04TPznupJ5yzzBPOGflavQuNPJZ0f/kU7lnK9o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621936; c=relaxed/simple; bh=EOVplbuDWQWQGjNkDXeba19+oFfqGEO/h19jL7jxUhE=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=epzXEpFT5qfYamm7DPJ7xryg12SveMocFcDU7a/Qa394KiNJlv4uj2xNbpNECA8GHYB8pMLYvynzC4KrKz7Bas5ZTKj2p4ph7heaRJUP/XHu1hUS7VTutevz8GuERbT1Lxt9OVeWVHsn+YoEUiF7c+C4+lDVEp0BRKlMIWnuK6Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=iF1w6P9l; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="iF1w6P9l" Received: from pps.filterd (m0431383.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66KNcWLd3069960; Tue, 21 Jul 2026 01:18:46 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=c JdM3RVyDuux7cFTWnOfeR1Ua7B1zb8sts8Ni33Yzqo=; b=iF1w6P9ldg0+/cDqX eLx1nG7GgRKxijo6PH8vra0XhuiHbJOJxY5rByT2fmFpr08E4hgIXA/yPwqvqqkE Z7Ofbf+VTUy+4FO8rITZq6wIJ3uZbmWHCsotzMpllL9AR9dI2zOrtnUPshJ1KrtE G/LuwLepNi5SVC91JvVraS9DrMS5Y7/c6Ai5x+XRjLTP3rr6551J6udFPb8URpdP OaOaISImLaWlz3qJ6SBSPMeZ6hasZOkhwYaKPYMnXur0zw1q4brqB6d1JpZiBPcv zUmiBqZMDGcO90P/wmWiHiMp7OBWn8Jog9lrnUfJVbvV2GrGWcZQMezJkCOMEjh3 txl+w== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fhwdkh6yr-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:45 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:45 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:44 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 522453F7070; Tue, 21 Jul 2026 01:18:42 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 5/9] octeontx2-af: switch: TL1 scheduling and NPC channel control Date: Tue, 21 Jul 2026 13:48:20 +0530 Message-ID: <20260721081824.1430607-6-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: -4DMyJS-4fTrlffPYaMeDs9sTHB4eVSf X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX/zfGafWj+av1 qHUcSwhJtsrQgJLVPgA55qDaxoblYwSra7zJE/E+CttwFY3UGTdlUtBp6bRhwxbuj5R27v+ZngA JyfN+qUpH2ExRjWmE8Rc4zhQvWnDM98= X-Authority-Analysis: v=2.4 cv=TrXWQjXh c=1 sm=1 tr=0 ts=6a5f2b65 cx=c_pps a=gIfcoYsirJbf48DBMSPrZA==:117 a=gIfcoYsirJbf48DBMSPrZA==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=qit2iCtTFQkLgVSMPQTB:22 a=M5GUcnROAAAA:8 a=b2e9Q_Z-T6RzkBTqyW8A:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-GUID: -4DMyJS-4fTrlffPYaMeDs9sTHB4eVSf X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX4TsWgPXOhG4w fXY9Mi5CG2AfMAE08kJ7LWWldsYtsBGc34F9zkfNHhnk+D5XOobZFEST/RjtvE2iwVYt7PipmK8 jl6go8CLvl9zYV81KQyT+Gi1lH5WgCB21U6u3LKk3rPJrFp+odeVqaSu5W5maUBq3mY0kUrOypx KEZo7JRUm+0vjHldf2raXUlGTNP6Fk4IYS3JkjeO1Sq7SjeAvabPzYxeGHyVAx0R7Xx+drV1VBG swgmVH6x6GTD0YWra8nXe+hx3jjkbMKeNY2HIWpXQSkZDv0aICnADWo88EY9fqvgJKYRQ8tjDNT hVQb9ZqI/JdySouKowelEaO5/e7lefBsZR6qCab7gYVQuOMNIqb2OmAHc1A8GSqXVgFAlXHtx3M +LXVjR5EtkQiKumKRfEfI3ubxfS3ZVXIE8y5+IH0Hfzz8/hdNNPnwelS1Yaxj0jJ1B06JDXcJts t5Mn02MH2fv87Qhrg3Q== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" Switch (PAN) mode needs more than one TL1 scheduler queue index so the hardware can steer traffic to different links according to NPC flow rules, not only the PF/VF default Tx link. Add NIX_TXSCH_ALLOC_FLAG_PAN to nix_txsch_alloc requests: use the PAN link index for scheduler range calculation, allow multiple TL1 queues when the aggregate level spans start..end, and allocate indices in that range. Add TXSCHQ_FREE_PAN_TL1 so TL1 entries in that path can be freed via nix_txsch_free where they were previously skipped. For NPC install flow, add set_chanmask so callers can keep a non-default chan_mask when the requester is not the AF; without it, chan_mask was always forced to 0xFFF for non-AF functions. Allocate the NIX LF SQ bitmap with the same span used by bitmap_weight(..., BITS_PER_LONG * 16) in rvu_get_hwinfo(). Extend struct sg_list with cq_idx and len for transmit-side metadata. Signed-off-by: Ratheesh Kannoth --- .../net/ethernet/marvell/octeontx2/af/mbox.h | 15 ++ .../net/ethernet/marvell/octeontx2/af/rvu.c | 25 ++- .../net/ethernet/marvell/octeontx2/af/rvu.h | 6 + .../ethernet/marvell/octeontx2/af/rvu_nix.c | 179 ++++++++++++++++-- .../marvell/octeontx2/af/rvu_npc_fs.c | 20 +- .../marvell/octeontx2/nic/otx2_txrx.h | 2 + 6 files changed, 216 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net= /ethernet/marvell/octeontx2/af/mbox.h index cdfb5a8bafb9..a63771d7b102 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -1158,6 +1158,13 @@ struct nix_txsch_alloc_req { /* Scheduler queue count request at each level */ u16 schq_contig[NIX_TXSCH_LVL_CNT]; /* No of contiguous queues */ u16 schq[NIX_TXSCH_LVL_CNT]; /* No of non-contiguous queues */ + /* Set only by the single switchdev PF (rvu->rswitch.pcifunc). This is + * not the eswitch representor (rvu->rep_pcifunc). That PF requests two + * aggregate-level TL2 queues on the PAN link, one for CGX and one for + * SDP steering. No other PF or VF sets this flag. + */ +#define NIX_TXSCH_ALLOC_FLAG_PAN BIT(0) + u32 flags; }; =20 struct nix_txsch_alloc_rsp { @@ -1176,6 +1183,10 @@ struct nix_txsch_alloc_rsp { struct nix_txsch_free_req { struct mbox_msghdr hdr; #define TXSCHQ_FREE_ALL BIT_ULL(0) + /* Frees PAN TL2 queues allocated with NIX_TXSCH_ALLOC_FLAG_PAN. Used + * only by the switchdev PF (rvu->rswitch.pcifunc), not by other PFs/VFs. + */ +#define TXSCHQ_FREE_PAN_TL1 BIT_ULL(1) u16 flags; /* Scheduler queue level to be freed */ u16 schq_lvl; @@ -2115,6 +2126,10 @@ struct npc_install_flow_req { u8 hw_prio; u8 req_kw_type; /* Key type to be written */ u8 alloc_entry; /* only for cn20k */ + /* When set, keep caller chan_mask instead of the CPT default. Only + * honored for the switchdev PF; see rvu_mbox_handler_npc_install_flow(). + */ + u8 set_chanmask; /* For now use any priority, once AF driver is changed to * allocate least priority entry instead of mid zone then make * NPC_MCAM_LEAST_PRIO as 3 diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/= ethernet/marvell/octeontx2/af/rvu.c index a0ae0ccc1b2b..168a50655351 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1990,18 +1990,29 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, s= truct msg_req *req, return 0; } =20 -static void rvu_iface_get_qcnts(struct rvu *rvu, struct rvu_pfvf *pfvf, - struct iface_info *info) +static void rvu_iface_get_qcnts(struct rvu *rvu, u16 pcifunc, + struct rvu_pfvf *pfvf, struct iface_info *info) { + int sq_bmap_bits; + mutex_lock(&rvu->rsrc_lock); =20 info->sq_cnt =3D 0; info->cq_cnt =3D 0; info->rq_cnt =3D 0; =20 - /* Use each LF queue context size; bitmaps are sized to qsize longs. */ - if (pfvf->sq_ctx && pfvf->sq_bmap) - info->sq_cnt =3D bitmap_weight(pfvf->sq_bmap, pfvf->sq_ctx->qsize); + if (pfvf->sq_bmap) { + /* Match switchdev sq_bmap allocation size in nix_lf_alloc(). */ + if (rvu_is_switch_pcifunc(rvu, pcifunc)) + sq_bmap_bits =3D NIX_SQ_BMAP_BITS; + else if (pfvf->sq_ctx) + sq_bmap_bits =3D pfvf->sq_ctx->qsize; + else + sq_bmap_bits =3D 0; + + if (sq_bmap_bits) + info->sq_cnt =3D bitmap_weight(pfvf->sq_bmap, sq_bmap_bits); + } if (pfvf->cq_ctx && pfvf->cq_bmap) info->cq_cnt =3D bitmap_weight(pfvf->cq_bmap, pfvf->cq_ctx->qsize); if (pfvf->rq_ctx && pfvf->rq_bmap) @@ -2064,7 +2075,7 @@ int rvu_mbox_handler_iface_get_info(struct rvu *rvu, = struct msg_req *req, if (is_sdp_pfvf(rvu, pcifunc)) info->is_sdp =3D 1; =20 - rvu_iface_get_qcnts(rvu, pfvf, info); + rvu_iface_get_qcnts(rvu, pcifunc, pfvf, info); =20 if (pfvf->nix_blkaddr =3D=3D BLKADDR_NIX0) info->nix =3D 0; @@ -2097,7 +2108,7 @@ int rvu_mbox_handler_iface_get_info(struct rvu *rvu, = struct msg_req *req, if (is_sdp_pfvf(rvu, pcifunc)) info->is_sdp =3D 1; =20 - rvu_iface_get_qcnts(rvu, pfvf, info); + rvu_iface_get_qcnts(rvu, pcifunc, pfvf, info); =20 if (pfvf->nix_blkaddr =3D=3D BLKADDR_NIX0) info->nix =3D 0; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/= ethernet/marvell/octeontx2/af/rvu.h index 8cf1ad9ec749..0662cc6134b0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -335,6 +335,7 @@ struct nix_txsch { u8 lvl; #define NIX_TXSCHQ_FREE BIT_ULL(1) #define NIX_TXSCHQ_CFG_DONE BIT_ULL(0) +#define NIX_SQ_BMAP_BITS (BITS_PER_LONG * 16) #define TXSCH_MAP_FUNC(__pfvf_map) ((__pfvf_map) & 0xFFFF) #define TXSCH_MAP_FLAGS(__pfvf_map) ((__pfvf_map) >> 16) #define TXSCH_MAP(__func, __flags) (((__func) & 0xFFFF) | ((__flags) <<= 16)) @@ -904,6 +905,11 @@ static inline bool is_pffunc_af(u16 pcifunc) return !pcifunc; } =20 +static inline bool rvu_is_switch_pcifunc(struct rvu *rvu, u16 pcifunc) +{ + return rvu->rswitch.pcifunc && pcifunc =3D=3D rvu->rswitch.pcifunc; +} + static inline bool is_rvu_fwdata_valid(struct rvu *rvu) { return (rvu->fwdata->header_magic =3D=3D RVU_FWDATA_HEADER_MAGIC) && diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/= net/ethernet/marvell/octeontx2/af/rvu_nix.c index 0c6b2b425534..e3e2f0113328 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -1052,6 +1052,7 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, s= truct nix_hw *nix_hw, u16 pcifunc =3D req->hdr.pcifunc; int nixlf, blkaddr, rc =3D 0; struct nix_aq_inst_s inst; + u64 sq_bmap_bits, max_q; struct rvu_block *block; struct admin_queue *aq; struct rvu_pfvf *pfvf; @@ -1086,10 +1087,25 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu,= struct nix_hw *nix_hw, if (!pfvf->rq_ctx || req->qidx >=3D pfvf->rq_ctx->qsize) rc =3D NIX_AF_ERR_AQ_ENQUEUE; break; - case NIX_AQ_CTYPE_SQ: - if (!pfvf->sq_ctx || req->qidx >=3D pfvf->sq_ctx->qsize) + case NIX_AQ_CTYPE_SQ: { + if (!pfvf->sq_ctx) { + rc =3D NIX_AF_ERR_AQ_ENQUEUE; + break; + } + + /* Switchdev PF uses a fixed sq_bmap (NIX_SQ_BMAP_BITS); cap qidx + * to that span so __set_bit() cannot run past the allocation. + * nix_lf_alloc() also rejects sq_cnt above NIX_SQ_BMAP_BITS. + */ + sq_bmap_bits =3D rvu_is_switch_pcifunc(rvu, pcifunc) ? + NIX_SQ_BMAP_BITS : + (u64)pfvf->sq_ctx->qsize * BITS_PER_LONG; + max_q =3D min_t(u64, pfvf->sq_ctx->qsize, sq_bmap_bits); + + if ((u64)req->qidx >=3D max_q) rc =3D NIX_AF_ERR_AQ_ENQUEUE; break; + } case NIX_AQ_CTYPE_CQ: if (!pfvf->cq_ctx || req->qidx >=3D pfvf->cq_ctx->qsize) rc =3D NIX_AF_ERR_AQ_ENQUEUE; @@ -1511,16 +1527,26 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, int nixlf, qints, hwctx_size, intf, rc =3D 0; u16 bcast, mcast, promisc, ucast; struct rvu_hwinfo *hw =3D rvu->hw; + u64 cfg, ctx_cfg, sq_bmap_bits; u16 pcifunc =3D req->hdr.pcifunc; bool rules_created =3D false; struct rvu_block *block; struct rvu_pfvf *pfvf; - u64 cfg, ctx_cfg; int blkaddr; =20 if (!req->rq_cnt || !req->sq_cnt || !req->cq_cnt) return NIX_AF_ERR_PARAM; =20 + /* Switchdev PF sq_bmap is fixed at NIX_SQ_BMAP_BITS; reject larger + * sq_cnt before allocating context memory or the bitmap. + */ + sq_bmap_bits =3D rvu_is_switch_pcifunc(rvu, pcifunc) ? + NIX_SQ_BMAP_BITS : + (u64)req->sq_cnt * BITS_PER_LONG; + + if ((u64)req->sq_cnt > sq_bmap_bits) + return NIX_AF_ERR_PARAM; + if (req->way_mask) req->way_mask &=3D 0xFFFF; =20 @@ -1600,7 +1626,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, if (rc) goto free_mem; =20 - pfvf->sq_bmap =3D kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL); + if (rvu_is_switch_pcifunc(rvu, pcifunc)) + /* Fixed-size bitmap; sq_cnt capped to NIX_SQ_BMAP_BITS above. */ + pfvf->sq_bmap =3D kcalloc(BITS_TO_LONGS(NIX_SQ_BMAP_BITS), + sizeof(long), GFP_KERNEL); + else + pfvf->sq_bmap =3D kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL); if (!pfvf->sq_bmap) { rc =3D -ENOMEM; goto free_mem; @@ -2127,6 +2158,25 @@ static void nix_get_txschq_range(struct rvu *rvu, u1= 6 pcifunc, } } =20 +static int nix_get_pan_tx_link(struct rvu *rvu) +{ + struct rvu_hwinfo *hw =3D rvu->hw; + + return hw->cgx_links + hw->lbk_links + 1; +} + +static bool nix_txsch_is_pan_schq(struct rvu *rvu, int schq) +{ + int pan_link =3D nix_get_pan_tx_link(rvu); + + return schq >=3D pan_link && schq <=3D pan_link + 1; +} + +static bool nix_txsch_pan_allowed(struct rvu *rvu, u16 pcifunc) +{ + return rvu_is_switch_pcifunc(rvu, pcifunc); +} + static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifun= c, struct nix_hw *nix_hw, struct nix_txsch_alloc_req *req) @@ -2142,12 +2192,27 @@ static int nix_check_txschq_alloc_req(struct rvu *r= vu, int lvl, u16 pcifunc, if (!req_schq) return 0; =20 - link =3D nix_get_tx_link(rvu, pcifunc); + if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) { + if (!nix_txsch_pan_allowed(rvu, pcifunc)) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + link =3D nix_get_pan_tx_link(rvu); + } else { + link =3D nix_get_tx_link(rvu, pcifunc); + } =20 /* For traffic aggregating scheduler level, one queue is enough */ if (lvl >=3D hw->cap.nix_tx_aggr_lvl) { - if (req_schq !=3D 1) + if (req_schq !=3D 1 && !(req->flags & NIX_TXSCH_ALLOC_FLAG_PAN)) return NIX_AF_ERR_TLX_ALLOC_FAIL; + if (req->schq[lvl] > MAX_TXSCHQ_PER_FUNC || + req->schq_contig[lvl] > MAX_TXSCHQ_PER_FUNC) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) { + if (link >=3D txsch->schq.max || link + 1 >=3D txsch->schq.max) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + if (req_schq > 2) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + } return 0; } =20 @@ -2176,9 +2241,9 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu= , int lvl, u16 pcifunc, return 0; } =20 -static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch, - struct nix_txsch_alloc_rsp *rsp, - int lvl, int start, int end) +static int nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch, + struct nix_txsch_alloc_rsp *rsp, + int lvl, int start, int end) { struct rvu_hwinfo *hw =3D rvu->hw; u16 pcifunc =3D rsp->hdr.pcifunc; @@ -2188,6 +2253,46 @@ static void nix_txsch_alloc(struct rvu *rvu, struct = nix_txsch *txsch, * on transmit link to which PF_FUNC is mapped to. */ if (lvl >=3D hw->cap.nix_tx_aggr_lvl) { + if (start !=3D end) { + int want_contig =3D rsp->schq_contig[lvl]; + int got_contig =3D 0, got =3D 0; + int want =3D rsp->schq[lvl]; + + for (schq =3D start; schq <=3D end; schq++) { + if (test_bit(schq, txsch->schq.bmap)) + continue; + + if (got_contig < want_contig) { + set_bit(schq, txsch->schq.bmap); + rsp->schq_contig_list[lvl][got_contig++] =3D schq; + continue; + } + + if (got < want) { + set_bit(schq, txsch->schq.bmap); + rsp->schq_list[lvl][got++] =3D schq; + } + } + + rsp->schq_contig[lvl] =3D got_contig; + rsp->schq[lvl] =3D got; + + if (got_contig < want_contig || got < want) { + for (idx =3D 0; idx < got_contig; idx++) + clear_bit(rsp->schq_contig_list[lvl][idx], + txsch->schq.bmap); + for (idx =3D 0; idx < got; idx++) + clear_bit(rsp->schq_list[lvl][idx], + txsch->schq.bmap); + rsp->schq_contig[lvl] =3D 0; + rsp->schq[lvl] =3D 0; + dev_err(rvu->dev, + "Could not allocate schq at lvl=3D%u start=3D%u end=3D%u\n", + lvl, start, end); + return -ENOMEM; + } + return 0; + } /* A single TL queue is allocated */ if (rsp->schq_contig[lvl]) { rsp->schq_contig[lvl] =3D 1; @@ -2202,7 +2307,7 @@ static void nix_txsch_alloc(struct rvu *rvu, struct n= ix_txsch *txsch, rsp->schq[lvl] =3D 1; rsp->schq_list[lvl][0] =3D start; } - return; + return 0; } =20 /* Adjust the queue request count if HW supports @@ -2214,7 +2319,7 @@ static void nix_txsch_alloc(struct rvu *rvu, struct n= ix_txsch *txsch, if (idx >=3D (end - start) || test_bit(schq, txsch->schq.bmap)) { rsp->schq_contig[lvl] =3D 0; rsp->schq[lvl] =3D 0; - return; + return 0; } =20 if (rsp->schq_contig[lvl]) { @@ -2227,7 +2332,7 @@ static void nix_txsch_alloc(struct rvu *rvu, struct n= ix_txsch *txsch, set_bit(schq, txsch->schq.bmap); rsp->schq_list[lvl][0] =3D schq; } - return; + return 0; } =20 /* Allocate contiguous queue indices requesty first */ @@ -2258,6 +2363,8 @@ static void nix_txsch_alloc(struct rvu *rvu, struct n= ix_txsch *txsch, /* Update how many were allocated */ rsp->schq[lvl] =3D idx; } + + return 0; } =20 int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, @@ -2282,6 +2389,10 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, if (!nix_hw) return NIX_AF_ERR_INVALID_NIXBLK; =20 + if ((req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) && + !nix_txsch_pan_allowed(rvu, pcifunc)) + return NIX_AF_ERR_TLX_ALLOC_FAIL; + mutex_lock(&rvu->rsrc_lock); =20 /* Check if request is valid as per HW capabilities @@ -2304,11 +2415,14 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rv= u, rsp->schq[lvl] =3D req->schq[lvl]; rsp->schq_contig[lvl] =3D req->schq_contig[lvl]; =20 - link =3D nix_get_tx_link(rvu, pcifunc); + if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) + link =3D nix_get_pan_tx_link(rvu); + else + link =3D nix_get_tx_link(rvu, pcifunc); =20 if (lvl >=3D hw->cap.nix_tx_aggr_lvl) { start =3D link; - end =3D link; + end =3D link + !!(req->flags & NIX_TXSCH_ALLOC_FLAG_PAN); } else if (hw->cap.nix_fixed_txschq_mapping) { nix_get_txschq_range(rvu, pcifunc, link, &start, &end); } else { @@ -2316,10 +2430,11 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rv= u, end =3D txsch->schq.max; } =20 - nix_txsch_alloc(rvu, txsch, rsp, lvl, start, end); + if (nix_txsch_alloc(rvu, txsch, rsp, lvl, start, end)) + goto err; =20 /* Reset queue config */ - for (idx =3D 0; idx < req->schq_contig[lvl]; idx++) { + for (idx =3D 0; idx < rsp->schq_contig[lvl]; idx++) { schq =3D rsp->schq_contig_list[lvl][idx]; if (!(TXSCH_MAP_FLAGS(pfvf_map[schq]) & NIX_TXSCHQ_CFG_DONE)) @@ -2329,7 +2444,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, nix_reset_tx_schedule(rvu, blkaddr, lvl, schq); } =20 - for (idx =3D 0; idx < req->schq[lvl]; idx++) { + for (idx =3D 0; idx < rsp->schq[lvl]; idx++) { schq =3D rsp->schq_list[lvl][idx]; if (!(TXSCH_MAP_FLAGS(pfvf_map[schq]) & NIX_TXSCHQ_CFG_DONE)) @@ -2598,6 +2713,19 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcif= unc) } nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1, nix_get_tx_link(rvu, pcifunc)); + /* TL1 is at nix_tx_aggr_lvl so the loop above skips it; also clear + * PAN TL1 XOFF on switch-owned links before flushing SMQs. + */ + if (nix_txsch_pan_allowed(rvu, pcifunc)) { + txsch =3D &nix_hw->txsch[NIX_TXSCH_LVL_TL1]; + + for (schq =3D nix_get_pan_tx_link(rvu); + nix_txsch_is_pan_schq(rvu, schq); schq++) { + if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) !=3D pcifunc) + continue; + nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1, schq); + } + } =20 /* On PF cleanup, clear cfg done flag as * PF would have changed default config. @@ -2625,11 +2753,11 @@ static int nix_txschq_free(struct rvu *rvu, u16 pci= func) /* TLs above aggregation level are shared across all PF * and it's VFs, hence skip freeing them. */ - if (lvl >=3D hw->cap.nix_tx_aggr_lvl) - continue; - txsch =3D &nix_hw->txsch[lvl]; for (schq =3D 0; schq < txsch->schq.max; schq++) { + if (lvl >=3D hw->cap.nix_tx_aggr_lvl && + !nix_txsch_is_pan_schq(rvu, schq)) + continue; if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) !=3D pcifunc) continue; nix_reset_tx_schedule(rvu, blkaddr, lvl, schq); @@ -2673,7 +2801,16 @@ static int nix_txschq_free_one(struct rvu *rvu, schq =3D req->schq; txsch =3D &nix_hw->txsch[lvl]; =20 - if (lvl >=3D hw->cap.nix_tx_aggr_lvl || schq >=3D txsch->schq.max) + if (req->flags & TXSCHQ_FREE_PAN_TL1) { + if (!nix_txsch_pan_allowed(rvu, pcifunc)) + return NIX_AF_ERR_TLX_INVALID; + if (!nix_txsch_is_pan_schq(rvu, schq)) + return NIX_AF_ERR_TLX_INVALID; + } else if (lvl >=3D hw->cap.nix_tx_aggr_lvl) { + return 0; + } + + if (schq >=3D txsch->schq.max) return 0; =20 pfvf_map =3D txsch->pfvf_map; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drive= rs/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 09c7ee8571df..a1a82ddb7c50 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1828,9 +1828,23 @@ int rvu_mbox_handler_npc_install_flow(struct rvu *rv= u, target =3D req->hdr.pcifunc; } =20 - /* ignore chan_mask in case pf func is not AF, revisit later */ - if (!is_pffunc_af(req->hdr.pcifunc)) - req->chan_mask =3D rvu_get_cpt_chan_mask(rvu); + /* Non-AF callers get the CPT default chan_mask unless the authorized + * switchdev PF sets set_chanmask to preserve a caller-supplied mask. + * VFs and other PFs must not use set_chanmask; that would bypass + * channel isolation. + */ + if (!is_pffunc_af(req->hdr.pcifunc)) { + if (req->set_chanmask && + !rvu_is_switch_pcifunc(rvu, req->hdr.pcifunc)) { + rvu_npc_free_entry_for_flow_install(rvu, + req->hdr.pcifunc, + allocated, + req->entry); + return NPC_FLOW_VF_PERM_DENIED; + } + if (!req->set_chanmask) + req->chan_mask =3D rvu_get_cpt_chan_mask(rvu); + } =20 err =3D npc_check_unsupported_flows(rvu, req->features, req->intf); if (err) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drive= rs/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index acf259d72008..73a98b94426b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -78,6 +78,8 @@ struct otx2_rcv_queue { struct sg_list { u16 num_segs; u16 flags; + u16 cq_idx; + u16 len; u64 skb; u64 size[OTX2_MAX_FRAGS_IN_SQE]; u64 dma_addr[OTX2_MAX_FRAGS_IN_SQE]; --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 309493BB12F; Tue, 21 Jul 2026 08:18:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621942; cv=none; b=Y+0acQl0L9T+m+fC7Cl+N6teQ1P01jTICfdqIuaO1qTMlSp5VZiVslzdKA2nq0EsySxp5pHKM0K8O6dOaXrwy4RboY6O+yjF/uq7/zX7jSqqpIYjuyUI51uSLm/O4oAsI8wA+NO/rn7+shkYW0saWlp8EzuNDxqKQ+hDvmTGzSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621942; c=relaxed/simple; bh=/FYO6HCp4iW/JTRfcT6i+tOX9/22tqc8L/4HPgbptYk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MOH33+0pOTyPAxAWlg7JcCnm75lUIjQH9Z1Rol/6LJkMEzRQdBUfEiVt0BkOPuGApF/zZEZoKKuxXbohypTmcPWTAD5/Ty/3iSOVWwbWpFpKPH4shGWHJKozArwMNgEjaln9u73bAk8wDF8m0Dm5A6RP9j8drfvsD9b6nIHdqU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=AbDCq40H; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="AbDCq40H" Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66L4Re0G1213770; Tue, 21 Jul 2026 01:18:49 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=K Gni0KMoF6Y+5o8iy1x/sDi1OvjTchI1eEnkE+z7VCs=; b=AbDCq40Hg9yVQG343 JNz5/qD9mYSI/Q0shRg+X0zS5DnVYHFnjLjXaM3yqzNTzeq+4H9V0RrgOb0UWRPH 075jCstpNEgrDOhfa/uR+bivlR6rlrmKntKqy66o22ddmoDRzod7e3mgFJAuk0sE 07srgQa5JuIXPycbzHWVx3KCIZvJkt3xA3IXEIFUnSoM1V6SfFAxKJ7AZWQLuqtp DvayY3SyctUBNyetu1doJxXrrmI5WTkL30SbqBzSwqmGtLrdOynaNUKdcDWfbjcJ roU2GtLjSQIXsPI2v1ZXZvCzLs6Xismxyuds0/RUanR3w7tN1jMJ2qmUG4IxCqXe 6E53Q== Received: from dc5-exch05.marvell.com ([199.233.59.128]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fgd7dfsy7-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:49 -0700 (PDT) Received: from DC5-EXCH05.marvell.com (10.69.176.209) by DC5-EXCH05.marvell.com (10.69.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:47 -0700 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH05.marvell.com (10.69.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:47 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 3920E3F7091; Tue, 21 Jul 2026 01:18:45 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 6/9] octeontx2-pf: switch: Register notifiers for switch offload Date: Tue, 21 Jul 2026 13:48:21 +0530 Message-ID: <20260721081824.1430607-7-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX9TK2EdE0YqbA O+Qim+ArdTDoIxL1IUHeO9TWViZDdlOLGMtg2gcxpKdtgzrtePnSZuuYFTEzD9RfDM0U55ggB/d Bk29seiNycV6VBoIGrT5LcJX+lCtzUY= X-Proofpoint-ORIG-GUID: TyVB8cun6RaY3QpAGgQusgb5m2gqEGDh X-Proofpoint-GUID: TyVB8cun6RaY3QpAGgQusgb5m2gqEGDh X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NiBTYWx0ZWRfX9aPDvLoYNU7F aeQLHzeDWAdKVC7B2xpT8gRdVJt+BcyC0mbnaWS+xS7Rh2/OUxP9loYZvjmI+acs3Vi1X0Bs/vp /1YVe5m/0BeyMdX5znKRUzcMnc7jpU2uJ3XIx+bLPNNIPcEhLtdptInfrpccJuT107iBx8lGmTF gVpKss+dO5JdhP5yUzgvfVNgVP3q24pUdbabm4kweshO1EGTo3cenrbE2jfImA+GBgVh1Ffa+fG O8wytDdmFF7Vm1uf8pjhr4VfTqkYNUvEYSXMa36mcizYU2Hl67pDJ1TTM9WQeOIkWct+pqK9Wv5 XQKSKIKEPP8jCq52M65LTD6r13Scpbvx1Cq+YLCNuyUdEoTwbWcXTXb1Mv6f7mtS3ZAA44EVC3D lq8u/Mo088p4FZslQsb1LDWMvoszxbPPfy8mKZKJd7CACLU2QDQZJiqdCko+3j2H9LOfn0wJjM7 /TevGKwvjAPvrLsgBFw== X-Authority-Analysis: v=2.4 cv=I9tVgtgg c=1 sm=1 tr=0 ts=6a5f2b69 cx=c_pps a=rEv8fa4AjpPjGxpoe8rlIQ==:117 a=rEv8fa4AjpPjGxpoe8rlIQ==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=QXcCYyLzdtTjyudCfB6f:22 a=M5GUcnROAAAA:8 a=on-D7ewkxeXsN44YeIoA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" The representor enables switch mode via devlink; register and unregister the switch notifier blocks when that mode is turned on or off so the PF can observe FIB routes, neighbour updates, IPv4/IPv6 address changes, netdev state, and switchdev FDB notifications. Add sw_nb_v4.c and sw_nb_v6.c for IPv4 and IPv6-specific handling, build sw_nb_v6.o only when CONFIG_IPV6 is set, and extend sw_nb.c with device filtering for Cavium ports behind bridges and VLANs. Initialize and tear down the existing sw_fdb, sw_fib, and sw_fl helpers together with notifier registration. Signed-off-by: Ratheesh Kannoth --- .../ethernet/marvell/octeontx2/nic/Makefile | 6 +- .../net/ethernet/marvell/octeontx2/nic/rep.c | 38 +- .../marvell/octeontx2/nic/switch/sw_nb.c | 502 +++++++++++++++++- .../marvell/octeontx2/nic/switch/sw_nb.h | 37 +- .../marvell/octeontx2/nic/switch/sw_nb_v4.c | 358 +++++++++++++ .../marvell/octeontx2/nic/switch/sw_nb_v4.h | 21 + .../marvell/octeontx2/nic/switch/sw_nb_v6.c | 292 ++++++++++ .../marvell/octeontx2/nic/switch/sw_nb_v6.h | 21 + 8 files changed, 1266 insertions(+), 9 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb= _v4.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb= _v4.h create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb= _v6.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb= _v6.h diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/= net/ethernet/marvell/octeontx2/nic/Makefile index 123b0af23abd..02ab0634f58f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -11,7 +11,11 @@ rvu_nicpf-y :=3D otx2_pf.o otx2_common.o otx2_txrx.o otx= 2_ethtool.o \ otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \ otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \ switch/sw_fdb.o switch/sw_fl.o -rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) +=3D switch/sw_nb.o switch/sw_fib.o +rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) +=3D switch/sw_nb.o switch/sw_fib.o \ + switch/sw_nb_v4.o +ifneq ($(CONFIG_IPV6),) +rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) +=3D switch/sw_nb_v6.o +endif =20 rvu_nicvf-y :=3D otx2_vf.o rvu_rep-y :=3D rep.o diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net= /ethernet/marvell/octeontx2/nic/rep.c index 257a2ae6a53e..1900235fabc5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c @@ -15,6 +15,7 @@ #include "cn10k.h" #include "otx2_reg.h" #include "rep.h" +#include "switch/sw_nb.h" =20 #define DRV_NAME "rvu_rep" #define DRV_STRING "Marvell RVU Representor Driver" @@ -399,22 +400,55 @@ static void rvu_rep_get_stats64(struct net_device *de= v, =20 static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena) { +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + struct net_device *netdev =3D priv->netdev; +#endif struct devlink_port_attrs attrs =3D {}; struct esw_cfg_req *req; + int mbox_err; +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + int err; +#endif =20 rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id); =20 +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + if (ena) { + err =3D sw_nb_register(netdev); + if (err) + return err; + } +#endif + mutex_lock(&priv->mbox.lock); req =3D otx2_mbox_alloc_msg_esw_cfg(&priv->mbox); if (!req) { mutex_unlock(&priv->mbox.lock); +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + if (ena) + sw_nb_unregister(netdev); +#endif return -ENOMEM; } req->ena =3D ena; memcpy(req->switch_id, attrs.switch_id.id, attrs.switch_id.id_len); - otx2_sync_mbox_msg(&priv->mbox); + mbox_err =3D otx2_sync_mbox_msg(&priv->mbox); mutex_unlock(&priv->mbox.lock); - return 0; + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + if (ena && mbox_err) { + sw_nb_unregister(netdev); + return mbox_err; + } + + if (!ena) { + err =3D sw_nb_unregister(netdev); + if (err && !mbox_err) + return err; + } +#endif + + return mbox_err; } =20 static netdev_tx_t rvu_rep_xmit(struct sk_buff *skb, struct net_device *de= v) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c index 243611835e3a..8a09876e8297 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c @@ -4,18 +4,516 @@ * Copyright (C) 2026 Marvell. * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../otx2_reg.h" +#include "../otx2_common.h" +#include "../otx2_struct.h" +#include "../cn10k.h" #include "sw_nb.h" +#include "sw_fdb.h" +#include "sw_fib.h" +#include "sw_fl.h" +#include "sw_nb_v4.h" +#include "sw_nb_v6.h" =20 #if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) =20 -int sw_nb_unregister(void) +/* PF netdev for netdev_* logging when notifier info has no device */ +static struct net_device *sw_nb_pf_netdev; +/* Notifier registration is only toggled from rvu_eswitch_config(), which = is + * reached exclusively via otx2_devlink_eswitch_mode_set() on the RVU + * representor devlink (otx2_rep_dev()). Devlink holds the per-instance + * devlink->lock for the full DEVLINK_CMD_ESWITCH_SET handler (pre_doit + * through post_doit), serializing register/unregister on that devlink. + * Regular netdev PFs return -EOPNOTSUPP from eswitch_mode_set and never + * invoke these helpers, so concurrent devlink changes on other PFs cannot + * race on this state. + */ +static bool sw_nb_registered; + +static const char *sw_nb_cmd2str[OTX2_CMD_MAX] =3D { + [OTX2_DEV_UP] =3D "OTX2_DEV_UP", + [OTX2_DEV_DOWN] =3D "OTX2_DEV_DOWN", + [OTX2_DEV_CHANGE] =3D "OTX2_DEV_CHANGE", + [OTX2_NEIGH_UPDATE] =3D "OTX2_NEIGH_UPDATE", + [OTX2_FIB_ENTRY_REPLACE] =3D "OTX2_FIB_ENTRY_REPLACE", + [OTX2_FIB_ENTRY_ADD] =3D "OTX2_FIB_ENTRY_ADD", + [OTX2_FIB_ENTRY_DEL] =3D "OTX2_FIB_ENTRY_DEL", + [OTX2_FIB_ENTRY_APPEND] =3D "OTX2_FIB_ENTRY_APPEND", +}; + +const char *sw_nb_get_cmd2str(int cmd) +{ + return sw_nb_cmd2str[cmd]; +} +EXPORT_SYMBOL(sw_nb_get_cmd2str); + +bool sw_nb_is_cavium_dev(struct net_device *netdev) +{ + struct pci_dev *pdev; + struct device *dev; + + dev =3D netdev->dev.parent; + if (!dev || dev->bus !=3D &pci_bus_type) + return false; + + pdev =3D to_pci_dev(dev); + if (pdev->vendor !=3D PCI_VENDOR_ID_CAVIUM) + return false; + + return true; +} + +/* Resolve the Cavium PF netdev used to reach the switch AF for offload. + * + * For a bridge master netdev, any Cavium netdev enslaved to the bridge is + * sufficient: callers only need a PF netdev to obtain the switch AF mailb= ox + * context (pcifunc). Bridge-specific information is tagged separately in + * the offload entry (entry->bridge), so walking every lower netdev is not + * required here. + */ +struct net_device *sw_nb_resolve_pf_dev(struct net_device *dev) { + struct net_device *pf_dev =3D dev; + struct list_head *iter; + + rcu_read_lock(); + + if (netif_is_bridge_master(dev)) { + iter =3D &dev->adj_list.lower; + pf_dev =3D netdev_next_lower_dev_rcu(dev, &iter); + if (!pf_dev) + pf_dev =3D dev; + } else if (is_vlan_dev(dev)) { + pf_dev =3D vlan_dev_real_dev(dev); + } + + rcu_read_unlock(); + + if (!sw_nb_is_cavium_dev(pf_dev)) + return NULL; + + return pf_dev; +} + +static int sw_nb_check_slaves(struct net_device *dev, + struct netdev_nested_priv *priv) +{ + int *cnt; + + if (!priv->flags) + return 0; + + priv->flags &=3D sw_nb_is_cavium_dev(dev); + if (priv->flags) { + cnt =3D priv->data; + (*cnt)++; + } + return 0; } =20 -int sw_nb_register(void) +bool sw_nb_is_valid_dev(struct net_device *netdev) +{ + struct netdev_nested_priv priv; + struct net_device *br; + int cnt =3D 0; + bool valid; + + priv.flags =3D true; + priv.data =3D &cnt; + + rcu_read_lock(); + + if (netif_is_bridge_master(netdev) || is_vlan_dev(netdev)) { + netdev_walk_all_lower_dev_rcu(netdev, sw_nb_check_slaves, &priv); + valid =3D priv.flags && cnt; + rcu_read_unlock(); + return valid; + } + + if (netif_is_bridge_port(netdev)) { + br =3D netdev_master_upper_dev_get_rcu(netdev); + if (!br) { + rcu_read_unlock(); + return false; + } + netdev_walk_all_lower_dev_rcu(br, sw_nb_check_slaves, &priv); + valid =3D priv.flags && cnt; + rcu_read_unlock(); + return valid; + } + + rcu_read_unlock(); + + return sw_nb_is_cavium_dev(netdev); +} + +static int sw_nb_fdb_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev =3D switchdev_notifier_info_to_dev(ptr); + struct switchdev_notifier_fdb_info *fdb_info =3D ptr; + + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + if (fdb_info->is_local) + break; + break; + + case SWITCHDEV_FDB_DEL_TO_DEVICE: + if (fdb_info->is_local) + break; + break; + + default: + return NOTIFY_DONE; + } + + return NOTIFY_DONE; +} + +static struct notifier_block sw_nb_fdb =3D { + .notifier_call =3D sw_nb_fdb_event, +}; + +static void __maybe_unused +sw_nb_fib_event_dump(unsigned long event, void *ptr) +{ + struct fib_entry_notifier_info *fen_info =3D ptr; + struct net_device *log_dev; + struct fib_nh *fib_nh; + struct fib_info *fi; + int i; + + fi =3D fen_info->fi; + log_dev =3D (fi && fi->fib_nhs) ? fi->fib_nh->fib_nh_dev : sw_nb_pf_netde= v; + if (log_dev) + netdev_info(log_dev, "%s: FIB event=3D%lu dst=3D%pI4 dstlen=3D%u type=3D= %u\n", + __func__, event, (const __be32 *)&fen_info->dst, + fen_info->dst_len, fen_info->type); + + if (!fi) + return; + + fib_nh =3D fi->fib_nh; + for (i =3D 0; i < fi->fib_nhs; i++, fib_nh++) { + if (!fib_nh->fib_nh_dev) + continue; + netdev_info(fib_nh->fib_nh_dev, + "%s: dev=3D%s saddr=3D%pI4 gw=3D%pI4\n", + __func__, fib_nh->fib_nh_dev->name, + &fib_nh->nh_saddr, &fib_nh->fib_nh_gw4); + } +} + +#define SWITCH_NB_FIB_EVENT_DUMP(...) \ + sw_nb_fib_event_dump(__VA_ARGS__) + +int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev) +{ + switch (event) { + case FIB_EVENT_ENTRY_REPLACE: + return OTX2_FIB_ENTRY_REPLACE; + case FIB_EVENT_ENTRY_ADD: + return OTX2_FIB_ENTRY_ADD; + case FIB_EVENT_ENTRY_DEL: + return OTX2_FIB_ENTRY_DEL; + default: + break; + } + + netdev_err(netdev, "Wrong FIB event %d\n", event); + return -1; +} + +static int sw_nb_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct fib_notifier_info *info =3D ptr; + + switch (event) { + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_DEL: + break; + default: + if (sw_nb_pf_netdev) + netdev_dbg(sw_nb_pf_netdev, + "%s: Won't process FIB event %lu\n", + __func__, event); + return NOTIFY_DONE; + } + + switch (info->family) { + case AF_INET: + return sw_nb_v4_fib_event(nb, event, ptr); +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + return sw_nb_v6_fib_event(nb, event, ptr); +#endif + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block sw_nb_fib =3D { + .notifier_call =3D sw_nb_fib_event, +}; + +static int sw_nb_net_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct neighbour *n =3D ptr; + + if (!sw_nb_is_valid_dev(n->dev)) + return NOTIFY_DONE; + + if (event !=3D NETEVENT_NEIGH_UPDATE) + return NOTIFY_DONE; + + switch (n->tbl->family) { + case AF_INET: + return sw_nb_net_v4_neigh_update(nb, event, ptr); +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + return sw_nb_net_v6_neigh_update(nb, event, ptr); +#endif + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block sw_nb_netevent =3D { + .notifier_call =3D sw_nb_net_event, + +}; + +int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netde= v) +{ + switch (event) { + case NETDEV_CHANGE: + return OTX2_DEV_CHANGE; + case NETDEV_UP: + return OTX2_DEV_UP; + case NETDEV_DOWN: + return OTX2_DEV_DOWN; + default: + break; + } + netdev_dbg(netdev, "%s: Wrong interaddr event %d\n", + __func__, event); + return -1; +} + +static struct notifier_block sw_nb_v4_inetaddr =3D { + .notifier_call =3D sw_nb_v4_inetaddr_event, +}; + +#if IS_ENABLED(CONFIG_IPV6) +static struct notifier_block sw_nb_v6_inetaddr =3D { + .notifier_call =3D sw_nb_v6_inetaddr_event, +}; +#endif + +static int sw_nb_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) { + struct net_device *dev =3D netdev_notifier_info_to_dev(ptr); + struct in_device *idev; + struct inet6_dev *i6dev; + + if (event !=3D NETDEV_CHANGE && + event !=3D NETDEV_UP && + event !=3D NETDEV_DOWN) { + return NOTIFY_DONE; + } + + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + + idev =3D __in_dev_get_rtnl(dev); + if (idev) + sw_nb_v4_netdev_event(unused, event, ptr); + +#if IS_ENABLED(CONFIG_IPV6) + i6dev =3D __in6_dev_get(dev); + if (i6dev) + sw_nb_v6_netdev_event(unused, event, ptr); +#endif + + return NOTIFY_DONE; +} + +static struct notifier_block sw_nb_netdev =3D { + .notifier_call =3D sw_nb_netdev_event, +}; + +int sw_nb_unregister(struct net_device *netdev) +{ + int err, ret =3D 0; + + if (!sw_nb_registered) + return 0; + + err =3D unregister_switchdev_notifier(&sw_nb_fdb); + if (err) { + netdev_err(netdev, "Failed to unregister switchdev nb\n"); + ret =3D err; + } + + err =3D unregister_fib_notifier(&init_net, &sw_nb_fib); + if (err) { + netdev_err(netdev, "Failed to unregister fib nb\n"); + if (!ret) + ret =3D err; + } + + err =3D unregister_netevent_notifier(&sw_nb_netevent); + if (err) { + netdev_err(netdev, "Failed to unregister netevent\n"); + if (!ret) + ret =3D err; + } + + err =3D unregister_inetaddr_notifier(&sw_nb_v4_inetaddr); + if (err) { + netdev_err(netdev, "Failed to unregister addr event\n"); + if (!ret) + ret =3D err; + } + +#if IS_ENABLED(CONFIG_IPV6) + err =3D unregister_inet6addr_notifier(&sw_nb_v6_inetaddr); + if (err) { + netdev_err(netdev, "Failed to unregister addr event\n"); + if (!ret) + ret =3D err; + } +#endif + + err =3D unregister_netdevice_notifier(&sw_nb_netdev); + if (err) { + netdev_err(netdev, "Failed to unregister netdev notifier\n"); + if (!ret) + ret =3D err; + } + + sw_fl_deinit(); + sw_fib_deinit(); + sw_fdb_deinit(); + + sw_nb_pf_netdev =3D NULL; + sw_nb_registered =3D false; + + return ret; +} +EXPORT_SYMBOL(sw_nb_unregister); + +int sw_nb_register(struct net_device *netdev) +{ + int err; + + if (sw_nb_registered) + return -EBUSY; + + sw_nb_pf_netdev =3D netdev; + + err =3D sw_fdb_init(); + if (err) + goto err_clear; + + err =3D sw_fib_init(); + if (err) + goto err_fdb; + + err =3D sw_fl_init(); + if (err) + goto err_fib; + + err =3D register_switchdev_notifier(&sw_nb_fdb); + if (err) { + netdev_err(netdev, "Failed to register switchdev nb\n"); + goto err_helpers; + } + + err =3D register_fib_notifier(&init_net, &sw_nb_fib, NULL, NULL); + if (err) { + netdev_err(netdev, "Failed to register fb notifier block\n"); + goto err1; + } + + err =3D register_netevent_notifier(&sw_nb_netevent); + if (err) { + netdev_err(netdev, "Failed to register netevent\n"); + goto err2; + } + +#if IS_ENABLED(CONFIG_IPV6) + err =3D register_inet6addr_notifier(&sw_nb_v6_inetaddr); + if (err) { + netdev_err(netdev, "Failed to register addr event\n"); + goto err3; + } +#endif + + err =3D register_inetaddr_notifier(&sw_nb_v4_inetaddr); + if (err) { + netdev_err(netdev, "Failed to register addr event\n"); + goto err4; + } + + err =3D register_netdevice_notifier(&sw_nb_netdev); + if (err) { + netdev_err(netdev, "Failed to register netdevice nb\n"); + goto err5; + } + + sw_nb_registered =3D true; + return 0; + +err5: + unregister_inetaddr_notifier(&sw_nb_v4_inetaddr); + +err4: +#if IS_ENABLED(CONFIG_IPV6) + unregister_inet6addr_notifier(&sw_nb_v6_inetaddr); + +err3: +#endif + unregister_netevent_notifier(&sw_nb_netevent); + +err2: + unregister_fib_notifier(&init_net, &sw_nb_fib); + +err1: + unregister_switchdev_notifier(&sw_nb_fdb); + +err_helpers: + sw_fl_deinit(); +err_fib: + sw_fib_deinit(); +err_fdb: + sw_fdb_deinit(); +err_clear: + sw_nb_pf_netdev =3D NULL; + return err; } +EXPORT_SYMBOL(sw_nb_register); =20 #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h index 73cc1e99b8ec..e995c0e6046b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h @@ -9,12 +9,41 @@ =20 #include =20 +struct net_device; +struct otx2_nic; +struct af2pf_fdb_refresh_req; +struct msg_rsp; + #if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) -int sw_nb_register(void); -int sw_nb_unregister(void); +enum { + OTX2_DEV_UP =3D 1, + OTX2_DEV_DOWN, + OTX2_DEV_CHANGE, + OTX2_NEIGH_UPDATE, + OTX2_FIB_ENTRY_REPLACE, + OTX2_FIB_ENTRY_ADD, + OTX2_FIB_ENTRY_DEL, + OTX2_FIB_ENTRY_APPEND, + OTX2_CMD_MAX, +}; + +int sw_nb_register(struct net_device *netdev); +int sw_nb_unregister(struct net_device *netdev); +bool sw_nb_is_valid_dev(struct net_device *netdev); +struct net_device *sw_nb_resolve_pf_dev(struct net_device *dev); + +int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf, + struct af2pf_fdb_refresh_req *req, + struct msg_rsp *rsp); + +bool sw_nb_is_cavium_dev(struct net_device *netdev); +int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev); +int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netde= v); + +const char *sw_nb_get_cmd2str(int cmd); #else -static inline int sw_nb_register(void) { return 0; } -static inline int sw_nb_unregister(void) { return 0; } +static inline int sw_nb_register(struct net_device *netdev) { return 0; } +static inline int sw_nb_unregister(struct net_device *netdev) { return 0; } #endif =20 #endif /* SW_NB_H_ */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c new file mode 100644 index 000000000000..c773fce1bc50 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../otx2_reg.h" +#include "../otx2_common.h" +#include "../otx2_struct.h" +#include "../cn10k.h" +#include "sw_nb.h" +#include "sw_fdb.h" +#include "sw_fib.h" +#include "sw_fl.h" +#include "sw_nb_v4.h" + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + +int sw_nb_v4_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev =3D netdev_notifier_info_to_dev(ptr); + struct netdev_hw_addr *dev_addr; + struct net_device *pf_dev; + struct in_device *idev; + struct in_ifaddr *ifa; + struct fib_entry *entry; + struct otx2_nic *pf; + + idev =3D __in_dev_get_rtnl(dev); + if (!idev || !idev->ifa_list) + return NOTIFY_DONE; + + /* Switch offload supports a single IPv4 address per interface for now. */ + ifa =3D rtnl_dereference(idev->ifa_list); + + entry =3D kcalloc(1, sizeof(*entry), GFP_KERNEL); + if (!entry) + return NOTIFY_DONE; + + entry->cmd =3D sw_nb_inetaddr_event_to_otx2_event(event, dev); + entry->dst =3D ifa->ifa_address; + entry->dst_len =3D 32; + entry->mac_valid =3D 1; + entry->host =3D 1; + + pf_dev =3D sw_nb_resolve_pf_dev(dev); + if (!pf_dev) { + kfree(entry); + return NOTIFY_DONE; + } + + if (netif_is_bridge_master(dev)) { + entry->bridge =3D 1; + } else if (is_vlan_dev(dev)) { + entry->vlan_valid =3D 1; + entry->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(dev)); + } + + pf =3D netdev_priv(pf_dev); + entry->port_id =3D pf->pcifunc; + + for_each_dev_addr(dev, dev_addr) { + ether_addr_copy(entry->mac, dev_addr->addr); + break; + } + + netdev_dbg(dev, "%s: pushing netdev event from HOST interface address %pI= 4, %pM, dev=3D%s\n", + __func__, &entry->dst, entry->mac, dev->name); + kfree(entry); + + return NOTIFY_DONE; +} + +int sw_nb_v4_inetaddr_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa =3D (struct in_ifaddr *)ptr; + struct net_device *dev =3D ifa->ifa_dev->dev; + struct netdev_hw_addr *dev_addr; + struct net_device *pf_dev; + struct in_device *idev; + struct fib_entry *entry; + struct otx2_nic *pf; + + if (event !=3D NETDEV_CHANGE && + event !=3D NETDEV_UP && + event !=3D NETDEV_DOWN) { + return NOTIFY_DONE; + } + + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + + idev =3D __in_dev_get_rtnl(dev); + if (!idev || !idev->ifa_list) + return NOTIFY_DONE; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return NOTIFY_DONE; + + entry->cmd =3D sw_nb_inetaddr_event_to_otx2_event(event, dev); + entry->dst =3D ifa->ifa_address; + entry->dst_len =3D 32; + entry->mac_valid =3D 1; + entry->host =3D 1; + + pf_dev =3D sw_nb_resolve_pf_dev(dev); + if (!pf_dev) { + kfree(entry); + return NOTIFY_DONE; + } + + if (netif_is_bridge_master(dev)) { + entry->bridge =3D 1; + } else if (is_vlan_dev(dev)) { + entry->vlan_valid =3D 1; + entry->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(dev)); + } + + pf =3D netdev_priv(pf_dev); + entry->port_id =3D pf->pcifunc; + + for_each_dev_addr(dev, dev_addr) { + ether_addr_copy(entry->mac, dev_addr->addr); + break; + } + + netdev_dbg(dev, "%s: pushing inetaddr event from HOST interface address %= pI4, %pM, %s\n", + __func__, &entry->dst, entry->mac, dev->name); + + kfree(entry); + return NOTIFY_DONE; +} + +int sw_nb_v4_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *dev, *pf_dev =3D NULL, *nh_pf_dev; + struct fib_entry_notifier_info *fen_info =3D ptr; + struct fib_entry *entries, *iter; + struct netdev_hw_addr *dev_addr; + struct neighbour *neigh; + struct fib_nh *fib_nh; + struct fib_info *fi; + struct otx2_nic *pf; + __be32 *haddr; + int hcnt =3D 0; + int cnt, i; + + /* Process only UNICAST routes add or del */ + if (fen_info->type !=3D RTN_UNICAST) + return NOTIFY_DONE; + + fi =3D fen_info->fi; + if (!fi) + return NOTIFY_DONE; + + if (fi->fib_nh_is_v6) { + struct net_device *log_dev =3D (fi->fib_nhs > 0) ? + fi->fib_nh->fib_nh_dev : NULL; + + if (log_dev) + netdev_dbg(log_dev, "%s: Received v6 notification\n", + __func__); + return NOTIFY_DONE; + } + + entries =3D kcalloc(fi->fib_nhs, sizeof(*entries), GFP_ATOMIC); + if (!entries) + return NOTIFY_DONE; + + haddr =3D kcalloc(fi->fib_nhs, sizeof(*haddr), GFP_ATOMIC); + if (!haddr) { + kfree(entries); + return NOTIFY_DONE; + } + + iter =3D entries; + fib_nh =3D fi->fib_nh; + for (i =3D 0; i < fi->fib_nhs; i++, fib_nh++) { + dev =3D fib_nh->fib_nh_dev; + + if (!dev) + continue; + + if (dev->type !=3D ARPHRD_ETHER) + continue; + + if (!sw_nb_is_valid_dev(dev)) + continue; + + iter->cmd =3D sw_nb_fib_event_to_otx2_event(event, dev); + iter->dst =3D (__force __be32)fen_info->dst; + iter->dst_len =3D fen_info->dst_len; + iter->gw =3D fib_nh->fib_nh_gw4; + + netdev_dbg(dev, "%s: FIB route Rule cmd=3D%llu dst=3D%pI4 dst_len=3D%u g= w=3D%pI4\n", + __func__, iter->cmd, &iter->dst, iter->dst_len, &iter->gw); + + nh_pf_dev =3D sw_nb_resolve_pf_dev(dev); + if (!nh_pf_dev) { + iter++; + continue; + } + pf_dev =3D nh_pf_dev; + + if (netif_is_bridge_master(dev)) { + iter->bridge =3D 1; + } else if (is_vlan_dev(dev)) { + iter->vlan_valid =3D 1; + iter->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(dev)); + } + + pf =3D netdev_priv(pf_dev); + iter->port_id =3D pf->pcifunc; + + /* Point-to-point routes, including default routes with no + * gateway, are not supported for switch offload. + */ + if (!fib_nh->fib_nh_gw4) { + if (iter->dst || iter->dst_len) + iter++; + + continue; + } + iter->gw_valid =3D 1; + + if (fib_nh->nh_saddr) + haddr[hcnt++] =3D fib_nh->nh_saddr; + + rcu_read_lock(); + neigh =3D ip_neigh_gw4(fib_nh->fib_nh_dev, fib_nh->fib_nh_gw4); + if (!neigh) { + rcu_read_unlock(); + iter++; + continue; + } + + if (is_valid_ether_addr(neigh->ha)) { + iter->mac_valid =3D 1; + neigh_ha_snapshot(iter->mac, neigh, fib_nh->fib_nh_dev); + } + + iter++; + rcu_read_unlock(); + } + + cnt =3D iter - entries; + if (!cnt) { + kfree(entries); + kfree(haddr); + return NOTIFY_DONE; + } + + if (pf_dev) + netdev_dbg(pf_dev, "pf_dev is %s cnt=3D%d\n", pf_dev->name, cnt); + kfree(entries); + + if (!hcnt) { + kfree(haddr); + return NOTIFY_DONE; + } + + if (!pf_dev) { + kfree(haddr); + return NOTIFY_DONE; + } + + entries =3D kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC); + if (!entries) { + kfree(haddr); + return NOTIFY_DONE; + } + + iter =3D entries; + + /* Host routes reuse pf_dev/pf from the last resolved Cavium netdev: + * pf_dev only identifies the switch AF mailbox context for switchdev + * programming; any previously resolved Cavium netdev is sufficient. + */ + for (i =3D 0; i < hcnt; i++, iter++) { + iter->cmd =3D sw_nb_fib_event_to_otx2_event(event, pf_dev); + iter->dst =3D haddr[i]; + iter->dst_len =3D 32; + iter->mac_valid =3D 1; + iter->host =3D 1; + iter->port_id =3D pf->pcifunc; + + rcu_read_lock(); + for_each_dev_addr(pf_dev, dev_addr) { + ether_addr_copy(iter->mac, dev_addr->addr); + break; + } + rcu_read_unlock(); + + netdev_dbg(pf_dev, "%s: FIB host Rule cmd=3D%llu dst=3D%pI4 dst_len=3D%u= gw=3D%pI4 %s\n", + __func__, iter->cmd, &iter->dst, iter->dst_len, &iter->gw, + pf_dev->name); + } + kfree(entries); + kfree(haddr); + return NOTIFY_DONE; +} + +int sw_nb_net_v4_neigh_update(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *pf_dev; + struct neighbour *n =3D ptr; + struct fib_entry *entry; + struct otx2_nic *pf; + + if (n->tbl !=3D &arp_tbl) + return NOTIFY_DONE; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return NOTIFY_DONE; + + entry->cmd =3D OTX2_NEIGH_UPDATE; + entry->dst =3D *(__be32 *)n->primary_key; + entry->dst_len =3D n->tbl->key_len * 8; + entry->mac_valid =3D 1; + entry->nud_state =3D n->nud_state; + neigh_ha_snapshot(entry->mac, n, n->dev); + + pf_dev =3D sw_nb_resolve_pf_dev(n->dev); + if (!pf_dev) { + kfree(entry); + return NOTIFY_DONE; + } + + if (netif_is_bridge_master(n->dev)) { + entry->bridge =3D 1; + } else if (is_vlan_dev(n->dev)) { + entry->vlan_valid =3D 1; + entry->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(n->dev)); + } + + pf =3D netdev_priv(pf_dev); + entry->port_id =3D pf->pcifunc; + + kfree(entry); + return NOTIFY_DONE; +} + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h new file mode 100644 index 000000000000..c6dbf4b93a9a --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#ifndef SW_NB_V4_H_ +#define SW_NB_V4_H_ + +int sw_nb_v4_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr); + +int sw_nb_net_v4_neigh_update(struct notifier_block *nb, + unsigned long event, void *ptr); + +int sw_nb_v4_inetaddr_event(struct notifier_block *nb, + unsigned long event, void *ptr); + +int sw_nb_v4_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr); +#endif // SW_NB_V4_H__ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c new file mode 100644 index 000000000000..62ab00658879 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../otx2_reg.h" +#include "../otx2_common.h" +#include "../otx2_struct.h" +#include "../cn10k.h" +#include "sw_nb.h" +#include "sw_fdb.h" +#include "sw_fib.h" +#include "sw_fl.h" +#include "sw_nb_v6.h" + +#if IS_ENABLED(CONFIG_IPV6) && IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + +int sw_nb_v6_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev =3D netdev_notifier_info_to_dev(ptr); + struct netdev_hw_addr *dev_addr; + struct net_device *pf_dev; + struct inet6_ifaddr *ifp; + struct inet6_dev *i6dev; + struct fib_entry *entry; + struct in6_addr addr; + struct otx2_nic *pf; + u32 prefix_len; + + i6dev =3D __in6_dev_get(dev); + if (!i6dev) + return NOTIFY_DONE; + + /* Invoked from sw_nb_netdev_event() on NETDEV_UP/DOWN/CHANGE, which + * run with RTNL held. IPv6 address list updates are also serialized + * by RTNL, so addr_list cannot race with concurrent assignments. + */ + rcu_read_lock(); + /* Switch offload supports a single IPv6 address per interface for now. */ + ifp =3D list_first_entry_or_null(&i6dev->addr_list, + struct inet6_ifaddr, if_list); + if (!ifp) { + rcu_read_unlock(); + return NOTIFY_DONE; + } + + if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) { + rcu_read_unlock(); + return NOTIFY_DONE; + } + + addr =3D ifp->addr; + prefix_len =3D ifp->prefix_len; + rcu_read_unlock(); + + entry =3D kcalloc(1, sizeof(*entry), GFP_KERNEL); + if (!entry) + return NOTIFY_DONE; + + pf_dev =3D sw_nb_resolve_pf_dev(dev); + if (!pf_dev) { + kfree(entry); + return NOTIFY_DONE; + } + + entry->cmd =3D sw_nb_inetaddr_event_to_otx2_event(event, dev); + memcpy(entry->dst6, &addr, sizeof(entry->dst6)); + entry->dst6_plen =3D prefix_len; + entry->host =3D 1; + entry->ipv6 =3D 1; + + pf =3D netdev_priv(pf_dev); + entry->port_id =3D pf->pcifunc; + + for_each_dev_addr(dev, dev_addr) { + entry->mac_valid =3D 1; + ether_addr_copy(entry->mac, dev_addr->addr); + break; + } + + netdev_dbg(dev, "netdev event addr=3D%pI6c plen=3D%u mac=3D%pM\n", + &addr, prefix_len, entry->mac); + kfree(entry); + return NOTIFY_DONE; +} + +int sw_nb_v6_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct fib6_entry_notifier_info *f6_eni; + struct fib_notifier_info *info =3D ptr; + struct net_device *fib_dev, *pf_dev; + struct fib_entry *entry; + struct fib6_info *f6i; + struct neighbour *neigh; + struct fib6_nh *nh6; + struct rt6key *key; + struct otx2_nic *pf; + + f6_eni =3D container_of(info, struct fib6_entry_notifier_info, info); + f6i =3D f6_eni->rt; + + fib_dev =3D fib6_info_nh_dev(f6i); + + if (!fib_dev) + return NOTIFY_DONE; + + if (fib_dev->type !=3D ARPHRD_ETHER) + return NOTIFY_DONE; + + if (!sw_nb_is_valid_dev(fib_dev)) + return NOTIFY_DONE; + + if (f6i->fib6_type !=3D RTN_UNICAST) + return NOTIFY_DONE; + + key =3D &f6i->fib6_dst; + /* TODO: vlan and bridge support */ + if (ipv6_addr_type(&key->addr) & IPV6_ADDR_LINKLOCAL) + return NOTIFY_DONE; + + netdev_dbg(fib_dev, "fib6dst rt6key.addr=3D%pI6c len=3D%u\n", &key->addr, + key->plen); + + netdev_dbg(fib_dev, "fib6flags=3D%#x proto=3D%u type=3D%u\n", + f6i->fib6_flags, f6i->fib6_protocol, f6i->fib6_type); + + nh6 =3D f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh; + netdev_dbg(nh6->fib_nh_dev ? nh6->fib_nh_dev : fib_dev, + "nh family=3D%u dev=3D%s gw=3D%pI6c gwfamily=3D%u\n", + nh6->fib_nh_family, + nh6->fib_nh_dev ? nh6->fib_nh_dev->name : "No dev", + &nh6->fib_nh_gw6, nh6->fib_nh_gw_family); + + pf_dev =3D sw_nb_resolve_pf_dev(fib_dev); + if (!pf_dev) + return NOTIFY_DONE; + + pf =3D netdev_priv(pf_dev); + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return NOTIFY_DONE; + + entry->cmd =3D sw_nb_fib_event_to_otx2_event(event, fib_dev); + entry->ipv6 =3D 1; + entry->port_id =3D pf->pcifunc; + memcpy(entry->dst6, &key->addr, sizeof(entry->dst6)); + entry->dst6_plen =3D key->plen; + + memcpy(entry->gw6, &nh6->fib_nh_gw6, sizeof(nh6->fib_nh_gw6)); + entry->gw_valid =3D !!(ipv6_addr_type(&nh6->fib_nh_gw6) & IPV6_ADDR_UNICA= ST); + + /* TODO: No replay mechanism yet when the gateway neighbor is unresolved. + * If ip_neigh_gw6() returns NULL the route is skipped here; add replay + * from the neighbor update handler once nexthop resolution completes. + */ + rcu_read_lock(); + neigh =3D ip_neigh_gw6(fib_dev, &nh6->fib_nh_gw6); + if (!neigh) { + rcu_read_unlock(); + kfree(entry); + return NOTIFY_DONE; + } + + if (is_valid_ether_addr(neigh->ha)) { + entry->mac_valid =3D 1; + neigh_ha_snapshot(entry->mac, neigh, fib_dev); + netdev_dbg(fib_dev, "fib found MAC=3D%pM\n", entry->mac); + } + + rcu_read_unlock(); + kfree(entry); + + return NOTIFY_DONE; +} + +int sw_nb_net_v6_neigh_update(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *pf_dev; + struct neighbour *n =3D ptr; + struct fib_entry *entry; + struct otx2_nic *pf; + + if (n->tbl !=3D &nd_tbl) + return NOTIFY_DONE; + + if (ipv6_addr_type((struct in6_addr *)n->primary_key) & IPV6_ADDR_LINKLOC= AL) + return NOTIFY_DONE; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return NOTIFY_DONE; + + pf_dev =3D sw_nb_resolve_pf_dev(n->dev); + if (!pf_dev) { + kfree(entry); + return NOTIFY_DONE; + } + + pf =3D netdev_priv(pf_dev); + + entry->cmd =3D OTX2_NEIGH_UPDATE; + entry->dst6_plen =3D n->tbl->key_len * 8; + memcpy(entry->dst6, (struct in6_addr *)n->primary_key, + sizeof(entry->dst6)); + entry->ipv6 =3D 1; + entry->nud_state =3D n->nud_state; + neigh_ha_snapshot(entry->mac, n, n->dev); + entry->mac_valid =3D 1; + entry->port_id =3D pf->pcifunc; + + netdev_dbg(n->dev, "v6 neigh update %pI6c mac=3D%pM plen=3D%u\n", + n->primary_key, entry->mac, n->tbl->key_len * 8); + kfree(entry); + + return NOTIFY_DONE; +} + +int sw_nb_v6_inetaddr_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa6 =3D (struct inet6_ifaddr *)ptr; + struct net_device *dev =3D ifa6->idev->dev; + struct netdev_hw_addr *dev_addr; + struct net_device *pf_dev; + struct fib_entry *entry; + struct otx2_nic *pf; + + if (event !=3D NETDEV_CHANGE && + event !=3D NETDEV_UP && + event !=3D NETDEV_DOWN) { + return NOTIFY_DONE; + } + + if (dev->type !=3D ARPHRD_ETHER) + return NOTIFY_DONE; + + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + + if (ipv6_addr_type(&ifa6->addr) & IPV6_ADDR_LINKLOCAL) + return NOTIFY_DONE; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return NOTIFY_DONE; + + pf_dev =3D sw_nb_resolve_pf_dev(dev); + if (!pf_dev) { + kfree(entry); + return NOTIFY_DONE; + } + + pf =3D netdev_priv(pf_dev); + + entry->cmd =3D sw_nb_inetaddr_event_to_otx2_event(event, dev); + memcpy(entry->dst6, &ifa6->addr, sizeof(entry->dst6)); + entry->dst6_plen =3D ifa6->prefix_len; + entry->mac_valid =3D 1; + entry->host =3D 1; + entry->ipv6 =3D 1; + entry->port_id =3D pf->pcifunc; + + for_each_dev_addr(dev, dev_addr) { + ether_addr_copy(entry->mac, dev_addr->addr); + entry->mac_valid =3D 1; + break; + } + + netdev_dbg(dev, "inetaddr addr=3D%pI6c len=3D%u %pM\n", + &ifa6->addr, ifa6->prefix_len, entry->mac); + kfree(entry); + + return NOTIFY_DONE; +} +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h new file mode 100644 index 000000000000..f73efc98c311 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell switch driver + * + * Copyright (C) 2026 Marvell. + * + */ +#ifndef SW_NB_V6_H_ +#define SW_NB_V6_H_ + +int sw_nb_v6_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr); + +int sw_nb_net_v6_neigh_update(struct notifier_block *nb, + unsigned long event, void *ptr); + +int sw_nb_v6_inetaddr_event(struct notifier_block *nb, + unsigned long event, void *ptr); + +int sw_nb_v6_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr); +#endif // SW_NB_V6_H__ --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 CD5163BB12E; Tue, 21 Jul 2026 08:19:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621943; cv=none; b=s4/16RUITZJJRVBSIjwyu39Dv6Edf9dM49RoWUGI437sTaSOCAqicAp2R5xF6C4AHnFkyHGfQk+pEgcGNji2wqIjkMGLY2M3pnh/7QtExc3ggBgDkffnTp/39GPQf5R8wd/eiZgsTD6apiKQiM9lvD2CXlUOU6kawuFT7AZmB8s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621943; c=relaxed/simple; bh=3AF7RR7Rr88C5XV6PyfAF9E3K+f/92GDma6HLDDPUkc=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=oXiS64AjGrDLE1R+CNe6zWOMPpaAUXWqlF/xnxVgenIuRhbuLa1MJmmmoyjeC83wjNx3rUWGAgwVqwKRKnX8dZwYmH9hKvuabzxaV8sn4MUNQhpCD1+OnqmTsi+ldQcA6ZXx4n+E2I7kHBKXbk+LbZQWQC1pmsgYgiEbpTAXBZQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=AxxEHdYf; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="AxxEHdYf" Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66L7CZ0l1531219; Tue, 21 Jul 2026 01:18:52 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=j Rfy2UaVYyDCcJzs4yxp6ssjGRgNpdVlkgXEVXa7vl8=; b=AxxEHdYfppRmEx1Gs j+NCegVaG6HpBMyKzyUgcM0oOhP85K4KE6HkUk0s1l+7A68J4G//P7Y6va/ok769 snsFxDGVtJ95C9mvO9Zt6r1paXY8sXoVhaw657PhvAQqlfxbeZ9mt7JQ/SfjdfJN 5LqXvOx7WT69nStSmYC2rQ1zHG6IL5AGGFQJxaB16JVWu63UwCtct0kmqvX4lO1h E1sCmOj5zI5fxXUSpf7cKXEifWMUnawvOeB0YhIhhe7nbuV8zQdKr+ubgld0rzpz 9QD9Y9xqsXmchsrvoCdCr56Bw436ZPPhgu5e8cZvpWwDnsCXTSyMQGsyBzaC+doU f8qSQ== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fgd7dfsya-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:51 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:51 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:51 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 5B43A3F7070; Tue, 21 Jul 2026 01:18:48 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 7/9] octeontx2: switch: plumb bridge FDB updates through AF and switchdev Date: Tue, 21 Jul 2026 13:48:22 +0530 Message-ID: <20260721081824.1430607-8-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NyBTYWx0ZWRfX3CNz97OHqpGb RC7oXu5sQVySkcpF/2jWxAWqO90UnC4ZHb0uLkTUTHy78wequDrmFQyecXPy3OgpbfOUubnDQNw +8ljWMzhXLMrsYje29R/TS4MraKE2M8= X-Proofpoint-ORIG-GUID: aBSoQuo8cEuHVnl7eaNFpCJs4SuPJL8- X-Proofpoint-GUID: aBSoQuo8cEuHVnl7eaNFpCJs4SuPJL8- X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NyBTYWx0ZWRfXxKhEga6KNzqH odIO/p4YOJI8J/ug6w1YlBputsduG3ub3R7qplYUxcjR2mcZIO4gB4GBAoP9sMKTxaOY/owMwDJ oEkgNNjYiowe7RaAkcr3+09TwAF/yUtKT8BYXQe8lT3M/dVlde4C26bx9hBfYIvwhaDEDJmQgWi CrDu+iJ2JGmQChVhtkh48VX2FKib1bCpHVqtJm/OpsECiDCAcbCLmFyutG6w9F3fZkdNBDC7r7O e2J5fPngqdqtMw9XgIKRFbnU3ZxX4yx0/Of+f2hYmytiGKSjAFMhLg17dtz7yUxG15qkLPH2VKI lgDCddGKmAxZUBMYc8ieo/Y8NLMDWAgBPkAPqw3Lst7zFXbdFCC3Fr9+xRUSN8F49znqbnwCogj o1gORuFnp1kmvPxpT6p9u7xQeYuPoi4su/GMqcDvQEmLhoz/hXeZADYhaXDJUqsYtTyBNyQr+hy I6cTXJTBizX+VdN2r/g== X-Authority-Analysis: v=2.4 cv=I9tVgtgg c=1 sm=1 tr=0 ts=6a5f2b6b cx=c_pps a=gIfcoYsirJbf48DBMSPrZA==:117 a=gIfcoYsirJbf48DBMSPrZA==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=QXcCYyLzdtTjyudCfB6f:22 a=M5GUcnROAAAA:8 a=zjSZ3cS-AAAA:8 a=TCu3pGlnI5_C4XlfF2QA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 a=ZdzWmiyDu4ucoLeQK2uw:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" Handle switchdev FDB add and delete notifications on the PF by queuing work that sends fdb_notify mailbox messages to the AF. The AF queues those updates and pushes L2 rules toward the switchdev image with af2swdev notify messages when firmware is ready. Teach the AF swdev2af path to initialize L2 offload workqueues on firmware up/down and to accept refresh requests that enqueue FDB entries for AF to PF mailbox delivery. Add an AF to PF (and VF) upstream message for FDB refresh, handle it in the VF driver, and treat it like the CGX link event when acknowledging mailbox completion in the AF. On refresh, invoke the switchdev notifier so the host bridge can learn the updated FDB entry. Signed-off-by: Ratheesh Kannoth --- .../net/ethernet/marvell/octeontx2/af/mbox.h | 2 + .../net/ethernet/marvell/octeontx2/af/rvu.c | 2 + .../marvell/octeontx2/af/switch/rvu_sw.c | 51 +- .../marvell/octeontx2/af/switch/rvu_sw.h | 1 + .../marvell/octeontx2/af/switch/rvu_sw_l2.c | 474 ++++++++++++++++++ .../marvell/octeontx2/af/switch/rvu_sw_l2.h | 3 + .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 + .../ethernet/marvell/octeontx2/nic/otx2_vf.c | 44 ++ .../marvell/octeontx2/nic/switch/sw_fdb.c | 225 +++++++++ .../marvell/octeontx2/nic/switch/sw_fdb.h | 1 + .../marvell/octeontx2/nic/switch/sw_nb.c | 12 +- .../marvell/octeontx2/nic/switch/sw_nb.h | 8 +- 12 files changed, 816 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net= /ethernet/marvell/octeontx2/af/mbox.h index a63771d7b102..03ade6ead826 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -1996,6 +1996,7 @@ struct af2pf_fdb_refresh_req { struct mbox_msghdr hdr; u16 pcifunc; u8 mac[6]; + u64 flags; }; =20 struct iface_info { @@ -2035,6 +2036,7 @@ struct fl_info { struct swdev2af_notify_req { struct mbox_msghdr hdr; u64 msg_type; +/* Mutually exclusive message selectors (not a combinable bitmask). */ #define SWDEV2AF_MSG_TYPE_FW_STATUS BIT_ULL(0) #define SWDEV2AF_MSG_TYPE_REFRESH_FDB BIT_ULL(1) #define SWDEV2AF_MSG_TYPE_REFRESH_FL BIT_ULL(2) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/= ethernet/marvell/octeontx2/af/rvu.c index 168a50655351..4b9453519ead 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -23,6 +23,7 @@ #include "cn20k/reg.h" #include "cn20k/api.h" #include "cn20k/npc.h" +#include "switch/rvu_sw.h" =20 #define DRV_NAME "rvu_af" #define DRV_STRING "Marvell OcteonTX2 RVU Admin Function Driver" @@ -3850,6 +3851,7 @@ static void rvu_remove(struct pci_dev *pdev) rvu_fwdata_exit(rvu); rvu_mcs_exit(rvu); rvu_mbox_destroy(&rvu->afpf_wq_info); + rvu_sw_shutdown(); rvu_disable_sriov(rvu); rvu_reset_all_blocks(rvu); rvu_free_hw_resources(rvu); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c index 403d57870efe..b9cd7c7524b9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c @@ -9,6 +9,8 @@ =20 #include "rvu.h" #include "rvu_sw.h" +#include "rvu_sw_l2.h" +#include "rvu_sw_fl.h" =20 u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc) { @@ -26,9 +28,56 @@ u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc) FIELD_PREP(GENMASK_ULL(15, 0), pcifunc); } =20 +static bool rvu_sw_swdev2af_msg_valid(u64 msg_type) +{ + return msg_type =3D=3D SWDEV2AF_MSG_TYPE_FW_STATUS || + msg_type =3D=3D SWDEV2AF_MSG_TYPE_REFRESH_FDB || + msg_type =3D=3D SWDEV2AF_MSG_TYPE_REFRESH_FL; +} + +static int rvu_sw_swdev2af_sender_check(struct rvu *rvu, + struct swdev2af_notify_req *req, + u64 msg_type) +{ + u16 sender =3D req->hdr.pcifunc; + + if (!rvu_sw_swdev2af_msg_valid(msg_type)) + return -EINVAL; + + if (!rvu_is_switch_pcifunc(rvu, sender)) + return -EPERM; + + return 0; +} + int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu, struct swdev2af_notify_req *req, struct msg_rsp *rsp) { - return 0; + int rc; + + rc =3D rvu_sw_swdev2af_sender_check(rvu, req, req->msg_type); + if (rc) + return rc; + + switch (req->msg_type) { + case SWDEV2AF_MSG_TYPE_FW_STATUS: + rc =3D rvu_sw_l2_init_offl_wq(rvu, req->hdr.pcifunc, req->fw_up); + break; + + case SWDEV2AF_MSG_TYPE_REFRESH_FDB: + rc =3D rvu_sw_l2_fdb_list_entry_add(rvu, req->pcifunc, req->mac); + break; + + default: + rc =3D -EOPNOTSUPP; + break; + } + + return rc; +} + +void rvu_sw_shutdown(void) +{ + rvu_sw_l2_shutdown(); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h index e9ad32c84576..a0cb2a9ce7ab 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h @@ -12,5 +12,6 @@ #define RVU_SW_INVALID_PORT_ID ((u32)~0U) =20 u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc); +void rvu_sw_shutdown(void); =20 #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c index 5f805bfa81ed..2e6502d0c48d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c @@ -4,11 +4,485 @@ * Copyright (C) 2026 Marvell. * */ + +#include #include "rvu.h" +#include "rvu_sw.h" +#include "rvu_sw_l2.h" + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +static struct _req_type __maybe_unused \ +*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid) \ +{ \ + struct _req_type *req; \ + \ + req =3D (struct _req_type *)otx2_mbox_alloc_msg_rsp( \ + &rvu->afpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \ + sizeof(struct _rsp_type)); \ + if (!req) \ + return NULL; \ + req->hdr.sig =3D OTX2_MBOX_REQ_SIG; \ + req->hdr.id =3D _id; \ + return req; \ +} +MBOX_UP_AF2SWDEV_MESSAGES +MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES +#undef M + +#define RVU_SW_L2_LIST_MAX 4096 + +struct l2_entry { + struct list_head list; + u64 flags; + u32 port_id; + u8 mac[ETH_ALEN]; +}; + +static DEFINE_MUTEX(l2_offl_list_lock); +static LIST_HEAD(l2_offl_lh); +static atomic_t l2_offl_list_cnt =3D ATOMIC_INIT(0); + +static DEFINE_MUTEX(fdb_refresh_list_lock); +static LIST_HEAD(fdb_refresh_lh); +static atomic_t fdb_refresh_list_cnt =3D ATOMIC_INIT(0); + +struct rvu_sw_l2_work { + struct rvu *rvu; + struct work_struct work; +}; + +/* Work queue for switchdev message handling. There is only one RVU AF + * and one switch block per SoC; rvu_probe() enforces a single AF bind via + * device_bound, so one global workqueue instance per type is sufficient. + */ +static struct rvu_sw_l2_work l2_offl_work; +static struct workqueue_struct *rvu_sw_l2_offl_wq; + +static struct rvu_sw_l2_work fdb_refresh_work; +static struct workqueue_struct *fdb_refresh_wq; + +static bool fw_is_up; +static DEFINE_SPINLOCK(rvu_sw_l2_state_lock); + +static void rvu_sw_l2_list_cnt_warn(struct device *dev, atomic_t *cnt, + const char *name) +{ + int n =3D atomic_read(cnt); + + if (n < 0) + dev_warn(dev, "L2 %s list count underflow: %d\n", name, n); + else if (n > RVU_SW_L2_LIST_MAX) + dev_warn(dev, "L2 %s list count overflow: %d (max %d)\n", + name, n, RVU_SW_L2_LIST_MAX); +} + +static void rvu_sw_l2_list_cnt_inc(struct device *dev, atomic_t *cnt, + const char *name) +{ + atomic_inc(cnt); + rvu_sw_l2_list_cnt_warn(dev, cnt, name); +} + +static void rvu_sw_l2_list_cnt_dec(struct device *dev, atomic_t *cnt, + const char *name) +{ + atomic_dec(cnt); + rvu_sw_l2_list_cnt_warn(dev, cnt, name); +} + +static void rvu_sw_l2_destroy_wqs(struct rvu *rvu) +{ + struct workqueue_struct *offl_wq, *refresh_wq; + struct l2_entry *entry; + + spin_lock_bh(&rvu_sw_l2_state_lock); + rvu->rswitch.flags &=3D ~RVU_SWITCH_FLAG_FW_READY; + rvu->rswitch.pcifunc =3D 0; + fw_is_up =3D false; + spin_unlock_bh(&rvu_sw_l2_state_lock); + + mutex_lock(&fdb_refresh_list_lock); + refresh_wq =3D fdb_refresh_wq; + fdb_refresh_wq =3D NULL; + mutex_unlock(&fdb_refresh_list_lock); + + if (refresh_wq) { + cancel_work_sync(&fdb_refresh_work.work); + destroy_workqueue(refresh_wq); + + mutex_lock(&fdb_refresh_list_lock); + rvu_sw_l2_list_cnt_warn(rvu->dev, &fdb_refresh_list_cnt, + "fdb refresh"); + while (1) { + entry =3D list_first_entry_or_null(&fdb_refresh_lh, + struct l2_entry, list); + if (!entry) + break; + + list_del_init(&entry->list); + kfree(entry); + } + atomic_set(&fdb_refresh_list_cnt, 0); + mutex_unlock(&fdb_refresh_list_lock); + } + + mutex_lock(&l2_offl_list_lock); + offl_wq =3D rvu_sw_l2_offl_wq; + rvu_sw_l2_offl_wq =3D NULL; + mutex_unlock(&l2_offl_list_lock); + + if (offl_wq) { + cancel_work_sync(&l2_offl_work.work); + destroy_workqueue(offl_wq); + + mutex_lock(&l2_offl_list_lock); + rvu_sw_l2_list_cnt_warn(rvu->dev, &l2_offl_list_cnt, "offload"); + while (1) { + entry =3D list_first_entry_or_null(&l2_offl_lh, + struct l2_entry, list); + if (!entry) + break; + + list_del_init(&entry->list); + kfree(entry); + } + atomic_set(&l2_offl_list_cnt, 0); + mutex_unlock(&l2_offl_list_lock); + } +} + +/* High-frequency link state transitions or aggressive FDB + * aging intervals can induce rapid fdb churn. To prevent + * thrashing, inhibit hardware offloading of these transient + * forwarding states to the switching ASIC. Events are queued + * at the tail and processed from the head; when enqueueing a + * new operation, drop older pending opposite operations for the + * same MAC that have not yet reached hardware. When an opposite + * entry is removed, the new operation is dropped as well. + */ +static bool rvu_sw_l2_offl_coalesce_pending_locked(struct rvu *rvu, + struct l2_entry *new_entry) +{ + u64 opposite =3D (new_entry->flags & FDB_ADD) ? FDB_DEL : FDB_ADD; + struct l2_entry *entry, *tmp; + bool coalesced =3D false; + + lockdep_assert_held(&l2_offl_list_lock); + + list_for_each_entry_safe(entry, tmp, &l2_offl_lh, list) { + if (!ether_addr_equal(new_entry->mac, entry->mac)) + continue; + + if (!(entry->flags & opposite)) + continue; + + list_del_init(&entry->list); + rvu_sw_l2_list_cnt_dec(rvu->dev, &l2_offl_list_cnt, "offload"); + kfree(entry); + coalesced =3D true; + } + + return coalesced; +} + +static int rvu_sw_l2_offl_rule_push(struct rvu *rvu, struct l2_entry *l2_e= ntry) +{ + struct af2swdev_notify_req *req; + int swdev_pf; + + swdev_pf =3D rvu_get_pf(rvu->pdev, rvu->rswitch.pcifunc); + + mutex_lock(&rvu->mbox_lock); + req =3D otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf); + if (!req) { + mutex_unlock(&rvu->mbox_lock); + return -ENOMEM; + } + + ether_addr_copy(req->mac, l2_entry->mac); + req->flags =3D l2_entry->flags; + req->port_id =3D l2_entry->port_id; + + otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, swdev_pf); + otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, swdev_pf); + + mutex_unlock(&rvu->mbox_lock); + return 0; +} + +static int rvu_sw_l2_fdb_refresh_send(struct rvu *rvu, u16 pcifunc, u8 *ma= c) +{ + struct af2pf_fdb_refresh_req *req; + int pf, vf; + + if (!is_pf_func_valid(rvu, pcifunc)) + return -EINVAL; + + pf =3D rvu_get_pf(rvu->pdev, pcifunc); + vf =3D (pcifunc & RVU_PFVF_FUNC_MASK) - 1; + + mutex_lock(&rvu->mbox_lock); + + if (!is_cgx_vf(rvu, pcifunc)) { + if (pf >=3D rvu->afpf_wq_info.mbox_up.ndevs) { + mutex_unlock(&rvu->mbox_lock); + return -EINVAL; + } + + req =3D otx2_mbox_alloc_msg_af2pf_fdb_refresh(rvu, pf); + if (!req) { + mutex_unlock(&rvu->mbox_lock); + return -ENOMEM; + } + + req->hdr.pcifunc =3D pcifunc; + ether_addr_copy(req->mac, mac); + req->pcifunc =3D pcifunc; + req->flags =3D FDB_ADD; + + otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pf); + otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pf); + } else { + if (vf < 0 || vf >=3D rvu->afvf_wq_info.mbox_up.ndevs) { + mutex_unlock(&rvu->mbox_lock); + return -EINVAL; + } + + req =3D (struct af2pf_fdb_refresh_req *) + otx2_mbox_alloc_msg_rsp(&rvu->afvf_wq_info.mbox_up, vf, + sizeof(*req), sizeof(struct msg_rsp)); + if (!req) { + mutex_unlock(&rvu->mbox_lock); + return -ENOMEM; + } + req->hdr.sig =3D OTX2_MBOX_REQ_SIG; + req->hdr.id =3D MBOX_MSG_AF2PF_FDB_REFRESH; + + req->hdr.pcifunc =3D pcifunc; + ether_addr_copy(req->mac, mac); + req->pcifunc =3D pcifunc; + req->flags =3D FDB_ADD; + + otx2_mbox_wait_for_zero(&rvu->afvf_wq_info.mbox_up, vf); + otx2_mbox_msg_send_up(&rvu->afvf_wq_info.mbox_up, vf); + } + + mutex_unlock(&rvu->mbox_lock); + + return 0; +} + +static void rvu_sw_l2_fdb_refresh_wq_handler(struct work_struct *work) +{ + struct rvu_sw_l2_work *fdb_work; + struct l2_entry *l2_entry; + + fdb_work =3D container_of(work, struct rvu_sw_l2_work, work); + + while (1) { + mutex_lock(&fdb_refresh_list_lock); + l2_entry =3D list_first_entry_or_null(&fdb_refresh_lh, + struct l2_entry, list); + if (!l2_entry) { + mutex_unlock(&fdb_refresh_list_lock); + return; + } + + list_del_init(&l2_entry->list); + rvu_sw_l2_list_cnt_dec(fdb_work->rvu->dev, &fdb_refresh_list_cnt, + "fdb refresh"); + mutex_unlock(&fdb_refresh_list_lock); + + rvu_sw_l2_fdb_refresh_send(fdb_work->rvu, l2_entry->port_id, + l2_entry->mac); + kfree(l2_entry); + } +} + +static void rvu_sw_l2_offl_rule_wq_handler(struct work_struct *work) +{ + struct rvu_sw_l2_work *offl_work; + struct l2_entry *l2_entry; + int budget =3D 16; + + offl_work =3D container_of(work, struct rvu_sw_l2_work, work); + + while (budget--) { + mutex_lock(&l2_offl_list_lock); + l2_entry =3D list_first_entry_or_null(&l2_offl_lh, struct l2_entry, list= ); + if (!l2_entry) { + mutex_unlock(&l2_offl_list_lock); + return; + } + + list_del_init(&l2_entry->list); + rvu_sw_l2_list_cnt_dec(offl_work->rvu->dev, &l2_offl_list_cnt, + "offload"); + mutex_unlock(&l2_offl_list_lock); + + if (rvu_sw_l2_offl_rule_push(offl_work->rvu, l2_entry)) + dev_err(offl_work->rvu->dev, + "%s: Error to push l2 rule\n", + __func__); + kfree(l2_entry); + } + + mutex_lock(&l2_offl_list_lock); + if (rvu_sw_l2_offl_wq && atomic_read(&l2_offl_list_cnt)) + queue_work(rvu_sw_l2_offl_wq, &l2_offl_work.work); + mutex_unlock(&l2_offl_list_lock); +} + +int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up) +{ + struct rvu_switch *rswitch =3D &rvu->rswitch; + + if (!fw_up) { + rvu_sw_l2_destroy_wqs(rvu); + return 0; + } + + spin_lock_bh(&rvu_sw_l2_state_lock); + if (fw_is_up && rvu_sw_l2_offl_wq && fdb_refresh_wq) { + rswitch->pcifunc =3D pcifunc; + rswitch->flags |=3D RVU_SWITCH_FLAG_FW_READY; + spin_unlock_bh(&rvu_sw_l2_state_lock); + return 0; + } + spin_unlock_bh(&rvu_sw_l2_state_lock); + + if (rvu_sw_l2_offl_wq || fdb_refresh_wq) + rvu_sw_l2_destroy_wqs(rvu); + + l2_offl_work.rvu =3D rvu; + INIT_WORK(&l2_offl_work.work, rvu_sw_l2_offl_rule_wq_handler); + rvu_sw_l2_offl_wq =3D alloc_workqueue("swdev_rvu_sw_l2_offl_wq", 0, 0); + if (!rvu_sw_l2_offl_wq) { + dev_err(rvu->dev, "L2 offl workqueue allocation failed\n"); + return -ENOMEM; + } + + fdb_refresh_work.rvu =3D rvu; + INIT_WORK(&fdb_refresh_work.work, rvu_sw_l2_fdb_refresh_wq_handler); + fdb_refresh_wq =3D alloc_workqueue("swdev_fdb_refresh_wq", 0, 0); + if (!fdb_refresh_wq) { + dev_err(rvu->dev, "fdb refresh workqueue allocation failed\n"); + destroy_workqueue(rvu_sw_l2_offl_wq); + rvu_sw_l2_offl_wq =3D NULL; + return -ENOMEM; + } + + spin_lock_bh(&rvu_sw_l2_state_lock); + fw_is_up =3D true; + rswitch->pcifunc =3D pcifunc; + rswitch->flags |=3D RVU_SWITCH_FLAG_FW_READY; + spin_unlock_bh(&rvu_sw_l2_state_lock); + + return 0; +} + +int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac) +{ + struct workqueue_struct *wq; + struct l2_entry *l2_entry; + + if (!is_pf_func_valid(rvu, pcifunc)) + return -EINVAL; + + if (atomic_read(&fdb_refresh_list_cnt) >=3D RVU_SW_L2_LIST_MAX) { + rvu_sw_l2_list_cnt_warn(rvu->dev, &fdb_refresh_list_cnt, + "fdb refresh"); + return -ENOMEM; + } + + l2_entry =3D kcalloc(1, sizeof(*l2_entry), GFP_KERNEL); + if (!l2_entry) + return -ENOMEM; + + l2_entry->port_id =3D pcifunc; + ether_addr_copy(l2_entry->mac, mac); + + mutex_lock(&fdb_refresh_list_lock); + wq =3D fdb_refresh_wq; + if (!wq) { + mutex_unlock(&fdb_refresh_list_lock); + kfree(l2_entry); + return -EINVAL; + } + + if (atomic_read(&fdb_refresh_list_cnt) >=3D RVU_SW_L2_LIST_MAX) { + rvu_sw_l2_list_cnt_warn(rvu->dev, &fdb_refresh_list_cnt, + "fdb refresh"); + mutex_unlock(&fdb_refresh_list_lock); + kfree(l2_entry); + return -ENOMEM; + } + list_add_tail(&l2_entry->list, &fdb_refresh_lh); + rvu_sw_l2_list_cnt_inc(rvu->dev, &fdb_refresh_list_cnt, "fdb refresh"); + queue_work(wq, &fdb_refresh_work.work); + mutex_unlock(&fdb_refresh_list_lock); + + return 0; +} =20 int rvu_mbox_handler_fdb_notify(struct rvu *rvu, struct fdb_notify_req *req, struct msg_rsp *rsp) { + struct workqueue_struct *wq; + struct l2_entry *l2_entry; + + spin_lock_bh(&rvu_sw_l2_state_lock); + if (!(rvu->rswitch.flags & RVU_SWITCH_FLAG_FW_READY)) { + spin_unlock_bh(&rvu_sw_l2_state_lock); + return 0; + } + spin_unlock_bh(&rvu_sw_l2_state_lock); + + if (atomic_read(&l2_offl_list_cnt) >=3D RVU_SW_L2_LIST_MAX) { + rvu_sw_l2_list_cnt_warn(rvu->dev, &l2_offl_list_cnt, "offload"); + return -ENOMEM; + } + + l2_entry =3D kcalloc(1, sizeof(*l2_entry), GFP_KERNEL); + if (!l2_entry) + return -ENOMEM; + + l2_entry->port_id =3D rvu_sw_port_id(rvu, req->hdr.pcifunc); + ether_addr_copy(l2_entry->mac, req->mac); + l2_entry->flags =3D req->flags; + + mutex_lock(&l2_offl_list_lock); + wq =3D rvu_sw_l2_offl_wq; + if (!wq) { + mutex_unlock(&l2_offl_list_lock); + kfree(l2_entry); + return 0; + } + + if (atomic_read(&l2_offl_list_cnt) >=3D RVU_SW_L2_LIST_MAX) { + rvu_sw_l2_list_cnt_warn(rvu->dev, &l2_offl_list_cnt, "offload"); + mutex_unlock(&l2_offl_list_lock); + kfree(l2_entry); + return -ENOMEM; + } + if (rvu_sw_l2_offl_coalesce_pending_locked(rvu, l2_entry)) { + mutex_unlock(&l2_offl_list_lock); + kfree(l2_entry); + return 0; + } + list_add_tail(&l2_entry->list, &l2_offl_lh); + rvu_sw_l2_list_cnt_inc(rvu->dev, &l2_offl_list_cnt, "offload"); + queue_work(wq, &l2_offl_work.work); + mutex_unlock(&l2_offl_list_lock); + return 0; } + +void rvu_sw_l2_shutdown(void) +{ + if (!fdb_refresh_wq && !rvu_sw_l2_offl_wq) + return; + + rvu_sw_l2_destroy_wqs(l2_offl_work.rvu); +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h index ff28612150c9..6685431d60a2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h @@ -8,4 +8,7 @@ #ifndef RVU_SW_L2_H #define RVU_SW_L2_H =20 +int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up); +int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac); +void rvu_sw_l2_shutdown(void); #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers= /net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 2e33b33ec993..0cd6049c637e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -28,6 +28,7 @@ #include #include "cn10k_ipsec.h" #include "otx2_xsk.h" +#include "switch/sw_nb.h" =20 #define DRV_NAME "rvu_nicpf" #define DRV_STRING "Marvell RVU NIC Physical Function Driver" @@ -993,6 +994,7 @@ static int otx2_process_mbox_msg_up(struct otx2_nic *pf, MBOX_UP_CGX_MESSAGES MBOX_UP_MCS_MESSAGES MBOX_UP_REP_MESSAGES +MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES #undef M break; default: diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers= /net/ethernet/marvell/octeontx2/nic/otx2_vf.c index b022f52c6845..6f2fc4caf70c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -9,6 +9,7 @@ #include #include #include +#include =20 #include "otx2_common.h" #include "otx2_reg.h" @@ -114,6 +115,33 @@ static void otx2vf_vfaf_mbox_handler(struct work_struc= t *work) } } =20 +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) +static int otx2vf_mbox_af2pf_fdb_refresh(struct otx2_nic *vf, + struct af2pf_fdb_refresh_req *req, + struct msg_rsp *rsp) +{ + struct switchdev_notifier_fdb_info item =3D {0}; + + item.addr =3D req->mac; + item.info.dev =3D vf->netdev; + if (req->flags & FDB_DEL) + call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, + item.info.dev, &item.info, NULL); + else + call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, + item.info.dev, &item.info, NULL); + + return 0; +} +#else +static int otx2vf_mbox_af2pf_fdb_refresh(struct otx2_nic *vf, + struct af2pf_fdb_refresh_req *req, + struct msg_rsp *rsp) +{ + return 0; +} +#endif + static int otx2vf_process_mbox_msg_up(struct otx2_nic *vf, struct mbox_msghdr *req) { @@ -141,6 +169,22 @@ static int otx2vf_process_mbox_msg_up(struct otx2_nic = *vf, err =3D otx2_mbox_up_handler_cgx_link_event( vf, (struct cgx_link_info_msg *)req, rsp); return err; + + case MBOX_MSG_AF2PF_FDB_REFRESH: + rsp =3D (struct msg_rsp *)otx2_mbox_alloc_msg(&vf->mbox.mbox_up, 0, + sizeof(struct msg_rsp)); + if (!rsp) + return -ENOMEM; + + rsp->hdr.id =3D MBOX_MSG_AF2PF_FDB_REFRESH; + rsp->hdr.sig =3D OTX2_MBOX_RSP_SIG; + rsp->hdr.pcifunc =3D req->pcifunc; + rsp->hdr.rc =3D 0; + err =3D otx2vf_mbox_af2pf_fdb_refresh(vf, + (struct af2pf_fdb_refresh_req *)req, + rsp); + return err; + default: otx2_reply_invalid_msg(&vf->mbox.mbox_up, 0, 0, req->id); return -ENODEV; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c index 6842c8d91ffc..e9439219c091 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c @@ -4,13 +4,238 @@ * Copyright (C) 2026 Marvell. * */ +#include +#include +#include +#include +#include +#include + +#include "../otx2_reg.h" +#include "../otx2_common.h" +#include "../otx2_struct.h" +#include "../cn10k.h" +#include "sw_nb.h" #include "sw_fdb.h" =20 +#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + +int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf, + struct af2pf_fdb_refresh_req *req, + struct msg_rsp *rsp) +{ + return 0; +} + +#else + +#define SW_FDB_LIST_MAX 4096 + +static DEFINE_SPINLOCK(sw_fdb_llock); +static LIST_HEAD(sw_fdb_lh); +static atomic_t sw_fdb_list_cnt =3D ATOMIC_INIT(0); + +struct sw_fdb_list_entry { + struct list_head list; + u64 flags; + struct otx2_nic *pf; + netdevice_tracker dev_tracker; + u8 mac[ETH_ALEN]; + bool add_fdb; +}; + +static struct workqueue_struct *sw_fdb_wq; +static struct work_struct sw_fdb_work; + +static void sw_fdb_list_cnt_warn(struct net_device *netdev) +{ + int n =3D atomic_read(&sw_fdb_list_cnt); + + if (n < 0) + netdev_warn(netdev, "FDB list count underflow: %d\n", n); + else if (n > SW_FDB_LIST_MAX) + netdev_warn(netdev, "FDB list count overflow: %d (max %d)\n", + n, SW_FDB_LIST_MAX); +} + +static int sw_fdb_list_count(void) +{ + return atomic_read(&sw_fdb_list_cnt); +} + +static void sw_fdb_list_cnt_inc(struct net_device *netdev) +{ + atomic_inc(&sw_fdb_list_cnt); + sw_fdb_list_cnt_warn(netdev); +} + +static void sw_fdb_list_cnt_dec(struct net_device *netdev) +{ + atomic_dec(&sw_fdb_list_cnt); + sw_fdb_list_cnt_warn(netdev); +} + +static int sw_fdb_add_or_del(struct otx2_nic *pf, + const unsigned char *addr, + bool add_fdb) +{ + struct fdb_notify_req *req; + int rc; + + mutex_lock(&pf->mbox.lock); + req =3D otx2_mbox_alloc_msg_fdb_notify(&pf->mbox); + if (!req) { + rc =3D -ENOMEM; + goto out; + } + + ether_addr_copy(req->mac, addr); + req->flags =3D add_fdb ? FDB_ADD : FDB_DEL; + + rc =3D otx2_sync_mbox_msg(&pf->mbox); +out: + mutex_unlock(&pf->mbox.lock); + return rc; +} + +static void sw_fdb_wq_handler(struct work_struct *work) +{ + struct sw_fdb_list_entry *entry; + struct workqueue_struct *wq; + LIST_HEAD(tlist); + + spin_lock_bh(&sw_fdb_llock); + list_splice_init(&sw_fdb_lh, &tlist); + spin_unlock_bh(&sw_fdb_llock); + + while ((entry =3D + list_first_entry_or_null(&tlist, + struct sw_fdb_list_entry, + list)) !=3D NULL) { + list_del_init(&entry->list); + sw_fdb_list_cnt_dec(entry->pf->netdev); + if (sw_fdb_add_or_del(entry->pf, entry->mac, entry->add_fdb)) + netdev_err(entry->pf->netdev, + "Error to add/del fdb %pM entry\n", + entry->mac); + netdev_put(entry->pf->netdev, &entry->dev_tracker); + kfree(entry); + } + + spin_lock_bh(&sw_fdb_llock); + wq =3D sw_fdb_wq; + if (wq && !list_empty(&sw_fdb_lh)) + queue_work(wq, &sw_fdb_work); + spin_unlock_bh(&sw_fdb_llock); +} + +int sw_fdb_add_to_list(struct net_device *dev, u8 *mac, bool add_fdb) +{ + struct otx2_nic *pf =3D netdev_priv(dev); + struct sw_fdb_list_entry *entry; + struct workqueue_struct *wq; + + spin_lock_bh(&sw_fdb_llock); + if (!sw_fdb_wq) { + spin_unlock_bh(&sw_fdb_llock); + return -EINVAL; + } + spin_unlock_bh(&sw_fdb_llock); + + if (sw_fdb_list_count() >=3D SW_FDB_LIST_MAX) + return -ENOMEM; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + + ether_addr_copy(entry->mac, mac); + entry->add_fdb =3D add_fdb; + entry->pf =3D pf; + netdev_hold(dev, &entry->dev_tracker, GFP_ATOMIC); + + spin_lock_bh(&sw_fdb_llock); + wq =3D sw_fdb_wq; + if (wq) { + list_add_tail(&entry->list, &sw_fdb_lh); + sw_fdb_list_cnt_inc(dev); + queue_work(wq, &sw_fdb_work); + } + spin_unlock_bh(&sw_fdb_llock); + + if (!wq) { + netdev_put(dev, &entry->dev_tracker); + kfree(entry); + return -EINVAL; + } + + return 0; +} + int sw_fdb_init(void) { + INIT_WORK(&sw_fdb_work, sw_fdb_wq_handler); + sw_fdb_wq =3D alloc_workqueue("sw_fdb_wq", 0, 0); + if (!sw_fdb_wq) + return -ENOMEM; + return 0; } =20 void sw_fdb_deinit(void) { + struct sw_fdb_list_entry *entry; + struct workqueue_struct *wq; + LIST_HEAD(tlist); + + spin_lock_bh(&sw_fdb_llock); + wq =3D sw_fdb_wq; + sw_fdb_wq =3D NULL; + spin_unlock_bh(&sw_fdb_llock); + + if (!wq) + return; + + cancel_work_sync(&sw_fdb_work); + destroy_workqueue(wq); + + spin_lock_bh(&sw_fdb_llock); + list_splice_init(&sw_fdb_lh, &tlist); + spin_unlock_bh(&sw_fdb_llock); + + while ((entry =3D + list_first_entry_or_null(&tlist, + struct sw_fdb_list_entry, + list)) !=3D NULL) { + list_del_init(&entry->list); + sw_fdb_list_cnt_dec(entry->pf->netdev); + netdev_put(entry->pf->netdev, &entry->dev_tracker); + kfree(entry); + } +} + +int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf, + struct af2pf_fdb_refresh_req *req, + struct msg_rsp *rsp) +{ + struct switchdev_notifier_fdb_info item =3D {0}; + + /* FDB refresh is raised from the switch offload path (AF) after + * switchdev FDB updates and is delivered to the PF mailbox. + * Refreshes targeting the PF netdev are applied here on + * pf->netdev; VF-targeted refreshes are forwarded on the PF-VF + * mailbox and handled in otx2vf_mbox_af2pf_fdb_refresh() on + * vf->netdev (see rvu_sw_l2_fdb_refresh_send()). + */ + item.addr =3D req->mac; + item.info.dev =3D pf->netdev; + if (req->flags & FDB_DEL) + call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, + item.info.dev, &item.info, NULL); + else + call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, + item.info.dev, &item.info, NULL); + + return 0; } +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h index d4314d6d3ee4..3b06a77e6b56 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h @@ -7,6 +7,7 @@ #ifndef SW_FDB_H_ #define SW_FDB_H_ =20 +int sw_fdb_add_to_list(struct net_device *dev, u8 *mac, bool add_fdb); void sw_fdb_deinit(void); int sw_fdb_init(void); =20 diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c index 8a09876e8297..e908cc50a611 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c @@ -169,13 +169,17 @@ static int sw_nb_fdb_event(struct notifier_block *unu= sed, =20 switch (event) { case SWITCHDEV_FDB_ADD_TO_DEVICE: - if (fdb_info->is_local) - break; - break; - case SWITCHDEV_FDB_DEL_TO_DEVICE: if (fdb_info->is_local) break; + /* dev is the bridge port that learned the FDB + * (SWITCHDEV_FDB_*_TO_DEVICE), not the bridge master. + * sw_nb_is_valid_dev() limits this to Cavium-offloaded + * setups; only Cavium PF/representor netdevs are supported + * as bridge ports today (VLAN/virt under bridge is TODO). + */ + sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, + event =3D=3D SWITCHDEV_FDB_ADD_TO_DEVICE); break; =20 default: diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h index e995c0e6046b..a701574de1e4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h @@ -14,6 +14,10 @@ struct otx2_nic; struct af2pf_fdb_refresh_req; struct msg_rsp; =20 +int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf, + struct af2pf_fdb_refresh_req *req, + struct msg_rsp *rsp); + #if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) enum { OTX2_DEV_UP =3D 1, @@ -32,10 +36,6 @@ int sw_nb_unregister(struct net_device *netdev); bool sw_nb_is_valid_dev(struct net_device *netdev); struct net_device *sw_nb_resolve_pf_dev(struct net_device *dev); =20 -int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf, - struct af2pf_fdb_refresh_req *req, - struct msg_rsp *rsp); - bool sw_nb_is_cavium_dev(struct net_device *netdev); int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev); int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netde= v); --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com [67.231.148.174]) (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 B795943787C; Tue, 21 Jul 2026 08:19:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.148.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621946; cv=none; b=o8+LWkmjQnBW45wAMtDg1rOQlPwOeU+mTJoC+xpDtixyO7QA1qlVpJLkzPTs82b/8oDN/hragOLocjOaqou5X3VPJZO3paYmlm0jBhGQpNx+6Lp0lSCzOSqSFNJnN3OZXdF6ykyGTLMuRVnCsgWhPKXGroDZ2b8azXafzJ47+9c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621946; c=relaxed/simple; bh=oWGZZA7MGqt+4EbsXSdoppV1BfPsuPKVNYcT0Tj889k=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=hmVMFlGt0r+I7i5hGC4FEWUXDkKsQ0A7RzNLfTSYm6tjdwTB+iBgoTpA8WSYFIEZQBc7t9vSEEdL3bB89Xp8wchIuLkuNQBefuyFmASAc+EPGg1EGynVmfPzKKqA5NLWVHgkVVqg+9Ee5lUFrs0QX1noCrSiquh2o4N5e2PglFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=YSVEi1uM; arc=none smtp.client-ip=67.231.148.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="YSVEi1uM" Received: from pps.filterd (m0045849.ppops.net [127.0.0.1]) by mx0a-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66KNcCU4652687; Tue, 21 Jul 2026 01:18:54 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=k YreaVhN6iv7oIKzWVE5zzZYtPQ/2eBj0NZiSTtFmWE=; b=YSVEi1uM8Z7MlMwv6 93Xd6R2NiVVX7ro4k8Tn8V7IjwEvGk5sPdZaw+nVyGMRv1I8qfIMXYCTYjRtYUN7 47v1+Xv6aoSMJXr1qs34VKmWU0hEwYMQOz0LrjYot/aR39awLgbObVbk/QvfR95v VV0YmEmPqnq4qj/pSGVlRisOEa5OKDhAxYJuoJ2F+68/T+mdKLVsg7udDwgDfrAF acBSTnFIvzsxrg5Wnf0Qk1S3M1w0wWUIxD748AJw5Sht+BZWRwzllWVY8rlOMkKr 9y4rMEnXJ2swlC2oS7l/wv395jKmlwx4AyedJ/MW+ui5tUB7pFc03BPbBDk7JkYl 6N68A== Received: from dc5-exch05.marvell.com ([199.233.59.128]) by mx0a-0016f401.pphosted.com (PPS) with ESMTPS id 4fgdbcyufc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:54 -0700 (PDT) Received: from DC5-EXCH05.marvell.com (10.69.176.209) by DC5-EXCH05.marvell.com (10.69.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:53 -0700 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH05.marvell.com (10.69.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:53 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 429DB3F7091; Tue, 21 Jul 2026 01:18:51 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 8/9] octeontx2: switch: offload host FIB updates to switch via AF mailbox Date: Tue, 21 Jul 2026 13:48:23 +0530 Message-ID: <20260721081824.1430607-9-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: I5jlu9qIGy9qn37OMFUgFMULbyvHDK0M X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NyBTYWx0ZWRfX8JaCVj++Byk7 /kSiFzu6++klPukY5nAj6hfdXIEnLKEzB4wiezHwBsxVgrPFvxgFdEnWOR29gupIFwP3LhU1xM9 wiRDp4l0tr4j6XKpj8N+pX48NWhwtPY= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NyBTYWx0ZWRfX+dRNbMLBkWF+ TAswjbETsashYD05F12LNICwOs0J6Rh0CrRV4H7ebSqM/m1TVQ/Nd6FhxCjkQjpwH6sv7NpJ/h9 iQRjLc/lpMtQneYizxg1PgD/Rusi97ksbURm+HgTzNFpWAX8WgJWsSWaddIJ4Yc7/sSp2Vy4nxh 67J7SJka/SdL0KUXXJzM3qCpzRxwxoe2PIA+d0bDgFiyEJVn0qH8yAPV/k3NVeSPql9VZpaLMaK sKxLuwhiJUxedgNN7tGn7x/JCABd4ukejTzWtgCEua/9HNomd4AiNiBCL2pGO2CP/Ci1pjzoycL KgPkOqKi6W+khWvR11BUfXTYtUWWRNmt2q3XbjQA3dzTPGA2/qbbT2zMICU1HIW7B3CjD95FnAT kX9A8U2fd6t/xQNeYKymONwlWeCx4sRV1KIGH9PtU4uZ7wtkqiyaSrtMP7dCJrjCZEbg9XnqdpY wb/u3AAN5XeVJpArjHw== X-Proofpoint-GUID: I5jlu9qIGy9qn37OMFUgFMULbyvHDK0M X-Authority-Analysis: v=2.4 cv=Nq3htcdJ c=1 sm=1 tr=0 ts=6a5f2b6e cx=c_pps a=rEv8fa4AjpPjGxpoe8rlIQ==:117 a=rEv8fa4AjpPjGxpoe8rlIQ==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=EAYMVhzMl8SCOHhVQcBL:22 a=M5GUcnROAAAA:8 a=cLNcnrBKlAuJ8xe52PgA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" Queue IPv4/IPv6 FIB-derived updates from the switch notifier path and handle fib_notify in the RVU AF by batching fib_entry structures and sending them to the switch PF through the AF-to-switchdev FIB_CMD. Require the switch firmware to be ready before accepting offload work. Signed-off-by: Ratheesh Kannoth --- .../marvell/octeontx2/af/switch/rvu_sw.c | 3 +- .../marvell/octeontx2/af/switch/rvu_sw_l3.c | 243 ++++++++++++++++++ .../marvell/octeontx2/af/switch/rvu_sw_l3.h | 1 + .../marvell/octeontx2/nic/switch/sw_fib.c | 225 ++++++++++++++++ .../marvell/octeontx2/nic/switch/sw_fib.h | 14 + .../marvell/octeontx2/nic/switch/sw_nb.c | 11 +- .../marvell/octeontx2/nic/switch/sw_nb_v4.c | 192 +++++++------- .../marvell/octeontx2/nic/switch/sw_nb_v6.c | 22 +- .../marvell/octeontx2/nic/switch/sw_nb_v6.h | 31 ++- 9 files changed, 640 insertions(+), 102 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c index b9cd7c7524b9..1151ba47284b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c @@ -6,10 +6,10 @@ */ =20 #include - #include "rvu.h" #include "rvu_sw.h" #include "rvu_sw_l2.h" +#include "rvu_sw_l3.h" #include "rvu_sw_fl.h" =20 u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc) @@ -80,4 +80,5 @@ int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu, void rvu_sw_shutdown(void) { rvu_sw_l2_shutdown(); + rvu_sw_l3_shutdown(); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c index 2b798d5f0644..c47b93a66a3b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c @@ -4,11 +4,254 @@ * Copyright (C) 2026 Marvell. * */ + +#include #include "rvu.h" +#include "rvu_sw.h" +#include "rvu_sw_l3.h" + +static struct af2swdev_notify_req __maybe_unused +*otx2_mbox_alloc_msg_af2swdev_notify(struct rvu *rvu, int devid) +{ + struct af2swdev_notify_req *req; + + req =3D (struct af2swdev_notify_req *) + otx2_mbox_alloc_msg_rsp(&rvu->afpf_wq_info.mbox_up, devid, + sizeof(*req), sizeof(struct msg_rsp)); + if (!req) + return NULL; + req->hdr.sig =3D OTX2_MBOX_REQ_SIG; + req->hdr.id =3D MBOX_MSG_AF2SWDEV; + return req; +} + +#define RVU_SW_L3_BATCH_MAX \ + ((int)(sizeof_field(struct af2swdev_notify_req, entry) / \ + sizeof(struct fib_entry))) + +struct l3_entry { + struct list_head list; + /* Always this AF driver's rvu; stored for clarity only (single RVU). */ + struct rvu *rvu; + u32 port_id; + int cnt; + struct fib_entry entry[]; +}; + +static DEFINE_MUTEX(l3_offl_llock); +static LIST_HEAD(l3_offl_lh); + +static struct workqueue_struct *sw_l3_offl_wq; +static void sw_l3_offl_work_handler(struct work_struct *work); +static DECLARE_DELAYED_WORK(l3_offl_work, sw_l3_offl_work_handler); + +/* + * FIB offload to the switch ASIC: one octeontx2 AF driver instance, one + * switch PF (switchdev), and one sw_l3_offl_wq per SoC. + */ + +static void rvu_sw_l3_drain_list(struct list_head *lh) +{ + struct l3_entry *entry; + + while ((entry =3D list_first_entry_or_null(lh, struct l3_entry, list))) { + list_del(&entry->list); + kfree(entry); + } +} + +static void rvu_sw_l3_queue_work(void) +{ + if (sw_l3_offl_wq) + queue_delayed_work(sw_l3_offl_wq, &l3_offl_work, + msecs_to_jiffies(10)); +} + +static int rvu_sw_l3_ensure_wq(void) +{ + if (sw_l3_offl_wq) + return 0; + + sw_l3_offl_wq =3D alloc_workqueue("sw_af_fib_wq", 0, 0); + if (!sw_l3_offl_wq) + return -ENOMEM; + + return 0; +} + +static int rvu_sw_l3_offl_rule_push(struct list_head *lh) +{ + struct af2swdev_notify_req *req; + struct fib_entry *entry, *dst; + struct l3_entry *l3_entry; + struct rvu *rvu; + int tot_cnt =3D 0; + int swdev_pf; + int sz, cnt, i; + bool rc; + + BUILD_BUG_ON(sizeof_field(struct af2swdev_notify_req, entry) !=3D + sizeof(struct fib_entry) * RVU_SW_L3_BATCH_MAX); + + l3_entry =3D list_first_entry_or_null(lh, struct l3_entry, list); + if (!l3_entry) + return 0; + + /* + * Octeontx2 has a single AF (one struct rvu) per RVU chip. All queued + * entries therefore share the same rvu and the same switch PF below. + * Host PF identity is carried per fib_entry (port_id), not by picking + * a different switch PF here. + */ + rvu =3D l3_entry->rvu; + swdev_pf =3D rvu_get_pf(rvu->pdev, rvu->rswitch.pcifunc); + + mutex_lock(&rvu->mbox_lock); + req =3D otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf); + if (!req) { + mutex_unlock(&rvu->mbox_lock); + return -ENOMEM; + } + + dst =3D &req->entry[0]; + /* + * Batch fib_entry records from multiple host PF notifies into one + * af2swdev message. Safe on octeontx2: every l3_entry targets the + * same switch PF; egress port is encoded in each fib_entry.port_id. + * + * Entries are removed from lh and freed once copied into the mbox + * buffer, before the send attempt. If otx2_mbox_wait_for_zero() or + * the upstream send fails, that batch is lost with no replay path and + * the switch FIB may diverge from the host; tolerating that is a + * known limitation for now. + */ + while ((l3_entry =3D + list_first_entry_or_null(lh, + struct l3_entry, list)) !=3D NULL) { + entry =3D l3_entry->entry; + cnt =3D l3_entry->cnt; + + /* af2swdev_notify_req.entry[] holds RVU_SW_L3_BATCH_MAX slots; + * stop before copying the next l3_entry when the mbox buffer + * would overflow. Leftovers stay on lh and are re-queued. + */ + if (tot_cnt + cnt > RVU_SW_L3_BATCH_MAX) + break; + + sz =3D sizeof(*entry) * cnt; + + memcpy(dst, entry, sz); + for (i =3D 0; i < cnt; i++) + dst[i].port_id =3D l3_entry->port_id; + tot_cnt +=3D cnt; + dst +=3D cnt; + + list_del_init(&l3_entry->list); + kfree(l3_entry); + } + if (!tot_cnt) { + mutex_unlock(&rvu->mbox_lock); + return -EINVAL; + } + + req->flags =3D FIB_CMD; + req->cnt =3D tot_cnt; + + rc =3D otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, swdev_pf); + if (rc) + otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, swdev_pf); + + mutex_unlock(&rvu->mbox_lock); + return rc ? 0 : -EFAULT; +} + +static void sw_l3_offl_work_handler(struct work_struct *work) +{ + struct list_head l3lh; + + INIT_LIST_HEAD(&l3lh); + + mutex_lock(&l3_offl_llock); + if (list_empty(&l3_offl_lh)) { + mutex_unlock(&l3_offl_llock); + return; + } + list_splice_init(&l3_offl_lh, &l3lh); + mutex_unlock(&l3_offl_llock); + + if (rvu_sw_l3_offl_rule_push(&l3lh)) + pr_err("%s: Error to push rules\n", __func__); + + /* rvu_sw_l3_offl_rule_push() may leave entries when a batch is full. */ + if (!list_empty(&l3lh)) { + mutex_lock(&l3_offl_llock); + list_splice(&l3lh, &l3_offl_lh); + mutex_unlock(&l3_offl_llock); + if (sw_l3_offl_wq) + queue_delayed_work(sw_l3_offl_wq, &l3_offl_work, + msecs_to_jiffies(100)); + return; + } + + mutex_lock(&l3_offl_llock); + if (!list_empty(&l3_offl_lh)) + rvu_sw_l3_queue_work(); + mutex_unlock(&l3_offl_llock); +} =20 int rvu_mbox_handler_fib_notify(struct rvu *rvu, struct fib_notify_req *req, struct msg_rsp *rsp) { + struct l3_entry *l3_entry; + int sz, rc; + + if (!(rvu->rswitch.flags & RVU_SWITCH_FLAG_FW_READY)) + return -EAGAIN; + + /* Reject single notifies larger than af2swdev_notify_req.entry[]. */ + if (!req->cnt || req->cnt > RVU_SW_L3_BATCH_MAX) + return -EINVAL; + + sz =3D req->cnt * sizeof(struct fib_entry); + + l3_entry =3D kcalloc(1, sizeof(*l3_entry) + sz, GFP_KERNEL); + if (!l3_entry) + return -ENOMEM; + + l3_entry->port_id =3D rvu_sw_port_id(rvu, req->hdr.pcifunc); + l3_entry->rvu =3D rvu; + l3_entry->cnt =3D req->cnt; + INIT_LIST_HEAD(&l3_entry->list); + memcpy(l3_entry->entry, req->entry, sz); + + /* Host PFs on this RVU share one AF and one switch PF offload path. */ + mutex_lock(&l3_offl_llock); + rc =3D rvu_sw_l3_ensure_wq(); + if (rc) { + mutex_unlock(&l3_offl_llock); + kfree(l3_entry); + return rc; + } + + list_add_tail(&l3_entry->list, &l3_offl_lh); + if (sw_l3_offl_wq) + rvu_sw_l3_queue_work(); + mutex_unlock(&l3_offl_llock); + return 0; } + +void rvu_sw_l3_shutdown(void) +{ + if (!sw_l3_offl_wq) + return; + + cancel_delayed_work_sync(&l3_offl_work); + destroy_workqueue(sw_l3_offl_wq); + sw_l3_offl_wq =3D NULL; + + mutex_lock(&l3_offl_llock); + rvu_sw_l3_drain_list(&l3_offl_lh); + mutex_unlock(&l3_offl_llock); +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h index ac8c4f9ba5ac..153f1415466d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h @@ -8,4 +8,5 @@ #ifndef RVU_SW_L3_H #define RVU_SW_L3_H =20 +void rvu_sw_l3_shutdown(void); #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c index 41a9c5fb58fa..308ce3048a8d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c @@ -8,13 +8,238 @@ =20 #if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) =20 +#include +#include +#include +#include +#include +#include +#include + +#include "../otx2_reg.h" +#include "../otx2_common.h" +#include "../otx2_struct.h" +#include "../cn10k.h" +#include "sw_nb.h" + +#define SW_FIB_BATCH_MAX 16 +#define SW_FIB_LIST_MAX 4096 + +/* + * One switch PF registers notifiers via sw_nb_register(); a second call + * returns -EBUSY. A single sw_fib_wq therefore serves the one switchdev + * instance on octeontx2, matching the FDB offload path. + */ +static DEFINE_SPINLOCK(sw_fib_llock); +static LIST_HEAD(sw_fib_lh); +static atomic_t sw_fib_list_cnt =3D ATOMIC_INIT(0); + +static struct workqueue_struct *sw_fib_wq; +static void sw_fib_work_handler(struct work_struct *work); +static DECLARE_DELAYED_WORK(sw_fib_work, sw_fib_work_handler); + +struct sw_fib_list_entry { + struct list_head lh; + struct otx2_nic *pf; + netdevice_tracker dev_tracker; + int cnt; + struct fib_entry *entry; +}; + +static void sw_fib_list_cnt_warn(struct net_device *netdev) +{ + int n =3D atomic_read(&sw_fib_list_cnt); + + if (n < 0) + netdev_warn(netdev, "FIB list count underflow: %d\n", n); + else if (n > SW_FIB_LIST_MAX) + netdev_warn(netdev, "FIB list count overflow: %d (max %d)\n", + n, SW_FIB_LIST_MAX); +} + +static int sw_fib_list_count(void) +{ + return atomic_read(&sw_fib_list_cnt); +} + +static void sw_fib_list_cnt_inc(struct net_device *netdev) +{ + atomic_inc(&sw_fib_list_cnt); + sw_fib_list_cnt_warn(netdev); +} + +static void sw_fib_list_cnt_dec(struct net_device *netdev) +{ + atomic_dec(&sw_fib_list_cnt); + sw_fib_list_cnt_warn(netdev); +} + +static int sw_fib_notify(struct otx2_nic *pf, + int cnt, + struct fib_entry *entry) +{ + struct fib_notify_req *req; + int rc; + + if (cnt > SW_FIB_BATCH_MAX) + return -EINVAL; + + mutex_lock(&pf->mbox.lock); + req =3D otx2_mbox_alloc_msg_fib_notify(&pf->mbox); + if (!req) { + rc =3D -ENOMEM; + goto out; + } + + req->cnt =3D cnt; + memcpy(req->entry, entry, sizeof(*entry) * cnt); + + rc =3D otx2_sync_mbox_msg(&pf->mbox); +out: + mutex_unlock(&pf->mbox.lock); + return rc; +} + +static void sw_fib_work_handler(struct work_struct *work) +{ + struct sw_fib_list_entry *lentry; + LIST_HEAD(tlist); + + spin_lock_bh(&sw_fib_llock); + list_splice_init(&sw_fib_lh, &tlist); + spin_unlock_bh(&sw_fib_llock); + + while ((lentry =3D + list_first_entry_or_null(&tlist, + struct sw_fib_list_entry, lh)) !=3D NULL) { + list_del_init(&lentry->lh); + if (sw_fib_notify(lentry->pf, lentry->cnt, lentry->entry)) { + netdev_err(lentry->pf->netdev, + "Failed to notify FIB update to AF, will retry\n"); + spin_lock_bh(&sw_fib_llock); + if (sw_fib_wq) { + list_add(&lentry->lh, &sw_fib_lh); + queue_delayed_work(sw_fib_wq, &sw_fib_work, + msecs_to_jiffies(100)); + spin_unlock_bh(&sw_fib_llock); + continue; + } + spin_unlock_bh(&sw_fib_llock); + netdev_put(lentry->pf->netdev, &lentry->dev_tracker); + sw_fib_list_cnt_dec(lentry->pf->netdev); + kfree(lentry->entry); + kfree(lentry); + continue; + } + sw_fib_list_cnt_dec(lentry->pf->netdev); + netdev_put(lentry->pf->netdev, &lentry->dev_tracker); + kfree(lentry->entry); + kfree(lentry); + } + + spin_lock_bh(&sw_fib_llock); + if (!list_empty(&sw_fib_lh) && sw_fib_wq) + queue_delayed_work(sw_fib_wq, &sw_fib_work, + msecs_to_jiffies(10)); + spin_unlock_bh(&sw_fib_llock); +} + +int sw_fib_add_to_list(struct net_device *dev, + struct fib_entry *entry, int cnt) +{ + struct otx2_nic *pf =3D netdev_priv(dev); + struct sw_fib_list_entry *lentry; + struct workqueue_struct *wq; + + if (cnt <=3D 0 || cnt > SW_FIB_BATCH_MAX) { + kfree(entry); + return -EINVAL; + } + + spin_lock_bh(&sw_fib_llock); + if (!sw_fib_wq) { + spin_unlock_bh(&sw_fib_llock); + kfree(entry); + return -EINVAL; + } + spin_unlock_bh(&sw_fib_llock); + + if (sw_fib_list_count() >=3D SW_FIB_LIST_MAX) { + kfree(entry); + return -ENOMEM; + } + + lentry =3D kcalloc(1, sizeof(*lentry), GFP_ATOMIC); + if (!lentry) { + kfree(entry); + return -ENOMEM; + } + + lentry->pf =3D pf; + lentry->cnt =3D cnt; + lentry->entry =3D entry; + INIT_LIST_HEAD(&lentry->lh); + netdev_hold(dev, &lentry->dev_tracker, GFP_ATOMIC); + + spin_lock_bh(&sw_fib_llock); + wq =3D sw_fib_wq; + if (wq) { + list_add_tail(&lentry->lh, &sw_fib_lh); + sw_fib_list_cnt_inc(dev); + queue_delayed_work(wq, &sw_fib_work, + msecs_to_jiffies(10)); + } + spin_unlock_bh(&sw_fib_llock); + + if (!wq) { + netdev_put(dev, &lentry->dev_tracker); + kfree(lentry); + kfree(entry); + return -EINVAL; + } + + return 0; +} + int sw_fib_init(void) { + sw_fib_wq =3D alloc_workqueue("sw_pf_fib_wq", 0, 0); + if (!sw_fib_wq) + return -ENOMEM; + return 0; } =20 void sw_fib_deinit(void) { + struct sw_fib_list_entry *lentry; + struct workqueue_struct *wq; + LIST_HEAD(tlist); + + spin_lock_bh(&sw_fib_llock); + wq =3D sw_fib_wq; + sw_fib_wq =3D NULL; + spin_unlock_bh(&sw_fib_llock); + + if (!wq) + return; + + cancel_delayed_work_sync(&sw_fib_work); + destroy_workqueue(wq); + + spin_lock_bh(&sw_fib_llock); + list_splice_init(&sw_fib_lh, &tlist); + spin_unlock_bh(&sw_fib_llock); + + while ((lentry =3D + list_first_entry_or_null(&tlist, + struct sw_fib_list_entry, lh)) !=3D NULL) { + list_del_init(&lentry->lh); + sw_fib_list_cnt_dec(lentry->pf->netdev); + netdev_put(lentry->pf->netdev, &lentry->dev_tracker); + kfree(lentry->entry); + kfree(lentry); + } } =20 #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h b/d= rivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h index 9b72e95f2dd3..05a528931d14 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h @@ -8,11 +8,25 @@ #define SW_FIB_H_ =20 #include +#include + +struct fib_entry; +struct net_device; =20 #if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) +int sw_fib_add_to_list(struct net_device *dev, + struct fib_entry *entry, int cnt); void sw_fib_deinit(void); int sw_fib_init(void); #else +static inline int sw_fib_add_to_list(struct net_device *dev, + struct fib_entry *entry, int cnt) +{ + (void)dev; + (void)cnt; + kfree(entry); + return 0; +} static inline void sw_fib_deinit(void) {} static inline int sw_fib_init(void) { return 0; } #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c index e908cc50a611..f2597d413780 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c @@ -163,6 +163,7 @@ static int sw_nb_fdb_event(struct notifier_block *unuse= d, { struct net_device *dev =3D switchdev_notifier_info_to_dev(ptr); struct switchdev_notifier_fdb_info *fdb_info =3D ptr; + int rc =3D 0; =20 if (!sw_nb_is_valid_dev(dev)) return NOTIFY_DONE; @@ -178,14 +179,17 @@ static int sw_nb_fdb_event(struct notifier_block *unu= sed, * setups; only Cavium PF/representor netdevs are supported * as bridge ports today (VLAN/virt under bridge is TODO). */ - sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, - event =3D=3D SWITCHDEV_FDB_ADD_TO_DEVICE); + rc =3D sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, + event =3D=3D SWITCHDEV_FDB_ADD_TO_DEVICE); break; =20 default: return NOTIFY_DONE; } =20 + if (rc) + netdev_err(dev, "%s: Error to add to list\n", __func__); + return NOTIFY_DONE; } =20 @@ -354,8 +358,8 @@ static int sw_nb_netdev_event(struct notifier_block *un= used, if (idev) sw_nb_v4_netdev_event(unused, event, ptr); =20 -#if IS_ENABLED(CONFIG_IPV6) i6dev =3D __in6_dev_get(dev); +#if IS_ENABLED(CONFIG_IPV6) if (i6dev) sw_nb_v6_netdev_event(unused, event, ptr); #endif @@ -432,6 +436,7 @@ int sw_nb_register(struct net_device *netdev) { int err; =20 + /* One switch PF / switchdev instance registers system-wide notifiers. */ if (sw_nb_registered) return -EBUSY; =20 diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c index c773fce1bc50..38d2e8da9d31 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c @@ -12,6 +12,7 @@ #include #include #include +#include =20 #include "../otx2_reg.h" #include "../otx2_common.h" @@ -40,7 +41,13 @@ int sw_nb_v4_netdev_event(struct notifier_block *unused, if (!idev || !idev->ifa_list) return NOTIFY_DONE; =20 - /* Switch offload supports a single IPv4 address per interface for now. */ + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + + /* Switch offload supports a single IPv4 address per interface for + * now. Only the head of ifa_list is offloaded on netdev events; + * secondary addresses are not supported by the hardware path. + */ ifa =3D rtnl_dereference(idev->ifa_list); =20 entry =3D kcalloc(1, sizeof(*entry), GFP_KERNEL); @@ -66,6 +73,10 @@ int sw_nb_v4_netdev_event(struct notifier_block *unused, entry->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(dev)); } =20 + /* Switch offload is only enabled on OcteonTX2/CN10K SoCs. pf_dev is an + * octeontx2 PF or representor netdev, so netdev_priv() is otx2_nic even + * though sw_nb_is_cavium_dev() matches the shared Cavium PCI vendor ID. + */ pf =3D netdev_priv(pf_dev); entry->port_id =3D pf->pcifunc; =20 @@ -76,7 +87,7 @@ int sw_nb_v4_netdev_event(struct notifier_block *unused, =20 netdev_dbg(dev, "%s: pushing netdev event from HOST interface address %pI= 4, %pM, dev=3D%s\n", __func__, &entry->dst, entry->mac, dev->name); - kfree(entry); + sw_fib_add_to_list(pf_dev, entry, 1); =20 return NOTIFY_DONE; } @@ -88,7 +99,6 @@ int sw_nb_v4_inetaddr_event(struct notifier_block *nb, struct net_device *dev =3D ifa->ifa_dev->dev; struct netdev_hw_addr *dev_addr; struct net_device *pf_dev; - struct in_device *idev; struct fib_entry *entry; struct otx2_nic *pf; =20 @@ -101,10 +111,9 @@ int sw_nb_v4_inetaddr_event(struct notifier_block *nb, if (!sw_nb_is_valid_dev(dev)) return NOTIFY_DONE; =20 - idev =3D __in_dev_get_rtnl(dev); - if (!idev || !idev->ifa_list) - return NOTIFY_DONE; - + /* Use ifa from the notifier; idev->ifa_list is already empty when the + * final address is unlinked before NETDEV_DOWN is delivered. + */ entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); if (!entry) return NOTIFY_DONE; @@ -139,24 +148,27 @@ int sw_nb_v4_inetaddr_event(struct notifier_block *nb, netdev_dbg(dev, "%s: pushing inetaddr event from HOST interface address %= pI4, %pM, %s\n", __func__, &entry->dst, entry->mac, dev->name); =20 - kfree(entry); + sw_fib_add_to_list(pf_dev, entry, 1); return NOTIFY_DONE; } =20 int sw_nb_v4_fib_event(struct notifier_block *nb, unsigned long event, void *ptr) { - struct net_device *dev, *pf_dev =3D NULL, *nh_pf_dev; struct fib_entry_notifier_info *fen_info =3D ptr; - struct fib_entry *entries, *iter; + struct net_device *host_pf_dev =3D NULL; struct netdev_hw_addr *dev_addr; + struct net_device *nh_pf_dev; + struct fib_nh_common *nhc; struct neighbour *neigh; + struct fib_entry *entry; + struct net_device *dev; struct fib_nh *fib_nh; struct fib_info *fi; struct otx2_nic *pf; + int i, cnt, nhs; __be32 *haddr; int hcnt =3D 0; - int cnt, i; =20 /* Process only UNICAST routes add or del */ if (fen_info->type !=3D RTN_UNICAST) @@ -166,29 +178,30 @@ int sw_nb_v4_fib_event(struct notifier_block *nb, if (!fi) return NOTIFY_DONE; =20 + nhs =3D fib_info_num_path(fi); + if (fi->fib_nh_is_v6) { - struct net_device *log_dev =3D (fi->fib_nhs > 0) ? - fi->fib_nh->fib_nh_dev : NULL; + if (nhs > 0) { + nhc =3D fib_info_nhc(fi, 0); =20 - if (log_dev) - netdev_dbg(log_dev, "%s: Received v6 notification\n", - __func__); + if (nhc->nhc_dev) + netdev_dbg(nhc->nhc_dev, + "%s: Received v6 notification\n", + __func__); + } return NOTIFY_DONE; } =20 - entries =3D kcalloc(fi->fib_nhs, sizeof(*entries), GFP_ATOMIC); - if (!entries) + if (!nhs) return NOTIFY_DONE; =20 - haddr =3D kcalloc(fi->fib_nhs, sizeof(*haddr), GFP_ATOMIC); - if (!haddr) { - kfree(entries); + haddr =3D kcalloc(nhs, sizeof(*haddr), GFP_ATOMIC); + if (!haddr) return NOTIFY_DONE; - } =20 - iter =3D entries; - fib_nh =3D fi->fib_nh; - for (i =3D 0; i < fi->fib_nhs; i++, fib_nh++) { + for (i =3D 0; i < nhs; i++) { + nhc =3D fib_info_nhc(fi, i); + fib_nh =3D container_of(nhc, struct fib_nh, nh_common); dev =3D fib_nh->fib_nh_dev; =20 if (!dev) @@ -200,115 +213,111 @@ int sw_nb_v4_fib_event(struct notifier_block *nb, if (!sw_nb_is_valid_dev(dev)) continue; =20 - iter->cmd =3D sw_nb_fib_event_to_otx2_event(event, dev); - iter->dst =3D (__force __be32)fen_info->dst; - iter->dst_len =3D fen_info->dst_len; - iter->gw =3D fib_nh->fib_nh_gw4; - - netdev_dbg(dev, "%s: FIB route Rule cmd=3D%llu dst=3D%pI4 dst_len=3D%u g= w=3D%pI4\n", - __func__, iter->cmd, &iter->dst, iter->dst_len, &iter->gw); - nh_pf_dev =3D sw_nb_resolve_pf_dev(dev); - if (!nh_pf_dev) { - iter++; + if (!nh_pf_dev) continue; - } - pf_dev =3D nh_pf_dev; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + break; + + entry->cmd =3D sw_nb_fib_event_to_otx2_event(event, dev); + entry->dst =3D htonl(fen_info->dst); + entry->dst_len =3D fen_info->dst_len; + entry->gw =3D fib_nh->fib_nh_gw4; =20 if (netif_is_bridge_master(dev)) { - iter->bridge =3D 1; + entry->bridge =3D 1; } else if (is_vlan_dev(dev)) { - iter->vlan_valid =3D 1; - iter->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(dev)); + entry->vlan_valid =3D 1; + entry->vlan_tag =3D cpu_to_be16(vlan_dev_vlan_id(dev)); } =20 - pf =3D netdev_priv(pf_dev); - iter->port_id =3D pf->pcifunc; + pf =3D netdev_priv(nh_pf_dev); + entry->port_id =3D pf->pcifunc; =20 /* Point-to-point routes, including default routes with no * gateway, are not supported for switch offload. */ if (!fib_nh->fib_nh_gw4) { - if (iter->dst || iter->dst_len) - iter++; - + if (!entry->dst && !entry->dst_len) { + kfree(entry); + continue; + } + sw_fib_add_to_list(nh_pf_dev, entry, 1); continue; } - iter->gw_valid =3D 1; + + entry->gw_valid =3D 1; =20 if (fib_nh->nh_saddr) haddr[hcnt++] =3D fib_nh->nh_saddr; =20 rcu_read_lock(); neigh =3D ip_neigh_gw4(fib_nh->fib_nh_dev, fib_nh->fib_nh_gw4); - if (!neigh) { + if (IS_ERR_OR_NULL(neigh)) { rcu_read_unlock(); - iter++; + kfree(entry); continue; } =20 if (is_valid_ether_addr(neigh->ha)) { - iter->mac_valid =3D 1; - neigh_ha_snapshot(iter->mac, neigh, fib_nh->fib_nh_dev); + entry->mac_valid =3D 1; + neigh_ha_snapshot(entry->mac, neigh, fib_nh->fib_nh_dev); } - - iter++; rcu_read_unlock(); - } =20 - cnt =3D iter - entries; - if (!cnt) { - kfree(entries); - kfree(haddr); - return NOTIFY_DONE; + netdev_dbg(dev, "%s: FIB route Rule cmd=3D%llu dst=3D%pI4 dst_len=3D%u g= w=3D%pI4\n", + __func__, entry->cmd, &entry->dst, entry->dst_len, + &entry->gw); + sw_fib_add_to_list(nh_pf_dev, entry, 1); } =20 - if (pf_dev) - netdev_dbg(pf_dev, "pf_dev is %s cnt=3D%d\n", pf_dev->name, cnt); - kfree(entries); - if (!hcnt) { kfree(haddr); return NOTIFY_DONE; } =20 - if (!pf_dev) { - kfree(haddr); - return NOTIFY_DONE; - } + for (i =3D 0; i < hcnt; i++) { + host_pf_dev =3D NULL; + for (cnt =3D 0; cnt < nhs; cnt++) { + nhc =3D fib_info_nhc(fi, cnt); + fib_nh =3D container_of(nhc, struct fib_nh, nh_common); + if (fib_nh->nh_saddr =3D=3D haddr[i]) { + host_pf_dev =3D sw_nb_resolve_pf_dev(fib_nh->fib_nh_dev); + break; + } + } =20 - entries =3D kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC); - if (!entries) { - kfree(haddr); - return NOTIFY_DONE; - } + if (!host_pf_dev) + continue; =20 - iter =3D entries; + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + break; =20 - /* Host routes reuse pf_dev/pf from the last resolved Cavium netdev: - * pf_dev only identifies the switch AF mailbox context for switchdev - * programming; any previously resolved Cavium netdev is sufficient. - */ - for (i =3D 0; i < hcnt; i++, iter++) { - iter->cmd =3D sw_nb_fib_event_to_otx2_event(event, pf_dev); - iter->dst =3D haddr[i]; - iter->dst_len =3D 32; - iter->mac_valid =3D 1; - iter->host =3D 1; - iter->port_id =3D pf->pcifunc; + pf =3D netdev_priv(host_pf_dev); + entry->cmd =3D sw_nb_fib_event_to_otx2_event(event, host_pf_dev); + entry->dst =3D haddr[i]; + entry->dst_len =3D 32; + entry->mac_valid =3D 1; + entry->host =3D 1; + entry->port_id =3D pf->pcifunc; =20 rcu_read_lock(); - for_each_dev_addr(pf_dev, dev_addr) { - ether_addr_copy(iter->mac, dev_addr->addr); + for_each_dev_addr(host_pf_dev, dev_addr) { + ether_addr_copy(entry->mac, dev_addr->addr); break; } rcu_read_unlock(); =20 - netdev_dbg(pf_dev, "%s: FIB host Rule cmd=3D%llu dst=3D%pI4 dst_len=3D%u= gw=3D%pI4 %s\n", - __func__, iter->cmd, &iter->dst, iter->dst_len, &iter->gw, - pf_dev->name); + netdev_dbg(host_pf_dev, + "%s: FIB host Rule cmd=3D%llu dst=3D%pI4 dst_len=3D%u gw=3D%pI4 %s\n= ", + __func__, entry->cmd, &entry->dst, entry->dst_len, + &entry->gw, host_pf_dev->name); + sw_fib_add_to_list(host_pf_dev, entry, 1); } - kfree(entries); + kfree(haddr); return NOTIFY_DONE; } @@ -324,6 +333,9 @@ int sw_nb_net_v4_neigh_update(struct notifier_block *nb, if (n->tbl !=3D &arp_tbl) return NOTIFY_DONE; =20 + if (!sw_nb_is_valid_dev(n->dev)) + return NOTIFY_DONE; + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); if (!entry) return NOTIFY_DONE; @@ -351,7 +363,7 @@ int sw_nb_net_v4_neigh_update(struct notifier_block *nb, pf =3D netdev_priv(pf_dev); entry->port_id =3D pf->pcifunc; =20 - kfree(entry); + sw_fib_add_to_list(pf_dev, entry, 1); return NOTIFY_DONE; } =20 diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c index 62ab00658879..0a2ad61412d6 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c @@ -45,12 +45,18 @@ int sw_nb_v6_netdev_event(struct notifier_block *unused, if (!i6dev) return NOTIFY_DONE; =20 + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + /* Invoked from sw_nb_netdev_event() on NETDEV_UP/DOWN/CHANGE, which * run with RTNL held. IPv6 address list updates are also serialized * by RTNL, so addr_list cannot race with concurrent assignments. */ rcu_read_lock(); - /* Switch offload supports a single IPv6 address per interface for now. */ + /* Switch offload supports a single IPv6 address per interface for + * now. Only the head of addr_list is offloaded on netdev events; + * secondary addresses are not supported by the hardware path. + */ ifp =3D list_first_entry_or_null(&i6dev->addr_list, struct inet6_ifaddr, if_list); if (!ifp) { @@ -94,15 +100,15 @@ int sw_nb_v6_netdev_event(struct notifier_block *unuse= d, =20 netdev_dbg(dev, "netdev event addr=3D%pI6c plen=3D%u mac=3D%pM\n", &addr, prefix_len, entry->mac); - kfree(entry); + sw_fib_add_to_list(pf_dev, entry, 1); return NOTIFY_DONE; } =20 int sw_nb_v6_fib_event(struct notifier_block *nb, unsigned long event, void *ptr) { - struct fib6_entry_notifier_info *f6_eni; struct fib_notifier_info *info =3D ptr; + struct fib6_entry_notifier_info *f6_eni; struct net_device *fib_dev, *pf_dev; struct fib_entry *entry; struct fib6_info *f6i; @@ -171,7 +177,7 @@ int sw_nb_v6_fib_event(struct notifier_block *nb, */ rcu_read_lock(); neigh =3D ip_neigh_gw6(fib_dev, &nh6->fib_nh_gw6); - if (!neigh) { + if (IS_ERR_OR_NULL(neigh)) { rcu_read_unlock(); kfree(entry); return NOTIFY_DONE; @@ -183,8 +189,8 @@ int sw_nb_v6_fib_event(struct notifier_block *nb, netdev_dbg(fib_dev, "fib found MAC=3D%pM\n", entry->mac); } =20 + sw_fib_add_to_list(pf_dev, entry, 1); rcu_read_unlock(); - kfree(entry); =20 return NOTIFY_DONE; } @@ -225,9 +231,10 @@ int sw_nb_net_v6_neigh_update(struct notifier_block *n= b, entry->mac_valid =3D 1; entry->port_id =3D pf->pcifunc; =20 + sw_fib_add_to_list(pf_dev, entry, 1); + netdev_dbg(n->dev, "v6 neigh update %pI6c mac=3D%pM plen=3D%u\n", n->primary_key, entry->mac, n->tbl->key_len * 8); - kfree(entry); =20 return NOTIFY_DONE; } @@ -283,9 +290,10 @@ int sw_nb_v6_inetaddr_event(struct notifier_block *nb, break; } =20 + sw_fib_add_to_list(pf_dev, entry, 1); + netdev_dbg(dev, "inetaddr addr=3D%pI6c len=3D%u %pM\n", &ifa6->addr, ifa6->prefix_len, entry->mac); - kfree(entry); =20 return NOTIFY_DONE; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h index f73efc98c311..78c0df5eb880 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h @@ -7,6 +7,9 @@ #ifndef SW_NB_V6_H_ #define SW_NB_V6_H_ =20 +#include + +#if IS_ENABLED(CONFIG_IPV6) int sw_nb_v6_fib_event(struct notifier_block *nb, unsigned long event, void *ptr); =20 @@ -18,4 +21,30 @@ int sw_nb_v6_inetaddr_event(struct notifier_block *nb, =20 int sw_nb_v6_netdev_event(struct notifier_block *unused, unsigned long event, void *ptr); -#endif // SW_NB_V6_H__ +#else +static inline int sw_nb_v6_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + return NOTIFY_DONE; +} + +static inline int sw_nb_net_v6_neigh_update(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + return NOTIFY_DONE; +} + +static inline int sw_nb_v6_inetaddr_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + return NOTIFY_DONE; +} + +static inline int sw_nb_v6_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + return NOTIFY_DONE; +} +#endif + +#endif /* SW_NB_V6_H_ */ --=20 2.43.0 From nobody Sat Jul 25 01:24:45 2026 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (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 AD37E445AC3; Tue, 21 Jul 2026 08:19:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621951; cv=none; b=A5UI21jbx4evqTYMTF/VpkmywKrlYC74AxENL/PRLLIxXOu8b0Y/IY854YF8mQWFlqy8vVF1+uJwiYHCCgs8KS5xgfcbNq+bH9+TewKfvHk1qV5fbSCj2heHXPnu1w1JtXruJwkCif3e4THAPBmkA91B5e3pko4hq9c8kPkK2jk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784621951; c=relaxed/simple; bh=X9K2ajUDVXuAvv73k51pZBzAyu1smc3rQLuBpZmxun8=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jUEI9GxTMJbWEv2Ro37p8dtmZc0OLUgPNu92BWO7L5YquLOdhk6+rfh6wYavS6Pi9E8CIPGHqRYdEot8krr3fWBRq8C6JOp+gFeks6tC6PH6bFYhpIhXChBZoTd7BahOrqgAYSh0uDrfb+iM6V7/0l7NiGY8xnb4U0uSQIGOvRw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=D+yveLwx; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="D+yveLwx" Received: from pps.filterd (m0431383.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66KNcbL53070050; Tue, 21 Jul 2026 01:18:57 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=T MRT/yCdc/s9fuIDY4p+VKB1jZm0rgBQvvrGg7MX4kY=; b=D+yveLwxQhoMonl9r 1kqV67G5icyulcMDE2g2Newn/NQDiK6W+S7w6NYVg7lMaKEB8TE8UVnqYvfI3Hus VvZnImFjwGzYHKWQ915ea0q/5adN3NnV46UvIrPNfrJmiIkx2hpNyxIMlt05RG16 YdIoVdphkqZxYvFl9Ysir4F4044DtKpHza0l4Lug5kns4lhvtktBrzX8Fo5gZuYA t5P6URrKA/IP2gRlCH3U7+VCVfd8hwydcqr/ebbovYUQe12BTjMSb+LALMFs+ih3 c1mb/RE+ZGA8mgLL9MwFOliUEUMfOqrY/O1YxAY52fbzgeuFL++Z4gjX02NUZeKb lnXaw== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 4fhwdkh701-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jul 2026 01:18:57 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.25; Tue, 21 Jul 2026 01:18:56 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.25 via Frontend Transport; Tue, 21 Jul 2026 01:18:56 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 27BDF3F7091; Tue, 21 Jul 2026 01:18:53 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v4 net-next 9/9] octeontx2: switch: add TC flow offload path for switch flows Date: Tue, 21 Jul 2026 13:48:24 +0530 Message-ID: <20260721081824.1430607-10-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com> References: <20260721081824.1430607-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: wMiEyeukR-aBNutF8G3KzyN2OYA9DBEa X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDA4NyBTYWx0ZWRfX7CrpvUPPC5Uh TS9gyj2o9Q+JWkltcfON32fNPID0g+ZRwcyjd/3Opyd/C1O9b9MD9ZdNUkK4pXmfLMFohUfdZ5H +7mFfZdWd6N7T6gscFTMdsiN6c7FWV4= X-Authority-Analysis: v=2.4 cv=TrXWQjXh c=1 sm=1 tr=0 ts=6a5f2b71 cx=c_pps a=gIfcoYsirJbf48DBMSPrZA==:117 a=gIfcoYsirJbf48DBMSPrZA==:17 a=RAioF0-LDSMA:10 a=VkNPw1HP01LnGYTKEx00:22 a=l0iWHRpgs5sLHlkKQ1IR:22 a=qit2iCtTFQkLgVSMPQTB:22 a=M5GUcnROAAAA:8 a=gxODTceSeL9lbQbNvVgA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-GUID: wMiEyeukR-aBNutF8G3KzyN2OYA9DBEa X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDA4NyBTYWx0ZWRfXxZp1n+leQTsK fleb5Mxt3AXcSxWhF4W3d8U7kKwSn2V9cBWTUT1f1yd4gPUWymsBVYDmnehVsuFnz8RwaMvERjz Dtqq9Ydq9w/Uc2+2ABNSH1MjBJJKz0i8kvruvBzD0SUkJ7omrK1Z7jpG0dkCRVTkGzBpwo1aYr/ cWmLa9cU+rynlW4KjmjKlSGEYtjPdb+Ria8VW2/nnOP4VgcYXUI4Pb0y5hjsuGYSUqapQ+CnRbk P+6RNISQ9yJHHpPE20oOPsYOLwz1DX4S8vvrDucQd8q1aKXDm6waeaNL097viIOkJ+N+Opo2RVi NTvh5FSGqtDMpjDEpt26EMx4XBXrvjE6/IoDuH06Jhy2N/TfXpPqlkMn6JGfqCT76g+juy6qfBU VpLPQKCbUXveLHtpFAIS3UmqmNZqH5JIxDd2ijZUH2ZsqkigCcfSWWYciWqH2/GFV1FA+B67qmH x9wN4VzhVb4+aKw/tgw== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-20_06,2026-07-20_03,2025-10-01_01 Content-Type: text/plain; charset="utf-8" Register an ingress flow-table offload callback that translates TC flower rules into fl_tuple state, resolves ingress and egress pcifunc via FIB for accelerated ports, and notifies the RVU AF over the PF mailbox. The AF forwards flow updates to switchdev and keeps per-cookie packet counters in sync using NPC MCAM multi-stats when the switch requests SWDEV2AF refresh. Signed-off-by: Ratheesh Kannoth --- .../marvell/octeontx2/af/switch/rvu_sw.c | 9 +- .../marvell/octeontx2/af/switch/rvu_sw_fl.c | 327 +++++++ .../marvell/octeontx2/af/switch/rvu_sw_fl.h | 2 + .../ethernet/marvell/octeontx2/nic/Makefile | 3 +- .../marvell/octeontx2/nic/switch/sw_fl.c | 909 ++++++++++++++++++ .../marvell/octeontx2/nic/switch/sw_fl.h | 2 + .../marvell/octeontx2/nic/switch/sw_trace.c | 11 + .../marvell/octeontx2/nic/switch/sw_trace.h | 84 ++ 8 files changed, 1345 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_tr= ace.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_tr= ace.h diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/dr= ivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c index 1151ba47284b..f89633f4b821 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c @@ -7,10 +7,10 @@ =20 #include #include "rvu.h" -#include "rvu_sw.h" #include "rvu_sw_l2.h" #include "rvu_sw_l3.h" #include "rvu_sw_fl.h" +#include "rvu_sw.h" =20 u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc) { @@ -69,6 +69,12 @@ int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu, rc =3D rvu_sw_l2_fdb_list_entry_add(rvu, req->pcifunc, req->mac); break; =20 + case SWDEV2AF_MSG_TYPE_REFRESH_FL: + if (req->cnt <=3D 0 || req->cnt > ARRAY_SIZE(req->fl)) + return -EINVAL; + rc =3D rvu_sw_fl_stats_sync2db(rvu, req->fl, req->cnt); + break; + default: rc =3D -EOPNOTSUPP; break; @@ -81,4 +87,5 @@ void rvu_sw_shutdown(void) { rvu_sw_l2_shutdown(); rvu_sw_l3_shutdown(); + rvu_sw_fl_shutdown(); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c index 1f8b82a84a5d..a75aa991d29e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c @@ -4,12 +4,275 @@ * Copyright (C) 2026 Marvell. * */ + +#include #include "rvu.h" +#include "rvu_sw.h" +#include "rvu_sw_fl.h" + +static struct af2swdev_notify_req __maybe_unused +*otx2_mbox_alloc_msg_af2swdev_notify(struct rvu *rvu, int devid) +{ + struct af2swdev_notify_req *req; + + req =3D (struct af2swdev_notify_req *) + otx2_mbox_alloc_msg_rsp(&rvu->afpf_wq_info.mbox_up, devid, + sizeof(*req), sizeof(struct msg_rsp)); + if (!req) + return NULL; + req->hdr.sig =3D OTX2_MBOX_REQ_SIG; + req->hdr.id =3D MBOX_MSG_AF2SWDEV; + return req; +} + +#define RVU_SW_FL_REFRESH_MAX \ + ((int)ARRAY_SIZE(((struct swdev2af_notify_req *)0)->fl)) + +struct fl_entry { + struct list_head list; + struct rvu *rvu; + u32 port_id; + unsigned long cookie; + struct fl_tuple tuple; + u64 flags; + u64 features; +}; + +static DEFINE_MUTEX(fl_offl_llock); +static LIST_HEAD(fl_offl_lh); + +static struct workqueue_struct *sw_fl_offl_wq; +static void sw_fl_offl_work_handler(struct work_struct *work); +static DECLARE_DELAYED_WORK(fl_offl_work, sw_fl_offl_work_handler); + +struct sw_fl_stats_node { + struct list_head list; + unsigned long cookie; + u16 mcam_idx[2]; + u64 opkts, npkts; + bool uni_di; +}; + +static LIST_HEAD(sw_fl_stats_lh); +static DEFINE_MUTEX(sw_fl_stats_lock); + +static void rvu_sw_fl_queue_work(void) +{ + if (sw_fl_offl_wq) + queue_delayed_work(sw_fl_offl_wq, &fl_offl_work, + msecs_to_jiffies(10)); +} + +static int rvu_sw_fl_ensure_wq(void) +{ + if (sw_fl_offl_wq) + return 0; + + sw_fl_offl_wq =3D alloc_workqueue("sw_af_fl_wq", 0, 0); + if (!sw_fl_offl_wq) + return -ENOMEM; + + return 0; +} + +static int +rvu_sw_fl_stats_sync2db_one_entry(unsigned long cookie, u8 disabled, + u16 mcam_idx[2], bool uni_di, u64 pkts) +{ + struct sw_fl_stats_node *snode, *tmp; + + mutex_lock(&sw_fl_stats_lock); + list_for_each_entry_safe(snode, tmp, &sw_fl_stats_lh, list) { + if (snode->cookie !=3D cookie) + continue; + + if (disabled) { + list_del_init(&snode->list); + mutex_unlock(&sw_fl_stats_lock); + kfree(snode); + return 0; + } + + if (snode->uni_di !=3D uni_di) { + snode->uni_di =3D uni_di; + snode->mcam_idx[1] =3D mcam_idx[1]; + } + + if (snode->opkts =3D=3D pkts) { + mutex_unlock(&sw_fl_stats_lock); + return 0; + } + + snode->npkts =3D pkts; + mutex_unlock(&sw_fl_stats_lock); + return 0; + } + + if (disabled) { + mutex_unlock(&sw_fl_stats_lock); + return 0; + } + + snode =3D kcalloc(1, sizeof(*snode), GFP_KERNEL); + if (!snode) { + mutex_unlock(&sw_fl_stats_lock); + return -ENOMEM; + } + + snode->cookie =3D cookie; + snode->mcam_idx[0] =3D mcam_idx[0]; + if (!uni_di) + snode->mcam_idx[1] =3D mcam_idx[1]; + + snode->npkts =3D pkts; + snode->uni_di =3D uni_di; + INIT_LIST_HEAD(&snode->list); + + list_add_tail(&snode->list, &sw_fl_stats_lh); + mutex_unlock(&sw_fl_stats_lock); + + return 0; +} + +int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt) +{ + struct npc_mcam_get_mul_stats_req *req =3D NULL; + struct npc_mcam_get_mul_stats_rsp *rsp =3D NULL; + int i, idx; + int rc =3D 0; + u64 pkts; + + if (cnt <=3D 0 || cnt > RVU_SW_FL_REFRESH_MAX) + return -EINVAL; + + req =3D kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) { + rc =3D -ENOMEM; + goto fail; + } + + rsp =3D kcalloc(1, sizeof(*rsp), GFP_KERNEL); + if (!rsp) { + rc =3D -ENOMEM; + goto fail; + } + + idx =3D 0; + for (i =3D 0; i < cnt; i++) { + req->entry[idx++] =3D fl[i].mcam_idx[0]; + if (!fl[i].uni_di) + req->entry[idx++] =3D fl[i].mcam_idx[1]; + } + req->cnt =3D idx; + + if (idx > 256) { + rc =3D -EINVAL; + goto fail; + } + + if (rvu_mbox_handler_npc_mcam_mul_stats(rvu, req, rsp)) { + dev_err(rvu->dev, "Error to get multiple stats\n"); + rc =3D -EFAULT; + goto fail; + } + + idx =3D 0; + for (i =3D 0; i < cnt; i++) { + pkts =3D rsp->stat[idx++]; + if (!fl[i].uni_di) + pkts +=3D rsp->stat[idx++]; + + rc |=3D rvu_sw_fl_stats_sync2db_one_entry(fl[i].cookie, fl[i].dis, + fl[i].mcam_idx, + fl[i].uni_di, pkts); + } + +fail: + kfree(req); + kfree(rsp); + return rc; +} + +static int rvu_sw_fl_offl_rule_push(struct fl_entry *fl_entry) +{ + struct af2swdev_notify_req *req; + struct rvu *rvu; + int swdev_pf; + + rvu =3D fl_entry->rvu; + swdev_pf =3D rvu_get_pf(rvu->pdev, rvu->rswitch.pcifunc); + + mutex_lock(&rvu->mbox_lock); + req =3D otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf); + if (!req) { + mutex_unlock(&rvu->mbox_lock); + return -ENOMEM; + } + + req->tuple =3D fl_entry->tuple; + req->flags =3D fl_entry->flags; + req->cookie =3D fl_entry->cookie; + req->features =3D fl_entry->features; + + if (!otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, swdev_pf)) { + mutex_unlock(&rvu->mbox_lock); + return -EBUSY; + } + + otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, swdev_pf); + + mutex_unlock(&rvu->mbox_lock); + return 0; +} + +static void sw_fl_offl_work_handler(struct work_struct *work) +{ + struct fl_entry *fl_entry; + + mutex_lock(&fl_offl_llock); + fl_entry =3D list_first_entry_or_null(&fl_offl_lh, struct fl_entry, list); + if (!fl_entry) { + mutex_unlock(&fl_offl_llock); + return; + } + + list_del_init(&fl_entry->list); + mutex_unlock(&fl_offl_llock); + + if (rvu_sw_fl_offl_rule_push(fl_entry)) { + mutex_lock(&fl_offl_llock); + list_add_tail(&fl_entry->list, &fl_offl_lh); + mutex_unlock(&fl_offl_llock); + if (sw_fl_offl_wq) + queue_delayed_work(sw_fl_offl_wq, &fl_offl_work, + msecs_to_jiffies(100)); + return; + } + + kfree(fl_entry); + + mutex_lock(&fl_offl_llock); + if (!list_empty(&fl_offl_lh)) + rvu_sw_fl_queue_work(); + mutex_unlock(&fl_offl_llock); +} =20 int rvu_mbox_handler_fl_get_stats(struct rvu *rvu, struct fl_get_stats_req *req, struct fl_get_stats_rsp *rsp) { + struct sw_fl_stats_node *snode, *tmp; + + mutex_lock(&sw_fl_stats_lock); + list_for_each_entry_safe(snode, tmp, &sw_fl_stats_lh, list) { + if (snode->cookie !=3D req->cookie) + continue; + + rsp->pkts_diff =3D snode->npkts - snode->opkts; + snode->opkts =3D snode->npkts; + break; + } + mutex_unlock(&sw_fl_stats_lock); return 0; } =20 @@ -17,5 +280,69 @@ int rvu_mbox_handler_fl_notify(struct rvu *rvu, struct fl_notify_req *req, struct msg_rsp *rsp) { + struct fl_entry *fl_entry; + int rc; + + if (!(rvu->rswitch.flags & RVU_SWITCH_FLAG_FW_READY)) + return -EAGAIN; + + fl_entry =3D kcalloc(1, sizeof(*fl_entry), GFP_KERNEL); + if (!fl_entry) + return -ENOMEM; + + fl_entry->port_id =3D rvu_sw_port_id(rvu, req->hdr.pcifunc); + fl_entry->rvu =3D rvu; + INIT_LIST_HEAD(&fl_entry->list); + fl_entry->tuple =3D req->tuple; + fl_entry->cookie =3D req->cookie; + fl_entry->flags =3D req->flags; + fl_entry->features =3D req->features; + + mutex_lock(&fl_offl_llock); + rc =3D rvu_sw_fl_ensure_wq(); + if (rc) { + mutex_unlock(&fl_offl_llock); + kfree(fl_entry); + return rc; + } + + list_add_tail(&fl_entry->list, &fl_offl_lh); + rvu_sw_fl_queue_work(); + mutex_unlock(&fl_offl_llock); + return 0; } + +void rvu_sw_fl_shutdown(void) +{ + struct sw_fl_stats_node *snode, *tmp; + struct workqueue_struct *wq; + struct fl_entry *entry; + + mutex_lock(&sw_fl_stats_lock); + list_for_each_entry_safe(snode, tmp, &sw_fl_stats_lh, list) { + list_del_init(&snode->list); + kfree(snode); + } + mutex_unlock(&sw_fl_stats_lock); + + if (!sw_fl_offl_wq) + return; + + cancel_delayed_work_sync(&fl_offl_work); + wq =3D sw_fl_offl_wq; + sw_fl_offl_wq =3D NULL; + destroy_workqueue(wq); + + mutex_lock(&fl_offl_llock); + while (1) { + entry =3D list_first_entry_or_null(&fl_offl_lh, + struct fl_entry, list); + if (!entry) + break; + + list_del_init(&entry->list); + kfree(entry); + } + mutex_unlock(&fl_offl_llock); +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h b= /drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h index cf3e5b884f77..f117a96fc33e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h @@ -7,5 +7,7 @@ =20 #ifndef RVU_SW_FL_H #define RVU_SW_FL_H +int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt); +void rvu_sw_fl_shutdown(void); =20 #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/= net/ethernet/marvell/octeontx2/nic/Makefile index 02ab0634f58f..917a34a32ca8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_RVU_ESWITCH) +=3D rvu_rep.o rvu_nicpf-y :=3D otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \ otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \ otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \ - switch/sw_fdb.o switch/sw_fl.o + switch/sw_fdb.o switch/sw_fl.o switch/sw_trace.o rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) +=3D switch/sw_nb.o switch/sw_fib.o \ switch/sw_nb_v4.o ifneq ($(CONFIG_IPV6),) @@ -25,3 +25,4 @@ rvu_nicpf-$(CONFIG_MACSEC) +=3D cn10k_macsec.o rvu_nicpf-$(CONFIG_XFRM_OFFLOAD) +=3D cn10k_ipsec.o =20 ccflags-y +=3D -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af +ccflags-y +=3D -I$(src)/switch/ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c index 36a2359a0a48..7c7d4f788442 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c @@ -4,13 +4,922 @@ * Copyright (C) 2026 Marvell. * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../otx2_reg.h" +#include "../otx2_common.h" +#include "../otx2_struct.h" +#include "../cn10k.h" +#include "sw_nb.h" +#include "sw_trace.h" #include "sw_fl.h" =20 +#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH) +int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type, + void *type_data, void *cb_priv) +{ + return -EOPNOTSUPP; +} + +#else + +static DEFINE_SPINLOCK(sw_fl_lock); +static LIST_HEAD(sw_fl_lh); + +struct sw_fl_list_entry { + struct list_head list; + u64 flags; + unsigned long cookie; + struct otx2_nic *pf; + netdevice_tracker dev_tracker; + struct fl_tuple tuple; +}; + +static struct workqueue_struct *sw_fl_wq; +static void sw_fl_wq_handler(struct work_struct *work); +static DECLARE_DELAYED_WORK(sw_fl_work, sw_fl_wq_handler); + +struct sw_fl_ct_cb { + struct list_head list; + struct nf_flowtable *ft; + struct otx2_nic *nic; + refcount_t ref; +}; + +struct sw_fl_ct_cookie { + struct list_head list; + unsigned long cookie; + struct nf_flowtable *ft; + struct otx2_nic *nic; +}; + +struct sw_fl_ct_drain { + struct list_head list; + struct nf_flowtable *ft; + struct otx2_nic *nic; +}; + +static LIST_HEAD(sw_fl_ct_cb_list); +static LIST_HEAD(sw_fl_ct_drain_list); +static DEFINE_MUTEX(sw_fl_ct_cb_lock); +static LIST_HEAD(sw_fl_ct_cookie_list); +static DEFINE_MUTEX(sw_fl_ct_cookie_lock); + +static struct sw_fl_ct_cb *sw_fl_ct_cb_find(struct nf_flowtable *ft, + struct otx2_nic *nic) +{ + struct sw_fl_ct_cb *entry; + + list_for_each_entry(entry, &sw_fl_ct_cb_list, list) { + if (entry->ft =3D=3D ft && entry->nic =3D=3D nic) + return entry; + } + + return NULL; +} + +static bool sw_fl_ct_draining(struct nf_flowtable *ft, struct otx2_nic *ni= c) +{ + struct sw_fl_ct_drain *drain; + + list_for_each_entry(drain, &sw_fl_ct_drain_list, list) { + if (drain->ft =3D=3D ft && drain->nic =3D=3D nic) + return true; + } + + return false; +} + +static int sw_fl_ct_cb_get(struct nf_flowtable *ft, struct otx2_nic *nic) +{ + struct sw_fl_ct_cb *entry; + int err; + + mutex_lock(&sw_fl_ct_cb_lock); + if (sw_fl_ct_draining(ft, nic)) { + mutex_unlock(&sw_fl_ct_cb_lock); + return -EBUSY; + } + entry =3D sw_fl_ct_cb_find(ft, nic); + if (entry) { + refcount_inc(&entry->ref); + mutex_unlock(&sw_fl_ct_cb_lock); + return 0; + } + mutex_unlock(&sw_fl_ct_cb_lock); + + err =3D nf_flow_table_offload_add_cb(ft, sw_fl_setup_ft_block_ingress_cb,= nic); + if (err && err !=3D -EEXIST) + return err; + + mutex_lock(&sw_fl_ct_cb_lock); + if (sw_fl_ct_draining(ft, nic)) { + mutex_unlock(&sw_fl_ct_cb_lock); + if (!err) + nf_flow_table_offload_del_cb(ft, + sw_fl_setup_ft_block_ingress_cb, + nic); + return -EBUSY; + } + entry =3D sw_fl_ct_cb_find(ft, nic); + if (entry) { + refcount_inc(&entry->ref); + mutex_unlock(&sw_fl_ct_cb_lock); + if (!err) + nf_flow_table_offload_del_cb(ft, + sw_fl_setup_ft_block_ingress_cb, + nic); + return 0; + } + + entry =3D kzalloc_obj(*entry, GFP_KERNEL); + if (!entry) { + mutex_unlock(&sw_fl_ct_cb_lock); + if (!err) + nf_flow_table_offload_del_cb(ft, + sw_fl_setup_ft_block_ingress_cb, + nic); + return -ENOMEM; + } + + entry->ft =3D ft; + entry->nic =3D nic; + refcount_set(&entry->ref, 1); + list_add_tail(&entry->list, &sw_fl_ct_cb_list); + mutex_unlock(&sw_fl_ct_cb_lock); + + return 0; +} + +static void sw_fl_ct_cb_put(struct nf_flowtable *ft, struct otx2_nic *nic) +{ + struct sw_fl_ct_cb *entry; + struct sw_fl_ct_drain drain; + + mutex_lock(&sw_fl_ct_cb_lock); + entry =3D sw_fl_ct_cb_find(ft, nic); + if (!entry || !refcount_dec_and_test(&entry->ref)) { + mutex_unlock(&sw_fl_ct_cb_lock); + return; + } + + drain.ft =3D ft; + drain.nic =3D nic; + INIT_LIST_HEAD(&drain.list); + list_add_tail(&drain.list, &sw_fl_ct_drain_list); + + list_del(&entry->list); + mutex_unlock(&sw_fl_ct_cb_lock); + + nf_flow_table_offload_del_cb(ft, sw_fl_setup_ft_block_ingress_cb, nic); + + mutex_lock(&sw_fl_ct_cb_lock); + list_del(&drain.list); + mutex_unlock(&sw_fl_ct_cb_lock); + + kfree(entry); +} + +static struct sw_fl_ct_cookie *sw_fl_ct_cookie_find(unsigned long cookie) +{ + struct sw_fl_ct_cookie *entry; + + list_for_each_entry(entry, &sw_fl_ct_cookie_list, list) { + if (entry->cookie =3D=3D cookie) + return entry; + } + + return NULL; +} + +static int sw_fl_ct_cookie_add(unsigned long cookie, struct nf_flowtable *= ft, + struct otx2_nic *nic) +{ + struct sw_fl_ct_cookie *entry, *existing; + + entry =3D kzalloc_obj(*entry, GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->cookie =3D cookie; + entry->ft =3D ft; + entry->nic =3D nic; + + mutex_lock(&sw_fl_ct_cookie_lock); + existing =3D sw_fl_ct_cookie_find(cookie); + if (existing) { + if (existing->ft =3D=3D ft && existing->nic =3D=3D nic) { + mutex_unlock(&sw_fl_ct_cookie_lock); + kfree(entry); + sw_fl_ct_cb_put(ft, nic); + return 0; + } + + list_del(&existing->list); + mutex_unlock(&sw_fl_ct_cookie_lock); + sw_fl_ct_cb_put(existing->ft, existing->nic); + kfree(existing); + + mutex_lock(&sw_fl_ct_cookie_lock); + } + + list_add_tail(&entry->list, &sw_fl_ct_cookie_list); + mutex_unlock(&sw_fl_ct_cookie_lock); + + return 0; +} + +static void sw_fl_ct_cookie_put(unsigned long cookie) +{ + struct sw_fl_ct_cookie *entry, *tmp; + + mutex_lock(&sw_fl_ct_cookie_lock); + list_for_each_entry_safe(entry, tmp, &sw_fl_ct_cookie_list, list) { + if (entry->cookie !=3D cookie) + continue; + + list_del(&entry->list); + mutex_unlock(&sw_fl_ct_cookie_lock); + sw_fl_ct_cb_put(entry->ft, entry->nic); + kfree(entry); + return; + } + mutex_unlock(&sw_fl_ct_cookie_lock); +} + +static void sw_fl_ct_cb_flush(void) +{ + struct sw_fl_ct_cookie *cookie, *ctmp; + struct sw_fl_ct_cb *entry; + struct sw_fl_ct_drain drain; + struct nf_flowtable *ft; + struct otx2_nic *nic; + + mutex_lock(&sw_fl_ct_cookie_lock); + list_for_each_entry_safe(cookie, ctmp, &sw_fl_ct_cookie_list, list) { + list_del(&cookie->list); + kfree(cookie); + } + mutex_unlock(&sw_fl_ct_cookie_lock); + + mutex_lock(&sw_fl_ct_cb_lock); + while ((entry =3D list_first_entry_or_null(&sw_fl_ct_cb_list, + struct sw_fl_ct_cb, list))) { + ft =3D entry->ft; + nic =3D entry->nic; + drain.ft =3D ft; + drain.nic =3D nic; + INIT_LIST_HEAD(&drain.list); + list_add_tail(&drain.list, &sw_fl_ct_drain_list); + + list_del(&entry->list); + mutex_unlock(&sw_fl_ct_cb_lock); + + nf_flow_table_offload_del_cb(ft, + sw_fl_setup_ft_block_ingress_cb, + nic); + + mutex_lock(&sw_fl_ct_cb_lock); + list_del(&drain.list); + kfree(entry); + } + mutex_unlock(&sw_fl_ct_cb_lock); +} + +static int sw_fl_msg_send(struct otx2_nic *pf, + struct fl_tuple *tuple, + u64 flags, + unsigned long cookie) +{ + struct fl_notify_req *req; + int rc; + + mutex_lock(&pf->mbox.lock); + req =3D otx2_mbox_alloc_msg_fl_notify(&pf->mbox); + if (!req) { + rc =3D -ENOMEM; + goto out; + } + + req->tuple =3D *tuple; + req->flags =3D flags; + req->cookie =3D cookie; + + rc =3D otx2_sync_mbox_msg(&pf->mbox); +out: + mutex_unlock(&pf->mbox.lock); + return rc; +} + +static void sw_fl_wq_handler(struct work_struct *work) +{ + struct sw_fl_list_entry *entry; + LIST_HEAD(tlist); + + spin_lock_bh(&sw_fl_lock); + list_splice_init(&sw_fl_lh, &tlist); + spin_unlock_bh(&sw_fl_lock); + + while ((entry =3D + list_first_entry_or_null(&tlist, + struct sw_fl_list_entry, + list)) !=3D NULL) { + list_del_init(&entry->list); + if (sw_fl_msg_send(entry->pf, &entry->tuple, + entry->flags, entry->cookie)) { + netdev_err(entry->pf->netdev, + "Failed to notify flow update to AF, will retry\n"); + spin_lock_bh(&sw_fl_lock); + if (sw_fl_wq) { + list_add(&entry->list, &sw_fl_lh); + queue_delayed_work(sw_fl_wq, &sw_fl_work, + msecs_to_jiffies(100)); + spin_unlock_bh(&sw_fl_lock); + continue; + } + spin_unlock_bh(&sw_fl_lock); + netdev_put(entry->pf->netdev, &entry->dev_tracker); + kfree(entry); + continue; + } + netdev_put(entry->pf->netdev, &entry->dev_tracker); + kfree(entry); + } + + spin_lock_bh(&sw_fl_lock); + if (!list_empty(&sw_fl_lh) && sw_fl_wq) + queue_delayed_work(sw_fl_wq, &sw_fl_work, msecs_to_jiffies(10)); + spin_unlock_bh(&sw_fl_lock); +} + +static int +sw_fl_add_to_list(struct otx2_nic *pf, struct fl_tuple *tuple, + unsigned long cookie, bool add_fl) +{ + struct sw_fl_list_entry *entry; + + entry =3D kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + + entry->pf =3D pf; + entry->flags =3D add_fl ? FL_ADD : FL_DEL; + if (add_fl) + entry->tuple =3D *tuple; + entry->cookie =3D cookie; + entry->tuple.uni_di =3D netif_is_ovs_port(pf->netdev); + + spin_lock_bh(&sw_fl_lock); + if (!sw_fl_wq) { + spin_unlock_bh(&sw_fl_lock); + kfree(entry); + return -EINVAL; + } + + netdev_hold(pf->netdev, &entry->dev_tracker, GFP_ATOMIC); + list_add_tail(&entry->list, &sw_fl_lh); + queue_delayed_work(sw_fl_wq, &sw_fl_work, msecs_to_jiffies(10)); + spin_unlock_bh(&sw_fl_lock); + + return 0; +} + +static int sw_fl_mangle_layer(enum flow_action_mangle_base htype) +{ + switch (htype) { + case FLOW_ACT_MANGLE_HDR_TYPE_ETH: + return 0; + case FLOW_ACT_MANGLE_HDR_TYPE_IP4: + case FLOW_ACT_MANGLE_HDR_TYPE_IP6: + return 2; + case FLOW_ACT_MANGLE_HDR_TYPE_TCP: + case FLOW_ACT_MANGLE_HDR_TYPE_UDP: + return 3; + default: + return -EOPNOTSUPP; + } +} + +static int sw_fl_parse_actions(struct otx2_nic *nic, + struct flow_action *flow_action, + struct flow_cls_offload *f, + struct fl_tuple *tuple, u64 *op, + struct nf_flowtable **ct_ft) +{ + struct flow_action_entry *act; + struct nf_flowtable *parsed_ct_ft =3D NULL; + struct otx2_nic *out_nic; + int parsed_ct_refs =3D 0; + int used =3D 0; + int err; + int i; + + if (!flow_action_has_entries(flow_action)) + return -EINVAL; + + flow_action_for_each(i, act, flow_action) { + switch (act->id) { + case FLOW_ACTION_REDIRECT: + if (!act->dev || !sw_nb_is_valid_dev(act->dev)) { + err =3D -EOPNOTSUPP; + goto unwind_ct; + } + trace_sw_act_dump(__func__, "redirect to egress port", act->id); + tuple->in_pf =3D nic->pcifunc; + out_nic =3D netdev_priv(act->dev); + tuple->xmit_pf =3D out_nic->pcifunc; + *op |=3D BIT_ULL(FLOW_ACTION_REDIRECT); + break; + + case FLOW_ACTION_CT: + trace_sw_act_dump(__func__, "register conntrack offload callback", act-= >id); + err =3D sw_fl_ct_cb_get(act->ct.flow_table, nic); + if (err) { + netdev_err(nic->netdev, + "%s: Error to offload flow, err=3D%d\n", + __func__, err); + goto unwind_ct; + } + + parsed_ct_ft =3D act->ct.flow_table; + parsed_ct_refs++; + if (ct_ft) + *ct_ft =3D act->ct.flow_table; + *op |=3D BIT_ULL(FLOW_ACTION_CT); + break; + + case FLOW_ACTION_MANGLE: { + int layer; + + if (used >=3D MANGLE_ARR_SZ) { + netdev_err(nic->netdev, + "%s: More mangle entries than supported %u\n", + __func__, MANGLE_ARR_SZ); + err =3D -ENOMEM; + goto unwind_ct; + } + + layer =3D sw_fl_mangle_layer(act->mangle.htype); + if (layer < 0) { + err =3D layer; + goto unwind_ct; + } + + trace_sw_act_dump(__func__, "header mangle action", act->id); + tuple->mangle[used].type =3D act->mangle.htype; + tuple->mangle[used].val =3D act->mangle.val; + tuple->mangle[used].mask =3D act->mangle.mask; + tuple->mangle[used].offset =3D act->mangle.offset; + tuple->mangle_map[layer] |=3D BIT(used); + used++; + break; + } + + default: + trace_sw_act_dump(__func__, "unsupported flow action", act->id); + break; + } + } + + tuple->mangle_cnt =3D used; + + if (!*op && !used) { + netdev_dbg(nic->netdev, "%s: Op is not valid\n", __func__); + return -EOPNOTSUPP; + } + + return 0; + +unwind_ct: + while (parsed_ct_refs--) + sw_fl_ct_cb_put(parsed_ct_ft, nic); + return err; +} + +static int sw_fl_get_route(struct net *net, struct fib_result *res, __be32= addr) +{ + struct flowi4 fl4; + + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr =3D addr; + return fib_lookup(net, &fl4, res, 0); +} + +static int sw_fl_get_pcifunc(struct otx2_nic *pf, __be32 dst, u16 *pcifunc, + struct fl_tuple *ftuple, bool is_in_dev) +{ + struct fib_nh_common *fib_nhc; + struct net_device *dev, *br; + struct fib_result res; + struct list_head *lh; + struct otx2_nic *nic; + int err; + + rcu_read_lock(); + + err =3D sw_fl_get_route(dev_net(pf->netdev), &res, dst); + if (err) { + netdev_err(pf->netdev, + "%s: Failed to find route to dst %pI4\n", + __func__, &dst); + goto done; + } + + if (res.fi->fib_type !=3D RTN_UNICAST) { + netdev_err(pf->netdev, + "%s: Not unicast route to dst %pi4\n", + __func__, &dst); + err =3D -EFAULT; + goto done; + } + + fib_nhc =3D fib_info_nhc(res.fi, 0); + if (!fib_nhc) { + err =3D -EINVAL; + netdev_err(pf->netdev, + "%s: Could not get fib_nhc for %pI4\n", + __func__, &dst); + goto done; + } + + if (unlikely(netif_is_bridge_master(fib_nhc->nhc_dev))) { + br =3D fib_nhc->nhc_dev; + + if (is_in_dev) + ftuple->is_indev_br =3D 1; + else + ftuple->is_xdev_br =3D 1; + + lh =3D &br->adj_list.lower; + if (list_empty(lh)) { + netdev_err(pf->netdev, + "%s: Unable to find any slave device\n", + __func__); + err =3D -EINVAL; + goto done; + } + dev =3D netdev_next_lower_dev_rcu(br, &lh); + + } else { + dev =3D fib_nhc->nhc_dev; + } + + if (!dev || !sw_nb_is_valid_dev(dev)) { + netdev_err(pf->netdev, + "%s: flow acceleration support is only for cavium devices\n", + __func__); + err =3D -EOPNOTSUPP; + goto done; + } + + nic =3D netdev_priv(dev); + *pcifunc =3D nic->pcifunc; + +done: + rcu_read_unlock(); + return err; +} + +static int sw_fl_parse_flow(struct otx2_nic *nic, struct flow_cls_offload = *f, + struct fl_tuple *tuple, u64 *features) +{ + struct flow_rule *rule; + u8 ip_proto =3D 0; + + *features =3D 0; + + rule =3D flow_cls_offload_flow_rule(f); + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic match; + + flow_rule_match_basic(rule, &match); + + /* All EtherTypes can be matched, no hw limitation */ + + if (match.mask->n_proto) { + tuple->eth_type =3D match.key->n_proto; + tuple->m_eth_type =3D match.mask->n_proto; + *features |=3D BIT_ULL(NPC_ETYPE); + } + + if (match.mask->ip_proto) { + if (match.key->ip_proto !=3D IPPROTO_TCP && + match.key->ip_proto !=3D IPPROTO_UDP) + return -EOPNOTSUPP; + + ip_proto =3D match.key->ip_proto; + if (ip_proto =3D=3D IPPROTO_UDP) + *features |=3D BIT_ULL(NPC_IPPROTO_UDP); + else + *features |=3D BIT_ULL(NPC_IPPROTO_TCP); + } + + tuple->proto =3D ip_proto; + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + + flow_rule_match_eth_addrs(rule, &match); + + /* Switch flow offload matches unicast L2 addresses only. + * Multicast and broadcast MAC keys are not programmed in + * hardware; rules that rely solely on those keys are not + * supported here. + */ + if (!is_zero_ether_addr(match.key->dst) && + is_unicast_ether_addr(match.key->dst)) { + ether_addr_copy(tuple->dmac, + match.key->dst); + + ether_addr_copy(tuple->m_dmac, + match.mask->dst); + + *features |=3D BIT_ULL(NPC_DMAC); + } + + if (!is_zero_ether_addr(match.key->src) && + is_unicast_ether_addr(match.key->src)) { + ether_addr_copy(tuple->smac, + match.key->src); + ether_addr_copy(tuple->m_smac, + match.mask->src); + *features |=3D BIT_ULL(NPC_SMAC); + } + } + + /* Switch flow offload parses IPv4 address keys only; IPv6 flow + * matching and FIB-based pcifunc resolution are not supported yet. + */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_match_ipv4_addrs match; + + flow_rule_match_ipv4_addrs(rule, &match); + + if (match.mask->dst) { + tuple->ip4dst =3D match.key->dst; + tuple->m_ip4dst =3D match.mask->dst; + *features |=3D BIT_ULL(NPC_DIP_IPV4); + } + + if (match.mask->src) { + tuple->ip4src =3D match.key->src; + tuple->m_ip4src =3D match.mask->src; + *features |=3D BIT_ULL(NPC_SIP_IPV4); + } + } + + if (!(*features & BIT_ULL(NPC_DMAC))) { + if (!tuple->m_ip4src || !tuple->m_ip4dst) { + netdev_err(nic->netdev, + "%s: Invalid src=3D%pI4 and dst=3D%pI4 addresses\n", + __func__, &tuple->ip4src, &tuple->ip4dst); + return -EINVAL; + } + + if ((tuple->ip4src & tuple->m_ip4src) =3D=3D (tuple->ip4dst & tuple->m_i= p4dst)) { + netdev_err(nic->netdev, + "%s: Masked values are same; Invalid src=3D%pI4 and dst=3D%pI4 addr= esses\n", + __func__, &tuple->ip4src, &tuple->ip4dst); + return -EINVAL; + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports match; + + flow_rule_match_ports(rule, &match); + + if (ip_proto =3D=3D IPPROTO_UDP) { + if (match.mask->dst) + *features |=3D BIT_ULL(NPC_DPORT_UDP); + + if (match.mask->src) + *features |=3D BIT_ULL(NPC_SPORT_UDP); + } else if (ip_proto =3D=3D IPPROTO_TCP) { + if (match.mask->dst) + *features |=3D BIT_ULL(NPC_DPORT_TCP); + + if (match.mask->src) + *features |=3D BIT_ULL(NPC_SPORT_TCP); + } + + if (match.mask->src) { + tuple->sport =3D match.key->src; + tuple->m_sport =3D match.mask->src; + } + + if (match.mask->dst) { + tuple->dport =3D match.key->dst; + tuple->m_dport =3D match.mask->dst; + } + } + + if (!(*features & (BIT_ULL(NPC_DMAC) | + BIT_ULL(NPC_SMAC) | + BIT_ULL(NPC_DIP_IPV4) | + BIT_ULL(NPC_SIP_IPV4) | + BIT_ULL(NPC_DIP_IPV6) | + BIT_ULL(NPC_SIP_IPV6) | + BIT_ULL(NPC_DPORT_UDP) | + BIT_ULL(NPC_SPORT_UDP) | + BIT_ULL(NPC_DPORT_TCP) | + BIT_ULL(NPC_SPORT_TCP)))) { + return -EINVAL; + } + + tuple->features =3D *features; + + return 0; +} + +static int sw_fl_add(struct otx2_nic *nic, struct flow_cls_offload *f) +{ + struct nf_flowtable *ct_ft =3D NULL; + struct fl_tuple tuple =3D { 0 }; + struct flow_rule *rule; + u64 features =3D 0; + u64 op =3D 0; + int rc; + + rule =3D flow_cls_offload_flow_rule(f); + + rc =3D sw_fl_parse_actions(nic, &rule->action, f, &tuple, &op, &ct_ft); + if (rc) + return rc; + + if (ct_ft) { + rc =3D sw_fl_ct_cookie_add(f->cookie, ct_ft, nic); + if (rc) { + sw_fl_ct_cb_put(ct_ft, nic); + return rc; + } + } + + if (op =3D=3D BIT_ULL(FLOW_ACTION_CT) && !tuple.mangle_cnt) + return 0; + + rc =3D sw_fl_parse_flow(nic, f, &tuple, &features); + if (rc) { + trace_sw_fl_dump(__func__, "flow key parse failed", &tuple); + if (ct_ft) + sw_fl_ct_cookie_put(f->cookie); + return -EFAULT; + } + + /* Non-OVS ports resolve ingress and egress pcifunc via IPv4 FIB + * lookups below. IPv6 and L2-only flows are not supported yet. + */ + if (!netif_is_ovs_port(nic->netdev)) { + rc =3D sw_fl_get_pcifunc(nic, tuple.ip4src, &tuple.in_pf, + &tuple, true); + if (rc) { + trace_sw_fl_dump(__func__, "ingress pcifunc lookup failed", &tuple); + if (ct_ft) + sw_fl_ct_cookie_put(f->cookie); + return rc; + } + + rc =3D sw_fl_get_pcifunc(nic, tuple.ip4dst, + &tuple.xmit_pf, &tuple, false); + if (rc) { + trace_sw_fl_dump(__func__, "egress pcifunc lookup failed", &tuple); + if (ct_ft) + sw_fl_ct_cookie_put(f->cookie); + return rc; + } + } + + trace_sw_fl_dump(__func__, "offload flow add queued", &tuple); + return sw_fl_add_to_list(nic, &tuple, f->cookie, true); +} + +static int sw_fl_del(struct otx2_nic *nic, struct flow_cls_offload *f) +{ + sw_fl_ct_cookie_put(f->cookie); + return sw_fl_add_to_list(nic, NULL, f->cookie, false); +} + +static int sw_fl_stats(struct otx2_nic *nic, struct flow_cls_offload *f) +{ + struct fl_get_stats_req *req; + struct fl_get_stats_rsp *rsp; + u64 pkts_diff; + int rc =3D 0; + + mutex_lock(&nic->mbox.lock); + + req =3D otx2_mbox_alloc_msg_fl_get_stats(&nic->mbox); + if (!req) { + netdev_err(nic->netdev, + "%s: Error happened while mcam alloc req\n", + __func__); + rc =3D -ENOMEM; + goto fail; + } + req->cookie =3D f->cookie; + + rc =3D otx2_sync_mbox_msg(&nic->mbox); + if (rc) + goto fail; + + rsp =3D (struct fl_get_stats_rsp *)otx2_mbox_get_rsp + (&nic->mbox.mbox, 0, &req->hdr); + if (IS_ERR(rsp)) { + rc =3D PTR_ERR(rsp); + goto fail; + } + pkts_diff =3D rsp->pkts_diff; + mutex_unlock(&nic->mbox.lock); + + if (pkts_diff) { + flow_stats_update(&f->stats, 0x0, pkts_diff, + 0x0, jiffies, + FLOW_ACTION_HW_STATS_IMMEDIATE); + } + return 0; +fail: + mutex_unlock(&nic->mbox.lock); + return rc; +} + +static bool init_done; + +int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type, + void *type_data, void *cb_priv) +{ + struct flow_cls_offload *cls =3D type_data; + struct otx2_nic *nic =3D cb_priv; + + if (!smp_load_acquire(&init_done)) /* published in sw_fl_init() */ + return 0; + + switch (cls->command) { + case FLOW_CLS_REPLACE: + return sw_fl_add(nic, cls); + case FLOW_CLS_DESTROY: + return sw_fl_del(nic, cls); + case FLOW_CLS_STATS: + return sw_fl_stats(nic, cls); + default: + break; + } + + return -EOPNOTSUPP; +} + int sw_fl_init(void) { + sw_fl_wq =3D alloc_workqueue("sw_fl_wq", 0, 0); + if (!sw_fl_wq) + return -ENOMEM; + + smp_store_release(&init_done, true); /* visible to sw_fl_setup_ft_block_i= ngress_cb() */ return 0; } =20 void sw_fl_deinit(void) { + struct sw_fl_list_entry *entry; + struct workqueue_struct *wq; + LIST_HEAD(tlist); + + smp_store_release(&init_done, false); /* visible to sw_fl_setup_ft_block_= ingress_cb() */ + + spin_lock_bh(&sw_fl_lock); + wq =3D sw_fl_wq; + sw_fl_wq =3D NULL; + spin_unlock_bh(&sw_fl_lock); + + if (!wq) + return; + + cancel_delayed_work_sync(&sw_fl_work); + destroy_workqueue(wq); + + spin_lock_bh(&sw_fl_lock); + list_splice_init(&sw_fl_lh, &tlist); + spin_unlock_bh(&sw_fl_lock); + + while ((entry =3D + list_first_entry_or_null(&tlist, + struct sw_fl_list_entry, + list)) !=3D NULL) { + list_del_init(&entry->list); + netdev_put(entry->pf->netdev, &entry->dev_tracker); + kfree(entry); + } + + sw_fl_ct_cb_flush(); } +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h b/dr= ivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h index cd018d770a8a..8dd816eb17d2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h @@ -9,5 +9,7 @@ =20 void sw_fl_deinit(void); int sw_fl_init(void); +int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type, + void *type_data, void *cb_priv); =20 #endif // SW_FL_H diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c new file mode 100644 index 000000000000..672f3405de85 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#define CREATE_TRACE_POINTS +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) +#include "sw_trace.h" +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h b= /drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h new file mode 100644 index 000000000000..f4e2832939e4 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2026 Marvell. + * + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rvu_sw + +#if !defined(SW_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define SW_TRACE_H + +#include +#include +#include + +#include "mbox.h" + +TRACE_EVENT(sw_fl_dump, + TP_PROTO(const char *fname, const char *info, struct fl_tuple *ftuple= ), + TP_ARGS(fname, info, ftuple), + TP_STRUCT__entry(__string(f, fname) + __string(info, info) + __array(u8, smac, ETH_ALEN) + __array(u8, dmac, ETH_ALEN) + __field_struct(__be16, eth_type) + __field_struct(__be32, sip) + __field_struct(__be32, dip) + __field(u8, ip_proto) + __field_struct(__be16, sport) + __field_struct(__be16, dport) + __field(u8, uni_di) + __field(u16, in_pf) + __field(u16, out_pf) + ), + TP_fast_assign(__assign_str(f); + __assign_str(info); + memcpy(__entry->smac, ftuple->smac, ETH_ALEN); + memcpy(__entry->dmac, ftuple->dmac, ETH_ALEN); + __entry->sip =3D ftuple->ip4src; + __entry->dip =3D ftuple->ip4dst; + __entry->eth_type =3D ftuple->eth_type; + __entry->ip_proto =3D ftuple->proto; + __entry->sport =3D ftuple->sport; + __entry->dport =3D ftuple->dport; + __entry->uni_di =3D ftuple->uni_di; + __entry->in_pf =3D ftuple->in_pf; + __entry->out_pf =3D ftuple->xmit_pf; + ), + TP_printk("[%s] %s: %pM %pI4:%u to %pM %pI4:%u eth_type=3D%#x proto= =3D%u uni=3D%u in=3D%#x out=3D%#x", + __get_str(f), __get_str(info), + __entry->smac, &__entry->sip, __entry->sport, + __entry->dmac, &__entry->dip, __entry->dport, + __entry->eth_type, __entry->ip_proto, __entry->uni_di, + __entry->in_pf, __entry->out_pf) +); + +TRACE_EVENT(sw_act_dump, + TP_PROTO(const char *fname, const char *info, u32 act), + TP_ARGS(fname, info, act), + TP_STRUCT__entry(__string(fname, fname) + __string(info, info) + __field(u32, act) + ), + + TP_fast_assign(__assign_str(fname); + __assign_str(info); + __entry->act =3D act; + ), + + TP_printk("[%s] %s: act=3D%u", + __get_str(fname), __get_str(info), __entry->act) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE sw_trace + +#include --=20 2.43.0