From mboxrd@z Thu Jan 1 00:00:00 1970 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 B2FF936B061; Thu, 2 Jul 2026 04:50:57 +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=1782967859; cv=none; b=d3F9mE1l4WnNS7lyaZE6LVK5YOGNZc8sIPZwyQwwoEvBkWRN0BBtI3uQFZ4f0afOWGoGVn4AND71IqDXw+uibDHMdfRS+T89roFjJY4Ejv0wWo/7nTbnBpK/Gv7460H0EzTCC3ftYcei0daKYI3aYfdwm6ecUJ5T5QGsmhFPDZA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782967859; c=relaxed/simple; bh=DLMkO3BEY07octikgjRope10Z4osFyKG3xn5JjVVJyE=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ujOP0jVSslmATN0Nw6UuiDioxCh/gs2404omqw+A5yjK1jGKu7lNIaOqLrDe1JBKkOaFpHE+9C0X+OMMrr/9ghWy/8eqm5Qs7ctkOG4nw6QyK6Kiv9YnNzookIOnKNCX1kg2NsmexOWTn45xU8A9bE8PRZmOjt1bPPVWoS7qyEA= 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=a4bBwVB4; 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="a4bBwVB4" 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 6621MS4V205443; Wed, 1 Jul 2026 21:50: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=h aXAGTUSMcTl3SPYpN+tkl7SBcS7/v+aUH9hTW3RtlA=; b=a4bBwVB4Pz2BNTtdd bpHxb+CbfqmEPhQM5YZ+22qc5LcK473rjtRas5WP1CmzRVbActLHW9TEojFFegKn QZni4R4nSIS8atOtV2lLxbs4bPcRLeCBnsOrVCvXphay3yIQqQoy+mkBDikVfawN VEsMmPE1tAimeGvcprTd5Z5uUEljFQE+zWW2LUIPVPLxXWEF8AHL51FK0ciJ28VN +Ie4/ViHTXHYj2bvJn1krK8dp/DHAJK8+o6flk34YQXkFMK1CiEdb8GDD2xPRzvH 9+KJ1AFolwE2GxJr0Dm67jfqnDe56lYX2HtaBkHLnUt+vmx01KRYkQ2U6n6a8IBM ky1bg== Received: from dc5-exch05.marvell.com ([199.233.59.128]) by mx0a-0016f401.pphosted.com (PPS) with ESMTPS id 4f51273gac-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 01 Jul 2026 21:50: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; Wed, 1 Jul 2026 21:50:48 -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; Wed, 1 Jul 2026 21:50:48 -0700 Received: from rkannoth-OptiPlex-7090.. (unknown [10.28.36.165]) by maili.marvell.com (Postfix) with ESMTP id 1F39A3F708F; Wed, 1 Jul 2026 21:50:45 -0700 (PDT) From: Ratheesh Kannoth To: , CC: , , , , , , "Ratheesh Kannoth" Subject: [PATCH v2 net-next 6/9] octeontx2-pf: register switch notifiers for eswitch offload Date: Thu, 2 Jul 2026 10:20:23 +0530 Message-ID: <20260702045026.2914748-7-rkannoth@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260702045026.2914748-1-rkannoth@marvell.com> References: <20260702045026.2914748-1-rkannoth@marvell.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Proofpoint-ORIG-GUID: -Se1IVcFe8FyzW3KEMNe5rkv_41eR73b X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzAyMDA0NSBTYWx0ZWRfX2Lzxisi5JNOb 0zchNMqlJ0NJXKpovlkFnT4o0LMNEYkwkQEkdtLNIrqsU8LBt8DMxMepuLbrjYxEJ0/ez0rlkPa YDgGY448DNiIi7A+525nrXuA/jT/31pOfKLN8VBjZUlgoVuGRXZrx8N8cKfusstPvK9Q6CDdg/J IxSe1v9om9cC7tdRWAsEQot7qqC3sewPYQ/Vc15g9lIiLK3h54z8cjwnrpVq+dlHWPqbe63bt7V 3W4y4Xu6qu/0YY8nT5jel+u0pcIoaQxi339E5z89mUwS06awwR/QfuFAz7s1yCaFy5YetR1ZD8T ciP8kG+CPNI/9eXNb60VdgSvv0vDMwy04bsD0o0xGne6WJPFzBl8LPeSaYaiVW96Nuijc2r5TMV YdCHC55vsz2wLMt5Un8e/an1TeYMgR1VaTnaxao0St4hX8Er/MBR81n4UPlhIDHTVJ2VQ/hulf2 MSkBPjgNXBwwbeTAPuQ== X-Authority-Analysis: v=2.4 cv=HZkkiCE8 c=1 sm=1 tr=0 ts=6a45ee29 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=fyU3dd93seVRTA3AAQQA:9 a=OBjm3rFKGHvpk9ecZwUJ:22 X-Proofpoint-GUID: -Se1IVcFe8FyzW3KEMNe5rkv_41eR73b X-Proofpoint-Spam-Info: AW1haW4tMjYwNzAyMDA0NSBTYWx0ZWRfX+FvU8aEmHQ+r ZFQMWVL9RQm2pWMW7wOttlvQJhg8dWy/4SUYVqn2IRx00r0elYV0pwu5ZTZ3zyu+DawYYwUiSuP 3+EkL3LU/cp6GxVPKhckqDvGWzDmeJg= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.125,FMLib:17.12.100.49 definitions=2026-07-02_01,2026-06-26_01,2025-10-01_01 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 | 7 +- .../net/ethernet/marvell/octeontx2/nic/rep.c | 9 + .../marvell/octeontx2/nic/switch/sw_nb.c | 418 +++++++++++++++++- .../marvell/octeontx2/nic/switch/sw_nb.h | 28 +- .../marvell/octeontx2/nic/switch/sw_nb_v4.c | 333 ++++++++++++++ .../marvell/octeontx2/nic/switch/sw_nb_v4.h | 21 + .../marvell/octeontx2/nic/switch/sw_nb_v6.c | 246 +++++++++++ .../marvell/octeontx2/nic/switch/sw_nb_v6.h | 21 + 8 files changed, 1077 insertions(+), 6 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 da87e952c187..0e12659876e0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -13,7 +13,12 @@ rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \ switch/sw_fdb.o switch/sw_fl.o ifdef CONFIG_OCTEONTX_SWITCH -rvu_nicpf-y += switch/sw_nb.o switch/sw_fib.o +rvu_nicpf-y += switch/sw_nb.o switch/sw_fib.o \ + switch/sw_nb_v4.o + +ifdef CONFIG_IPV6 +rvu_nicpf-y += switch/sw_nb_v6.o +endif endif rvu_nicvf-y := otx2_vf.o diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c index 257a2ae6a53e..e4c01ac87477 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" #define DRV_NAME "rvu_rep" #define DRV_STRING "Marvell RVU Representor Driver" @@ -399,6 +400,9 @@ static void rvu_rep_get_stats64(struct net_device *dev, static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena) { +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + struct net_device *netdev = priv->netdev; +#endif struct devlink_port_attrs attrs = {}; struct esw_cfg_req *req; @@ -414,6 +418,11 @@ static int rvu_eswitch_config(struct otx2_nic *priv, u8 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); + +#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH) + ena ? sw_nb_register(netdev) : sw_nb_unregister(netdev); +#endif + return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c index 2d14a0590c5d..fb183265a9aa 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c @@ -4,14 +4,428 @@ * 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" + +/* PF netdev for netdev_* logging when notifier info has no device */ +static struct net_device *sw_nb_pf_netdev; -int sw_nb_unregister(void) +static const char *sw_nb_cmd2str[OTX2_CMD_MAX] = { + [OTX2_DEV_UP] = "OTX2_DEV_UP", + [OTX2_DEV_DOWN] = "OTX2_DEV_DOWN", + [OTX2_DEV_CHANGE] = "OTX2_DEV_CHANGE", + [OTX2_NEIGH_UPDATE] = "OTX2_NEIGH_UPDATE", + [OTX2_FIB_ENTRY_REPLACE] = "OTX2_FIB_ENTRY_REPLACE", + [OTX2_FIB_ENTRY_ADD] = "OTX2_FIB_ENTRY_ADD", + [OTX2_FIB_ENTRY_DEL] = "OTX2_FIB_ENTRY_DEL", + [OTX2_FIB_ENTRY_APPEND] = "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 = netdev->dev.parent; + if (!dev || dev->bus != &pci_bus_type) + return false; + + pdev = to_pci_dev(dev); + if (pdev->vendor != PCI_VENDOR_ID_CAVIUM) + return false; + + return true; +} + +static int sw_nb_check_slaves(struct net_device *dev, + struct netdev_nested_priv *priv) +{ + int *cnt; + + if (!priv->flags) + return 0; + + priv->flags &= sw_nb_is_cavium_dev(dev); + if (priv->flags) { + cnt = priv->data; + (*cnt)++; + } + return 0; } -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 = 0; + + priv.flags = true; + priv.data = &cnt; + + if (netif_is_bridge_master(netdev) || is_vlan_dev(netdev)) { + /* sw_nb_is_valid_dev() is invoked in notifier callback context, so + * rtnl_lcok is already acquired + */ + netdev_walk_all_lower_dev(netdev, sw_nb_check_slaves, &priv); + return priv.flags && !!*(int *)priv.data; + } + + if (netif_is_bridge_port(netdev)) { + br = netdev_master_upper_dev_get_rcu(netdev); + if (!br) + return false; + + netdev_walk_all_lower_dev(br, sw_nb_check_slaves, &priv); + return priv.flags && !!*(int *)priv.data; + } + + 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 = switchdev_notifier_info_to_dev(ptr); + struct switchdev_notifier_fdb_info *fdb_info = 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 = { + .notifier_call = 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 = ptr; + struct net_device *log_dev; + struct fib_nh *fib_nh; + struct fib_info *fi; + int i; + + fi = fen_info->fi; + log_dev = (fi && fi->fib_nhs) ? fi->fib_nh->fib_nh_dev : sw_nb_pf_netdev; + if (log_dev) + netdev_info(log_dev, "%s: FIB event=%lu dst=%#x dstlen=%u type=%u\n", + __func__, event, fen_info->dst, fen_info->dst_len, + fen_info->type); + + if (!fi) + return; + + fib_nh = fi->fib_nh; + for (i = 0; i < fi->fib_nhs; i++, fib_nh++) { + if (!fib_nh->fib_nh_dev) + continue; + netdev_info(fib_nh->fib_nh_dev, + "%s: dev=%s saddr=%#x gw=%#x\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 = 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 = { + .notifier_call = sw_nb_fib_event, +}; + +static int sw_nb_net_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct neighbour *n = ptr; + + if (!sw_nb_is_valid_dev(n->dev)) + return NOTIFY_DONE; + + if (event != 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 = { + .notifier_call = sw_nb_net_event, + +}; + +int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev) +{ + 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 = { + .notifier_call = sw_nb_v4_inetaddr_event, +}; + +#if IS_ENABLED(CONFIG_IPV6) +static struct notifier_block sw_nb_v6_inetaddr = { + .notifier_call = 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 = netdev_notifier_info_to_dev(ptr); + struct in_device *idev; + struct inet6_dev *i6dev; + + if (event != NETDEV_CHANGE && + event != NETDEV_UP && + event != NETDEV_DOWN) { + return NOTIFY_DONE; + } + + if (!sw_nb_is_valid_dev(dev)) + return NOTIFY_DONE; + + idev = __in_dev_get_rtnl(dev); + if (idev) + sw_nb_v4_netdev_event(unused, event, ptr); + +#if IS_ENABLED(CONFIG_IPV6) + i6dev = __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 = { + .notifier_call = sw_nb_netdev_event, +}; + +int sw_nb_unregister(struct net_device *netdev) +{ + int err; + + err = unregister_switchdev_notifier(&sw_nb_fdb); + + if (err) + netdev_err(netdev, "Failed to unregister switchdev nb\n"); + + err = unregister_fib_notifier(&init_net, &sw_nb_fib); + if (err) + netdev_err(netdev, "Failed to unregister fib nb\n"); + + err = unregister_netevent_notifier(&sw_nb_netevent); + if (err) + netdev_err(netdev, "Failed to unregister netevent\n"); + + err = unregister_inetaddr_notifier(&sw_nb_v4_inetaddr); + if (err) + netdev_err(netdev, "Failed to unregister addr event\n"); + +#if IS_ENABLED(CONFIG_IPV6) + err = unregister_inet6addr_notifier(&sw_nb_v6_inetaddr); + if (err) + netdev_err(netdev, "Failed to unregister addr event\n"); +#endif + + err = unregister_netdevice_notifier(&sw_nb_netdev); + if (err) + netdev_err(netdev, "Failed to unregister netdev notifier\n"); + + sw_fl_deinit(); + sw_fib_deinit(); + sw_fdb_deinit(); + + sw_nb_pf_netdev = NULL; + return 0; } +EXPORT_SYMBOL(sw_nb_unregister); + +int sw_nb_register(struct net_device *netdev) +{ + int err; + + sw_nb_pf_netdev = netdev; + + sw_fdb_init(); + sw_fib_init(); + sw_fl_init(); + + err = register_switchdev_notifier(&sw_nb_fdb); + if (err) { + netdev_err(netdev, "Failed to register switchdev nb\n"); + sw_nb_pf_netdev = NULL; + return err; + } + + err = 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 = register_netevent_notifier(&sw_nb_netevent); + if (err) { + netdev_err(netdev, "Failed to register netevent\n"); + goto err2; + } + +#if IS_ENABLED(CONFIG_IPV6) + err = register_inet6addr_notifier(&sw_nb_v6_inetaddr); + if (err) { + netdev_err(netdev, "Failed to register addr event\n"); + goto err3; + } +#endif + + err = register_inetaddr_notifier(&sw_nb_v4_inetaddr); + if (err) { + netdev_err(netdev, "Failed to register addr event\n"); + goto err4; + } + + err = register_netdevice_notifier(&sw_nb_netdev); + if (err) { + netdev_err(netdev, "Failed to register netdevice nb\n"); + goto err5; + } + + 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); + + sw_fl_deinit(); + sw_fib_deinit(); + sw_fdb_deinit(); + + sw_nb_pf_netdev = NULL; + return err; +} +EXPORT_SYMBOL(sw_nb_register); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h index 5f744cc3ecbb..b0ce10ed25d4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h @@ -7,7 +7,29 @@ #ifndef SW_NB_H_ #define SW_NB_H_ -int sw_nb_register(void); -int sw_nb_unregister(void); +enum { + OTX2_DEV_UP = 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, +}; -#endif // SW_NB_H_ +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); + +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 *netdev); + +const char *sw_nb_get_cmd2str(int cmd); +#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..947dafe586a0 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c @@ -0,0 +1,333 @@ +// 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.h" +#include "sw_nb_v4.h" + +int sw_nb_v4_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct netdev_hw_addr *dev_addr; + struct net_device *pf_dev; + struct in_ifaddr *ifa; + struct fib_entry *entry; + struct in_device *idev; + struct otx2_nic *pf; + struct list_head *iter; + struct net_device *lower; + + idev = __in_dev_get_rtnl(dev); + if (!idev || !idev->ifa_list) + return NOTIFY_DONE; + + ifa = rtnl_dereference(idev->ifa_list); + + entry = kcalloc(1, sizeof(*entry), GFP_KERNEL); + if (!entry) + return NOTIFY_DONE; + + entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev); + entry->dst = (__force u32)htonl((__force u32)ifa->ifa_address); + entry->dst_len = 32; + entry->mac_valid = 1; + entry->host = 1; + + pf_dev = dev; + if (netif_is_bridge_master(dev)) { + entry->bridge = 1; + netdev_for_each_lower_dev(dev, lower, iter) { + pf_dev = lower; + break; + } + } else if (is_vlan_dev(dev)) { + entry->vlan_valid = 1; + pf_dev = vlan_dev_real_dev(dev); + entry->vlan_tag = vlan_dev_vlan_id(dev); + } + + pf = netdev_priv(pf_dev); + entry->port_id = 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 %#x, %pM, dev=%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 = (struct in_ifaddr *)ptr; + struct net_device *dev = ifa->ifa_dev->dev; + struct net_device *lower, *pf_dev; + struct netdev_hw_addr *dev_addr; + struct fib_entry *entry; + struct in_device *idev; + struct list_head *iter; + struct otx2_nic *pf; + + if (event != NETDEV_CHANGE && + event != NETDEV_UP && + event != NETDEV_DOWN) { + return NOTIFY_DONE; + } + + idev = __in_dev_get_rtnl(dev); + if (!idev || !idev->ifa_list) + return NOTIFY_DONE; + + entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC); + entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev); + entry->dst = (__force u32)htonl((__force u32)ifa->ifa_address); + entry->dst_len = 32; + entry->mac_valid = 1; + entry->host = 1; + + pf_dev = dev; + if (netif_is_bridge_master(dev)) { + entry->bridge = 1; + netdev_for_each_lower_dev(dev, lower, iter) { + pf_dev = lower; + break; + } + } else if (is_vlan_dev(dev)) { + entry->vlan_valid = 1; + pf_dev = vlan_dev_real_dev(dev); + entry->vlan_tag = vlan_dev_vlan_id(dev); + } + + pf = netdev_priv(pf_dev); + entry->port_id = 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 %#x, %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 fib_entry_notifier_info *fen_info = ptr; + struct fib_entry *entries, *iter; + struct net_device *dev, *pf_dev = NULL; + struct netdev_hw_addr *dev_addr; + struct net_device *lower; + struct list_head *lh; + struct neighbour *neigh; + struct fib_nh *fib_nh; + struct fib_info *fi; + struct otx2_nic *pf; + u32 *haddr; + int hcnt = 0; + int cnt, i; + + /* Process only UNICAST routes add or del */ + if (fen_info->type != RTN_UNICAST) + return NOTIFY_DONE; + + fi = fen_info->fi; + if (!fi) + return NOTIFY_DONE; + + if (fi->fib_nh_is_v6) { + struct net_device *log_dev = (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 = kcalloc(fi->fib_nhs, sizeof(*entries), GFP_ATOMIC); + if (!entries) + return NOTIFY_DONE; + + haddr = kcalloc(fi->fib_nhs, sizeof(u32), GFP_ATOMIC); + if (!haddr) { + kfree(entries); + return NOTIFY_DONE; + } + + iter = entries; + fib_nh = fi->fib_nh; + for (i = 0; i < fi->fib_nhs; i++, fib_nh++) { + dev = fib_nh->fib_nh_dev; + + if (!dev) + continue; + + if (dev->type != ARPHRD_ETHER) + continue; + + if (!sw_nb_is_valid_dev(dev)) + continue; + + iter->cmd = sw_nb_fib_event_to_otx2_event(event, dev); + iter->dst = fen_info->dst; + iter->dst_len = fen_info->dst_len; + iter->gw = (__force u32)htonl((__force u32)fib_nh->fib_nh_gw4); + + netdev_dbg(dev, "%s: FIB route Rule cmd=%lld dst=%#x dst_len=%d gw=%#x\n", + __func__, iter->cmd, iter->dst, iter->dst_len, iter->gw); + + pf_dev = dev; + if (netif_is_bridge_master(dev)) { + iter->bridge = 1; + netdev_for_each_lower_dev(dev, lower, lh) { + pf_dev = lower; + break; + } + } else if (is_vlan_dev(dev)) { + iter->vlan_valid = 1; + pf_dev = vlan_dev_real_dev(dev); + iter->vlan_tag = vlan_dev_vlan_id(dev); + } + + pf = netdev_priv(pf_dev); + iter->port_id = pf->pcifunc; + + if (!fib_nh->fib_nh_gw4) { + if (iter->dst || iter->dst_len) + iter++; + + continue; + } + iter->gw_valid = 1; + + if (fib_nh->nh_saddr) + haddr[hcnt++] = (__force u32)fib_nh->nh_saddr; + + rcu_read_lock(); + neigh = 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 = 1; + ether_addr_copy(iter->mac, neigh->ha); + } + + iter++; + rcu_read_unlock(); + } + + cnt = iter - entries; + if (!cnt) + return NOTIFY_DONE; + + netdev_dbg(pf_dev, "pf_dev is %s cnt=%d\n", pf_dev->name, cnt); + kfree(entries); + + if (!hcnt) + return NOTIFY_DONE; + + entries = kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC); + if (!entries) + return NOTIFY_DONE; + + iter = entries; + + for (i = 0; i < hcnt; i++, iter++) { + iter->cmd = sw_nb_fib_event_to_otx2_event(event, pf_dev); + iter->dst = (__force u32)htonl(haddr[i]); + iter->dst_len = 32; + iter->mac_valid = 1; + iter->host = 1; + iter->port_id = pf->pcifunc; + + for_each_dev_addr(pf_dev, dev_addr) { + ether_addr_copy(iter->mac, dev_addr->addr); + break; + } + + netdev_dbg(pf_dev, "%s: FIB host Rule cmd=%lld dst=%#x dst_len=%d gw=%#x %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 *lower, *pf_dev; + struct neighbour *n = ptr; + struct fib_entry *entry; + struct list_head *iter; + struct otx2_nic *pf; + + if (n->tbl != &arp_tbl) + return NOTIFY_DONE; + + entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC); + entry->cmd = OTX2_NEIGH_UPDATE; + entry->dst = (__force u32)htonl(*(u32 *)n->primary_key); + entry->dst_len = n->tbl->key_len * 8; + entry->mac_valid = 1; + entry->nud_state = n->nud_state; + neigh_ha_snapshot(entry->mac, n, n->dev); + ether_addr_copy(entry->mac, n->ha); + + pf_dev = n->dev; + + rcu_read_lock(); + if (netif_is_bridge_master(n->dev)) { + entry->bridge = 1; + /* Get first lower device thru which we can send MBOX to AF */ + netdev_for_each_lower_dev(n->dev, lower, iter) { + pf_dev = lower; + break; + } + } else if (is_vlan_dev(n->dev)) { + entry->vlan_valid = 1; + pf_dev = vlan_dev_real_dev(n->dev); + entry->vlan_tag = vlan_dev_vlan_id(n->dev); + } + + pf = netdev_priv(pf_dev); + entry->port_id = pf->pcifunc; + + rcu_read_unlock(); + + kfree(entry); + return NOTIFY_DONE; +} 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..cc908f565d24 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c @@ -0,0 +1,246 @@ +// 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.h" +#include "sw_nb_v6.h" + +#if IS_ENABLED(CONFIG_IPV6) + +int sw_nb_v6_netdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct netdev_hw_addr *dev_addr; + struct inet6_ifaddr *ifp; + struct fib_entry *entry; + struct inet6_dev *i6dev; + struct otx2_nic *pf; + + i6dev = __in6_dev_get(dev); + + rcu_read_lock(); + ifp = 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; + } + + pf = netdev_priv(dev); + + entry = kcalloc(1, sizeof(*entry), GFP_KERNEL); + entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev); + memcpy(entry->dst6, &ifp->addr, sizeof(entry->dst6)); + entry->dst6_plen = ifp->prefix_len; + entry->host = 1; + entry->ipv6 = 1; + entry->port_id = pf->pcifunc; + + for_each_dev_addr(dev, dev_addr) { + entry->mac_valid = 1; + ether_addr_copy(entry->mac, dev_addr->addr); + break; + } + + netdev_dbg(dev, "netdev event %pM plen=%u mac=%pM\n", + &ifp->addr, ifp->prefix_len, entry->mac); + rcu_read_unlock(); + 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 = ptr; + struct net_device *fib_dev; + struct fib_entry *entry; + struct fib6_info *f6i; + struct neighbour *neigh; + struct fib6_nh *nh6; + struct otx2_nic *pf; + struct rt6key *key; + + f6_eni = container_of(info, struct fib6_entry_notifier_info, info); + f6i = f6_eni->rt; + + fib_dev = fib6_info_nh_dev(f6i); + + if (!fib_dev) + return NOTIFY_DONE; + + if (fib_dev->type != ARPHRD_ETHER) + return NOTIFY_DONE; + + if (!sw_nb_is_cavium_dev(fib_dev)) + return NOTIFY_DONE; + + if (f6i->fib6_type != RTN_UNICAST) + return NOTIFY_DONE; + + key = &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=%pI6c len=%u\n", &key->addr, + key->plen); + + netdev_dbg(fib_dev, "fib6flags=%#x proto=%u type=%u\n", + f6i->fib6_flags, f6i->fib6_protocol, f6i->fib6_type); + + nh6 = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh; + netdev_dbg(nh6->fib_nh_dev ? nh6->fib_nh_dev : fib_dev, + "nh family=%u dev=%s gw=%pI6c gwfamily=%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 = netdev_priv(fib_dev); + + entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC); + if (!entry) + return NOTIFY_DONE; + + entry->cmd = sw_nb_fib_event_to_otx2_event(event, fib_dev); + entry->ipv6 = 1; + entry->port_id = pf->pcifunc; + memcpy(entry->dst6, &key->addr, sizeof(entry->dst6)); + entry->dst6_plen = key->plen; + + memcpy(entry->gw6, &nh6->fib_nh_gw6, sizeof(nh6->fib_nh_gw6)); + entry->gw_valid = !!(ipv6_addr_type(&nh6->fib_nh_gw6) & IPV6_ADDR_UNICAST); + + rcu_read_lock(); + neigh = 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 = 1; + ether_addr_copy(entry->mac, neigh->ha); + netdev_dbg(fib_dev, "fib found MAC=%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 neighbour *n = ptr; + struct fib_entry *entry; + struct net_device *pf_dev; + struct otx2_nic *pf; + + if (n->tbl != &nd_tbl) + return NOTIFY_DONE; + + if (ipv6_addr_type((struct in6_addr *)n->primary_key) & IPV6_ADDR_LINKLOCAL) + return NOTIFY_DONE; + + pf_dev = n->dev; + pf = netdev_priv(pf_dev); + + entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC); + entry->cmd = OTX2_NEIGH_UPDATE; + + entry->dst6_plen = n->tbl->key_len * 8; + memcpy(entry->dst6, (struct in6_addr *)n->primary_key, + sizeof(entry->dst6)); + entry->ipv6 = 1; + entry->nud_state = n->nud_state; + ether_addr_copy(entry->mac, n->ha); + entry->mac_valid = 1; + entry->port_id = pf->pcifunc; + + netdev_dbg(n->dev, "v6 neigh update %pI6 mac=%pM plen=%u\n", + n->primary_key, n->ha, 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 = (struct inet6_ifaddr *)ptr; + struct net_device *dev = ifa6->idev->dev; + struct netdev_hw_addr *dev_addr; + struct fib_entry *entry; + struct otx2_nic *pf; + + if (event != NETDEV_CHANGE && + event != NETDEV_UP && + event != NETDEV_DOWN) { + return NOTIFY_DONE; + } + + if (dev->type != ARPHRD_ETHER) + return NOTIFY_DONE; + + if (!sw_nb_is_cavium_dev(dev)) + return NOTIFY_DONE; + + if (ipv6_addr_type(&ifa6->addr) & IPV6_ADDR_LINKLOCAL) + return NOTIFY_DONE; + + pf = netdev_priv(dev); + + entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC); + entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev); + memcpy(entry->dst6, &ifa6->addr, sizeof(entry->dst6)); + entry->dst6_plen = ifa6->prefix_len; + entry->mac_valid = 1; + entry->host = 1; + entry->ipv6 = 1; + entry->port_id = pf->pcifunc; + + for_each_dev_addr(dev, dev_addr) { + ether_addr_copy(entry->mac, dev_addr->addr); + entry->mac_valid = 1; + break; + } + + netdev_dbg(dev, "inetaddr addr=%pI6c len=%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__ -- 2.43.0