Add functions and structures to allocate virtual router.
prestera_hw_vr_create() return index of allocated VR so that we can move
forward and also add another objects (e.g. router interface),
which has link to VR.
Co-developed-by: Taras Chornyi <redacted>
Signed-off-by: Taras Chornyi <redacted>
Co-developed-by: Oleksandr Mazur <redacted>
Signed-off-by: Oleksandr Mazur <redacted>
Signed-off-by: Yevhen Orlov <redacted>
---
.../ethernet/marvell/prestera/prestera_hw.c | 42 +++++++++++++++++++
.../ethernet/marvell/prestera/prestera_hw.h | 4 ++
2 files changed, 46 insertions(+)
Add functions to enable routing on port, which is not in vlan.
Also we can enable routing on vlan.
prestera_hw_rif_create() take index of allocated virtual router.
Co-developed-by: Taras Chornyi <redacted>
Signed-off-by: Taras Chornyi <redacted>
Co-developed-by: Oleksandr Mazur <redacted>
Signed-off-by: Oleksandr Mazur <redacted>
Signed-off-by: Yevhen Orlov <redacted>
---
.../net/ethernet/marvell/prestera/prestera.h | 23 +++++
.../ethernet/marvell/prestera/prestera_hw.c | 97 +++++++++++++++++++
.../ethernet/marvell/prestera/prestera_hw.h | 7 ++
3 files changed, 127 insertions(+)
@@ -225,6 +225,29 @@ struct prestera_event {};};+enumprestera_if_type{+/* the interface is of port type (dev,port) */+PRESTERA_IF_PORT_E=0,++/* the interface is of lag type (lag-id) */+PRESTERA_IF_LAG_E=1,++/* the interface is of Vid type (vlan-id) */+PRESTERA_IF_VID_E=3,+};++structprestera_iface{+enumprestera_if_typetype;+struct{+u32hw_dev_num;+u32port_num;+}dev_port;+u32hw_dev_num;+u16vr_id;+u16lag_id;+u16vlan_id;+};+structprestera_switchdev;structprestera_span;structprestera_rxtx;
@@ -564,8 +596,12 @@ static void prestera_hw_build_tests(void)BUILD_BUG_ON(sizeof(structprestera_msg_acl_action)!=32);BUILD_BUG_ON(sizeof(structprestera_msg_counter_req)!=16);BUILD_BUG_ON(sizeof(structprestera_msg_counter_stats)!=16);+BUILD_BUG_ON(sizeof(structprestera_msg_rif_req)!=36);BUILD_BUG_ON(sizeof(structprestera_msg_vr_req)!=8);+/* structure that are part of req/resp fw messages */+BUILD_BUG_ON(sizeof(structprestera_msg_iface)!=16);+/* check responses */BUILD_BUG_ON(sizeof(structprestera_msg_common_resp)!=8);BUILD_BUG_ON(sizeof(structprestera_msg_switch_init_resp)!=24);
@@ -0,0 +1,28 @@+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0+/* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */++#include<linux/kernel.h>+#include<linux/types.h>++#include"prestera.h"++intprestera_router_init(structprestera_switch*sw)+{+structprestera_router*router;++router=kzalloc(sizeof(*sw->router),GFP_KERNEL);+if(!router)+return-ENOMEM;++sw->router=router;+router->sw=sw;++return0;+}++voidprestera_router_fini(structprestera_switch*sw)+{+kfree(sw->router);+sw->router=NULL;+}+
Add prestera_router_hw.c. This file contains functions, which track HW
objects relations and links. This include implicity creation of objects,
that needed by requested one and implicity removing of objects, which
reference counter is became zero.
We need this layer, because kernel callbacks not always mapped to
creation of single HW object. So let it be two different layers - one
for subscribing and parsing kernel structures, and another
(prestera_router_hw.c) for HW objects relations tracking.
There is two types of objects on router_hw layer:
- Explicit objects (rif_entry) : created by higher layer.
- Implicit objects (vr) : created on demand by explicit objects.
Co-developed-by: Taras Chornyi <redacted>
Signed-off-by: Taras Chornyi <redacted>
Co-developed-by: Oleksandr Mazur <redacted>
Signed-off-by: Oleksandr Mazur <redacted>
Signed-off-by: Yevhen Orlov <redacted>
---
.../net/ethernet/marvell/prestera/Makefile | 2 +-
.../marvell/prestera/prestera_router.c | 10 +
.../marvell/prestera/prestera_router_hw.c | 209 ++++++++++++++++++
.../marvell/prestera/prestera_router_hw.h | 36 +++
4 files changed, 256 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_router_hw.c
create mode 100644 drivers/net/ethernet/marvell/prestera/prestera_router_hw.h
@@ -0,0 +1,209 @@+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0+/* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */++#include<linux/rhashtable.h>++#include"prestera.h"+#include"prestera_hw.h"+#include"prestera_router_hw.h"+#include"prestera_acl.h"++/* +--++*+------->|vr|+*|+--++*|+*+-+-------++*|rif_entry|+*+---------++*Rifis+*usedas+*entrypoint+*forvrinhw+*/++intprestera_router_hw_init(structprestera_switch*sw)+{+INIT_LIST_HEAD(&sw->router->vr_list);+INIT_LIST_HEAD(&sw->router->rif_entry_list);++return0;+}++staticstructprestera_vr*__prestera_vr_find(structprestera_switch*sw,+u32tb_id)+{+structprestera_vr*vr;++list_for_each_entry(vr,&sw->router->vr_list,router_node){+if(vr->tb_id==tb_id)+returnvr;+}++returnNULL;+}++staticstructprestera_vr*__prestera_vr_create(structprestera_switch*sw,+u32tb_id,+structnetlink_ext_ack*extack)+{+structprestera_vr*vr;+u16hw_vr_id;+interr;++err=prestera_hw_vr_create(sw,&hw_vr_id);+if(err)+returnERR_PTR(-ENOMEM);++vr=kzalloc(sizeof(*vr),GFP_KERNEL);+if(!vr){+err=-ENOMEM;+gotoerr_alloc_vr;+}++vr->tb_id=tb_id;+vr->hw_vr_id=hw_vr_id;++list_add(&vr->router_node,&sw->router->vr_list);++returnvr;++err_alloc_vr:+prestera_hw_vr_delete(sw,hw_vr_id);+kfree(vr);+returnERR_PTR(err);+}++staticvoid__prestera_vr_destroy(structprestera_switch*sw,+structprestera_vr*vr)+{+prestera_hw_vr_delete(sw,vr->hw_vr_id);+list_del(&vr->router_node);+kfree(vr);+}++staticstructprestera_vr*prestera_vr_get(structprestera_switch*sw,u32tb_id,+structnetlink_ext_ack*extack)+{+structprestera_vr*vr;++vr=__prestera_vr_find(sw,tb_id);+if(!vr)+vr=__prestera_vr_create(sw,tb_id,extack);+if(IS_ERR(vr))+returnERR_CAST(vr);++returnvr;+}++staticvoidprestera_vr_put(structprestera_switch*sw,structprestera_vr*vr)+{+if(!vr->ref_cnt)+__prestera_vr_destroy(sw,vr);+}++/* iface is overhead struct. vr_id also can be removed. */+staticint+__prestera_rif_entry_key_copy(conststructprestera_rif_entry_key*in,+structprestera_rif_entry_key*out)+{+memset(out,0,sizeof(*out));++switch(in->iface.type){+casePRESTERA_IF_PORT_E:+out->iface.dev_port.hw_dev_num=in->iface.dev_port.hw_dev_num;+out->iface.dev_port.port_num=in->iface.dev_port.port_num;+break;+casePRESTERA_IF_LAG_E:+out->iface.lag_id=in->iface.lag_id;+break;+casePRESTERA_IF_VID_E:+out->iface.vlan_id=in->iface.vlan_id;+break;+default:+pr_err("Unsupported iface type");+return-EINVAL;+}++out->iface.type=in->iface.type;+return0;+}++structprestera_rif_entry*+prestera_rif_entry_find(conststructprestera_switch*sw,+conststructprestera_rif_entry_key*k)+{+structprestera_rif_entry*rif_entry;+structprestera_rif_entry_keylk;/* lookup key */++if(__prestera_rif_entry_key_copy(k,&lk))+returnNULL;++list_for_each_entry(rif_entry,&sw->router->rif_entry_list,+router_node){+if(!memcmp(k,&rif_entry->key,sizeof(*k)))+returnrif_entry;+}++returnNULL;+}++voidprestera_rif_entry_destroy(structprestera_switch*sw,+structprestera_rif_entry*e)+{+structprestera_ifaceiface;++list_del(&e->router_node);++memcpy(&iface,&e->key.iface,sizeof(iface));+iface.vr_id=e->vr->hw_vr_id;+prestera_hw_rif_delete(sw,e->hw_id,&iface);++e->vr->ref_cnt--;+prestera_vr_put(sw,e->vr);+kfree(e);+}++structprestera_rif_entry*+prestera_rif_entry_create(structprestera_switch*sw,+structprestera_rif_entry_key*k,+u32tb_id,constunsignedchar*addr)+{+interr;+structprestera_rif_entry*e;+structprestera_ifaceiface;++e=kzalloc(sizeof(*e),GFP_KERNEL);+if(!e)+gotoerr_kzalloc;++if(__prestera_rif_entry_key_copy(k,&e->key))+gotoerr_key_copy;++e->vr=prestera_vr_get(sw,tb_id,NULL);+if(IS_ERR(e->vr))+gotoerr_vr_get;++e->vr->ref_cnt++;+memcpy(&e->addr,addr,sizeof(e->addr));++/* HW */+memcpy(&iface,&e->key.iface,sizeof(iface));+iface.vr_id=e->vr->hw_vr_id;+err=prestera_hw_rif_create(sw,&iface,e->addr,&e->hw_id);+if(err)+gotoerr_hw_create;++list_add(&e->router_node,&sw->router->rif_entry_list);++returne;++err_hw_create:+e->vr->ref_cnt--;+prestera_vr_put(sw,e->vr);+err_vr_get:+err_key_copy:+kfree(e);+err_kzalloc:+returnNULL;+}+
@@ -3,10 +3,98 @@#include<linux/kernel.h>#include<linux/types.h>+#include<linux/inetdevice.h>#include"prestera.h"#include"prestera_router_hw.h"+staticint__prestera_inetaddr_port_event(structnet_device*port_dev,+unsignedlongevent,+structnetlink_ext_ack*extack)+{+structprestera_port*port=netdev_priv(port_dev);+interr;++err=prestera_is_valid_mac_addr(port,port_dev->dev_addr);+if(err){+NL_SET_ERR_MSG_MOD(extack,"RIF MAC must have the same prefix");+returnerr;+}++switch(event){+caseNETDEV_UP:+caseNETDEV_DOWN:+break;+}++return0;+}++staticint__prestera_inetaddr_event(structprestera_switch*sw,+structnet_device*dev,+unsignedlongevent,+structnetlink_ext_ack*extack)+{+if(prestera_netdev_check(dev)&&!netif_is_bridge_port(dev)&&+!netif_is_lag_port(dev)&&netif_is_ovs_port(dev))+return__prestera_inetaddr_port_event(dev,event,extack);++return0;+}++staticint__prestera_inetaddr_cb(structnotifier_block*nb,+unsignedlongevent,void*ptr)+{+structin_ifaddr*ifa=(structin_ifaddr*)ptr;+structnet_device*dev=ifa->ifa_dev->dev;+structprestera_router*router=container_of(nb,+structprestera_router,+inetaddr_nb);+structin_device*idev;+interr=0;++if(event!=NETDEV_DOWN)+gotoout;++/* Ignore if this is not latest address */+idev=__in_dev_get_rtnl(dev);+if(idev&&idev->ifa_list)+gotoout;++err=__prestera_inetaddr_event(router->sw,dev,event,NULL);+out:+returnnotifier_from_errno(err);+}++staticint__prestera_inetaddr_valid_cb(structnotifier_block*nb,+unsignedlongevent,void*ptr)+{+structin_validator_info*ivi=(structin_validator_info*)ptr;+structnet_device*dev=ivi->ivi_dev->dev;+structprestera_router*router=container_of(nb,+structprestera_router,+inetaddr_valid_nb);+structin_device*idev;+interr=0;++if(event!=NETDEV_UP)+gotoout;++/* Ignore if this is not first address */+idev=__in_dev_get_rtnl(dev);+if(idev&&idev->ifa_list)+gotoout;++if(ipv4_is_multicast(ivi->ivi_addr)){+err=-EINVAL;+gotoout;+}++err=__prestera_inetaddr_event(router->sw,dev,event,ivi->extack);+out:+returnnotifier_from_errno(err);+}+intprestera_router_init(structprestera_switch*sw){structprestera_router*router;
@@ -23,8 +111,22 @@ int prestera_router_init(struct prestera_switch *sw)if(err)gotoerr_router_lib_init;+router->inetaddr_valid_nb.notifier_call=__prestera_inetaddr_valid_cb;+err=register_inetaddr_validator_notifier(&router->inetaddr_valid_nb);+if(err)+gotoerr_register_inetaddr_validator_notifier;++router->inetaddr_nb.notifier_call=__prestera_inetaddr_cb;+err=register_inetaddr_notifier(&router->inetaddr_nb);+if(err)+gotoerr_register_inetaddr_notifier;+return0;+err_register_inetaddr_notifier:+unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb);+err_register_inetaddr_validator_notifier:+/* prestera_router_hw_fini */err_router_lib_init:kfree(sw->router);returnerr;
@@ -32,6 +134,9 @@ int prestera_router_init(struct prestera_switch *sw)voidprestera_router_fini(structprestera_switch*sw){+unregister_inetaddr_notifier(&sw->router->inetaddr_nb);+unregister_inetaddr_validator_notifier(&sw->router->inetaddr_valid_nb);+/* router_hw_fini */kfree(sw->router);sw->router=NULL;}
Add inetaddr notifiers to support add/del IPv4 address on switchdev
port. We create TRAP on first address, added on port and delete TRAP,
when last address removed.
Currently, driver supports only regular port to became routed.
Other port type support will be added later
Co-developed-by: Taras Chornyi <redacted>
Signed-off-by: Taras Chornyi <redacted>
Co-developed-by: Oleksandr Mazur <redacted>
Signed-off-by: Oleksandr Mazur <redacted>
Signed-off-by: Yevhen Orlov <redacted>
---
.../marvell/prestera/prestera_router.c | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
@@ -4,16 +4,31 @@#include<linux/kernel.h>#include<linux/types.h>#include<linux/inetdevice.h>+#include<net/switchdev.h>#include"prestera.h"#include"prestera_router_hw.h"+/* This util to be used, to convert kernel rules for default vr in hw_vr */+staticu32prestera_fix_tb_id(u32tb_id)+{+if(tb_id==RT_TABLE_UNSPEC||+tb_id==RT_TABLE_LOCAL||+tb_id==RT_TABLE_DEFAULT)+returntb_id=RT_TABLE_MAIN;++returntb_id;+}+staticint__prestera_inetaddr_port_event(structnet_device*port_dev,unsignedlongevent,structnetlink_ext_ack*extack){structprestera_port*port=netdev_priv(port_dev);interr;+structprestera_rif_entry*re;+structprestera_rif_entry_keyre_key={};+u32kern_tb_id;err=prestera_is_valid_mac_addr(port,port_dev->dev_addr);if(err){
@@ -21,9 +36,34 @@ static int __prestera_inetaddr_port_event(struct net_device *port_dev,returnerr;}+kern_tb_id=l3mdev_fib_table(port_dev);+re_key.iface.type=PRESTERA_IF_PORT_E;+re_key.iface.dev_port.hw_dev_num=port->dev_id;+re_key.iface.dev_port.port_num=port->hw_id;+re=prestera_rif_entry_find(port->sw,&re_key);+switch(event){caseNETDEV_UP:+if(re){+NL_SET_ERR_MSG_MOD(extack,"rif_entry already exist");+return-EEXIST;+}+re=prestera_rif_entry_create(port->sw,&re_key,+prestera_fix_tb_id(kern_tb_id),+port_dev->dev_addr);+if(!re){+NL_SET_ERR_MSG_MOD(extack,"Can't create rif_entry");+return-EINVAL;+}+dev_hold(port_dev);+break;caseNETDEV_DOWN:+if(!re){+NL_SET_ERR_MSG_MOD(extack,"rif_entry not exist");+return-EEXIST;+}+prestera_rif_entry_destroy(port->sw,re);+dev_put(port_dev);break;}
From: Stephen Hemminger <stephen@networkplumber.org> Date: 2021-12-17 21:03:45
On Fri, 17 Dec 2021 21:54:38 +0200
Yevhen Orlov [off-list ref] wrote:
quoted hunk
+/* This util to be used, to convert kernel rules for default vr in hw_vr */+static u32 prestera_fix_tb_id(u32 tb_id)+{+ if (tb_id == RT_TABLE_UNSPEC ||+ tb_id == RT_TABLE_LOCAL ||+ tb_id == RT_TABLE_DEFAULT)+ return tb_id = RT_TABLE_MAIN;
From: Andrew Lunn <andrew@lunn.ch> Date: 2021-12-18 16:53:09
On Fri, Dec 17, 2021 at 09:54:32PM +0200, Yevhen Orlov wrote:
Add initial router support for Marvell Prestera driver.
Subscribe on inetaddr notifications. TRAP packets, that has to be routed
(if packet has router's destination MAC address).
I must be missing something here. Why do you need to tell it the IP
address in order to perform software routing? All the switch needs to
know is the MAC address. Any packets for that MAC address should be
trapped to the host. The host can then decide what to do with it,
router, bridge, or consume it itself.
Add features:
- Support ip address adding on port.
e.g.: "ip address add PORT 1.1.1.1/24"
This should just work already. If it does not, you have something
wrong in your current support.
Andrew
On Sat, Dec 18, 2021 at 05:52:55PM +0100, Andrew Lunn wrote:
On Fri, Dec 17, 2021 at 09:54:32PM +0200, Yevhen Orlov wrote:
quoted
Add initial router support for Marvell Prestera driver.
Subscribe on inetaddr notifications. TRAP packets, that has to be routed
(if packet has router's destination MAC address).
I must be missing something here. Why do you need to tell it the IP
address in order to perform software routing? All the switch needs to
know is the MAC address. Any packets for that MAC address should be
trapped to the host. The host can then decide what to do with it,
router, bridge, or consume it itself.
You are right. We don't pass IP address. Subscription is only needed
to enable rif. IP address is not used here.
quoted
Add features:
- Support ip address adding on port.
e.g.: "ip address add PORT 1.1.1.1/24"
This should just work already. If it does not, you have something
wrong in your current support.
Andrew
Yes. For now we has just enabled TRAP's for every port. This is good for
software routing. But in order to implement routes offloading - we need
to control, on which port packets is routed. So, this patchset
prepares infrastructure for future routes offloading implementation.
On Fri, Dec 17, 2021 at 01:02:03PM -0800, Stephen Hemminger wrote:
Your router is quite limited if it only can handle a single unicast address.
We support several addresses on interface. We just have nothing to do for
second address, because rif is already enabled on this interface, after
first one.
So, answer is: we support one, two or more unicast addresses on
interface.