introduce rocker switch driver with hardware accelerated datapath api - phase 1: bridge fdb offload
Subsystems (12)
Jiri Pirko
maintainercover introduce rocker switch driver with hardware accelerated datapath api - phase 1: bridge fdb offload
Hi all.
This patchset is just the first phase of switch and switch-ish device support api in kernel. Note that the api will extend (our complete work can be pulled from https://github.com/jpirko/net-next-rocker).
So what this patchset includes:
- introduce switchdev api for implementing switch drivers (so far only linux bridge fdb offload is covered)
- introduce rocker switch driver which implements switchdev api
As to the discussion if there is need to have specific class of device representing the switch itself, so far we found no need to introduce that. But we are generally ok with the idea and when the time comes and it will be needed, it can be easily introduced without any disturbance.
This patchset introduces switch id export through rtnetlink and sysfs, which is similar to what we have for port id in SR-IOV. I will send iproute2 patchset for showing the switch id for port netdevs once this is applied.
For detailed description, please see individual patches.
Jiri Pirko (5):
net: rename netdev_phys_port_id to more generic name
net: introduce generic switch devices support
rtnl: expose physical switch id for particular device
net-sysfs: expose physical switch id for particular device
rocker: introduce rocker switch driver
Scott Feldman (5):
bridge: introduce fdb offloading via switchdev
bridge: call netdev_sw_port_stp_update when bridge port STP status
changes
bridge: add API to notify bridge driver of learned FBD on offloaded
device
rocker: implement rocker ofdpa flow table manipulation
rocker: implement L2 bridge offloading
Documentation/networking/switchdev.txt | 59 + MAINTAINERS | 14 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +- drivers/net/ethernet/rocker/Kconfig | 27 + drivers/net/ethernet/rocker/Makefile | 5 + drivers/net/ethernet/rocker/rocker.c | 4180 ++++++++++++++++++++++ drivers/net/ethernet/rocker/rocker.h | 427 +++ include/linux/if_bridge.h | 18 + include/linux/netdevice.h | 48 +- include/net/switchdev.h | 53 + include/uapi/linux/if_link.h | 1 + net/Kconfig | 1 + net/Makefile | 3 + net/bridge/br_fdb.c | 94 +- net/bridge/br_netlink.c | 2 + net/bridge/br_stp.c | 4 + net/bridge/br_stp_if.c | 3 + net/bridge/br_stp_timer.c | 2 + net/core/dev.c | 2 +- net/core/net-sysfs.c | 26 +- net/core/rtnetlink.c | 30 +- net/switchdev/Kconfig | 13 + net/switchdev/Makefile | 5 + net/switchdev/switchdev.c | 93 + 29 files changed, 5102 insertions(+), 18 deletions(-) create mode 100644 Documentation/networking/switchdev.txt create mode 100644 drivers/net/ethernet/rocker/Kconfig create mode 100644 drivers/net/ethernet/rocker/Makefile create mode 100644 drivers/net/ethernet/rocker/rocker.c create mode 100644 drivers/net/ethernet/rocker/rocker.h create mode 100644 include/net/switchdev.h create mode 100644 net/switchdev/Kconfig create mode 100644 net/switchdev/Makefile create mode 100644 net/switchdev/switchdev.c
Signature · 1 line
1.9.3
Jiri Pirko
maintainerPATCH 1/10 net: rename netdev_phys_port_id to more generic name
So this can be reused for identification of other "items" as well.
--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +- include/linux/netdevice.h | 16 ++++++++-------- net/core/dev.c | 2 +- net/core/net-sysfs.c | 2 +- net/core/rtnetlink.c | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-)
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +1 -1
@@ -12537,7 +12537,7 @@ static int bnx2x_validate_addr(struct net_device *dev) }static int bnx2x_get_phys_port_id (struct net_device *netdev, -struct netdev_phys_port_id *ppid) +struct netdev_phys_item_id *ppid) {struct bnx2x *bp =netdev_priv (netdev);
drivers/net/ethernet/intel/i40e/i40e_main.c +1 -1
@@ -7373,7 +7373,7 @@ static void i40e_del_vxlan_port(struct net_device *netdev,#endif static int i40e_get_phys_port_id (struct net_device *netdev, -struct netdev_phys_port_id *ppid) +struct netdev_phys_item_id *ppid) {struct i40e_netdev_priv *np =netdev_priv (netdev);struct i40e_pf *pf = np->vsi->back;
drivers/net/ethernet/mellanox/mlx4/en_netdev.c +1 -1
@@ -2258,7 +2258,7 @@ static int mlx4_en_set_vf_link_state(struct net_device *dev, int vf, int link_st#define PORT_ID_BYTE_LEN 8static int mlx4_en_get_phys_port_id (struct net_device *dev, -struct netdev_phys_port_id *ppid) +struct netdev_phys_item_id *ppid) {struct mlx4_en_priv *priv =netdev_priv (dev);struct mlx4_dev *mdev = priv->mdev->dev;
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +1 -1
@@ -460,7 +460,7 @@ static void qlcnic_82xx_cancel_idc_work(struct qlcnic_adapter *adapter) }static int qlcnic_get_phys_port_id (struct net_device *netdev, -struct netdev_phys_port_id *ppid) +struct netdev_phys_item_id *ppid) {struct qlcnic_adapter *adapter =netdev_priv (netdev);struct qlcnic_hardware_context *ahw = adapter->ahw;
include/linux/netdevice.h +8 -8
@@ -753,13 +753,13 @@ struct netdev_fcoe_hbainfo { };#endif -#define MAX_PHYS_PORT_ID_LEN 32 +#define MAX_PHYS_ITEM_ID_LEN 32 -/* This structure holds a unique identifier to identify the -* physical port used by a netdevice. +/* This structure holds a unique identifier to identify some +* physical item (port for example) used by a netdevice. */ -struct netdev_phys_port_id { -unsigned char id[MAX_PHYS_PORT_ID_LEN ]; +struct netdev_phys_item_id { +unsigned char id[MAX_PHYS_ITEM_ID_LEN ];unsigned char id_len; };
@@ -975,7 +975,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,* USB_CDC_NOTIFY_NETWORK_CONNECTION) should NOT implement this function. * * int (*ndo_get_phys_port_id)(struct net_device *dev, -* struct netdev_phys_port_id *ppid); +* struct netdev_phys_item_id *ppid); * Called to get ID of physical port of this device. If driver does * not implement this, it is assumed that the hw is not able to have * multiple net devices on single physical port.
@@ -1149,7 +1149,7 @@ struct net_device_ops {int (*ndo_change_carrier)(struct net_device *dev,bool new_carrier);int (*ndo_get_phys_port_id)(struct net_device *dev, -struct netdev_phys_port_id *ppid); +struct netdev_phys_item_id *ppid);void (*ndo_add_vxlan_port)(struct net_device *dev,sa_family_t sa_family,__be16 port);
@@ -2878,7 +2878,7 @@ void dev_set_group(struct net_device *, int);int dev_set_mac_address (struct net_device *,struct sockaddr *);int dev_change_carrier (struct net_device *,bool new_carrier);int dev_get_phys_port_id (struct net_device *dev, -struct netdev_phys_port_id *ppid); +struct netdev_phys_item_id *ppid);struct sk_buff *validate_xmit_skb_list (struct sk_buff *skb,struct net_device *dev);struct sk_buff *dev_hard_start_xmit (struct sk_buff *skb,struct net_device *dev,struct netdev_queue *txq,int *ret);
net/core/dev.c +1 -1
@@ -5817,7 +5817,7 @@ EXPORT_SYMBOL(dev_change_carrier);* Get device physical port ID */ int dev_get_phys_port_id (struct net_device *dev, -struct netdev_phys_port_id *ppid) +struct netdev_phys_item_id *ppid) {const struct net_device_ops *ops = dev->netdev_ops;
net/core/net-sysfs.c +1 -1
@@ -387,7 +387,7 @@ static ssize_t phys_port_id_show(struct device *dev,return restart_syscall ();if (dev_isalive (netdev)) { -struct netdev_phys_port_id ppid; +struct netdev_phys_item_id ppid; ret =dev_get_phys_port_id (netdev, &ppid);if (!ret)
net/core/rtnetlink.c +3 -3
@@ -868,7 +868,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, +rtnl_port_size (dev, ext_filter_mask)/* IFLA_VF_PORTS + IFLA_PORT_SELF */ +rtnl_link_get_size (dev)/* IFLA_LINKINFO */ +rtnl_link_get_af_size (dev)/* IFLA_AF_SPEC */ - +nla_total_size (MAX_PHYS_PORT_ID_LEN );/* IFLA_PHYS_PORT_ID */ + +nla_total_size (MAX_PHYS_ITEM_ID_LEN );/* IFLA_PHYS_PORT_ID */ }static int rtnl_vf_ports_fill (struct sk_buff *skb,struct net_device *dev)
@@ -952,7 +952,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,static int rtnl_phys_port_id_fill (struct sk_buff *skb,struct net_device *dev) {int err; -struct netdev_phys_port_id ppid; +struct netdev_phys_item_id ppid; err =dev_get_phys_port_id (dev, &ppid);if (err) {
@@ -1196,7 +1196,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_PROMISCUITY] = { .type = NLA_U32 }, [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, - [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */ };
Signature · 1 line
1.9.3
Jiri Pirko
maintainerPATCH 2/10 net: introduce generic switch devices support
In reply to Jiri PirkoThe goal of this is to provide a possibility to support various switch chips. Drivers should implement relevant ndos to do so. Now there is only one ndo defined:
- for getting physical switch id is in place.
Note that user can use random port netdevice to access the switch.
--- Documentation/networking/switchdev.txt | 59 ++++++++++++++++++++++++++++++++++ MAINTAINERS | 7 ++++ include/linux/netdevice.h | 10 ++++++ include/net/switchdev.h | 30 +++++++++++++++++ net/Kconfig | 1 + net/Makefile | 3 ++ net/switchdev/Kconfig | 13 ++++++++ net/switchdev/Makefile | 5 +++ net/switchdev/switchdev.c | 33 +++++++++++++++++++ 9 files changed, 161 insertions(+) create mode 100644 Documentation/networking/switchdev.txt create mode 100644 include/net/switchdev.h create mode 100644 net/switchdev/Kconfig create mode 100644 net/switchdev/Makefile create mode 100644 net/switchdev/switchdev.c
Documentation/networking/switchdev.txt new file +59 -0
@@ -0,0 +1,59 @@ +Switch (and switch-ish) device drivers HOWTO +=========================== + +Please note that the word "switch" is here used in very generic meaning. +This include devices supporting L2/L3 but also various flow offloading chips, +including switches embedded into SR-IOV NICs. + +Lets describe a topology a bit. Imagine the following example: + + +----------------------------+ +---------------+ + | SOME switch chip | | CPU | + +----------------------------+ +---------------+ + port1 port2 port3 port4 MNGMNT | PCI-E | + | | | | | +---------------+ + PHY PHY | | | | NIC0 NIC1 + | | | | | | + | | +- PCI-E -+ | | + | +------- MII -------+ | + +------------- MII ------------+ + +In this example, there are two independent lines between the switch silicon +and CPU. NIC0 and NIC1 drivers are not aware of a switch presence. They are +separate from the switch driver. SOME switch chip is by managed by a driver +via PCI-E device MNGMNT. Note that MNGMNT device, NIC0 and NIC1 may be +connected to some other type of bus. + +Now, for the previous example show the representation in kernel: + + +----------------------------+ +---------------+ + | SOME switch chip | | CPU | + +----------------------------+ +---------------+ + sw0p0 sw0p1 sw0p2 sw0p3 MNGMNT | PCI-E | + | | | | | +---------------+ + PHY PHY | | | | eth0 eth1 + | | | | | | + | | +- PCI-E -+ | | + | +------- MII -------+ | + +------------- MII ------------+ + +Lets call the example switch driver for SOME switch chip "SOMEswitch". This +driver takes care of PCI-E device MNGMNT. There is a netdevice instance sw0pX +created for each port of a switch. These netdevices are instances +of "SOMEswitch" driver. sw0pX netdevices serve as a "representation" +of the switch chip. eth0 and eth1 are instances of some other existing driver. + +The only difference of the switch-port netdevice from the ordinary netdevice +is that is implements couple more NDOs: + + ndo_sw_parent_get_id - This returns the same ID for two port netdevices + of the same physical switch chip. This is + mandatory to be implemented by all switch drivers + and serves the caller for recognition of a port + netdevice. + ndo_sw_parent_* - Functions that serve for a manipulation of the switch + chip itself (it can be though of as a "parent" of the + port, therefore the name). They are not port-specific. + Caller might use arbitrary port netdevice of the same + switch and it will make no difference. + ndo_sw_port_* - Functions that serve for a port-specific manipulation.
MAINTAINERS +7 -0
@@ -9003,6 +9003,13 @@ F: lib/swiotlb.c F: arch/*/kernel/pci-swiotlb.c F: include/linux/swiotlb.h +SWITCHDEV +M: Jiri Pirko <[email protected]> +L: [email protected] +S: Supported +F: net/switchdev/ +F: include/net/switchdev.h + SYNOPSYS ARC ARCHITECTURE M: Vineet Gupta <[email protected]> S: Supported
include/linux/netdevice.h +10 -0
@@ -1017,6 +1017,12 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,* performing GSO on a packet. The device returns true if it is * able to GSO the packet, false otherwise. If the return value is * false the stack will do software GSO. +* +* int (*ndo_sw_parent_id_get)(struct net_device *dev, +* struct netdev_phys_item_id *psid); +* Called to get an ID of the switch chip this port is part of. +* If driver implements this, it indicates that it represents a port +* of a switch chip. */ struct net_device_ops {int (*ndo_init)(struct net_device *dev);
@@ -1168,6 +1174,10 @@ struct net_device_ops {int (*ndo_get_lock_subclass)(struct net_device *dev);bool (*ndo_gso_check) (struct sk_buff *skb,struct net_device *dev); +#ifdef CONFIG_NET_SWITCHDEV +int (*ndo_sw_parent_id_get)(struct net_device *dev, +struct netdev_phys_item_id *psid); +#endif };/**
include/net/switchdev.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* include/net/switchdev.h - Switch device API +* Copyright (c) 2014 Jiri Pirko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +*/ +#ifndef _LINUX_SWITCHDEV_H_ +#define _LINUX_SWITCHDEV_H_ + +#include <linux/netdevice.h> + +#ifdef CONFIG_NET_SWITCHDEV + +int netdev_sw_parent_id_get (struct net_device *dev, +struct netdev_phys_item_id *psid); + +#else + +static inline int netdev_sw_parent_id_get (struct net_device *dev, +struct netdev_phys_item_id *psid) +{ +return -EOPNOTSUPP ; +} + +#endif + +#endif /* _LINUX_SWITCHDEV_H_ */
net/Kconfig +1 -0
@@ -228,6 +228,7 @@ source "net/vmw_vsock/Kconfig" source "net/netlink/Kconfig" source "net/mpls/Kconfig" source "net/hsr/Kconfig" +source "net/switchdev/Kconfig" config RPS boolean
net/Makefile +3 -0
@@ -73,3 +73,6 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ obj-$(CONFIG_VSOCKETS) += vmw_vsock/ obj-$(CONFIG_NET_MPLS_GSO) += mpls/ obj-$(CONFIG_HSR) += hsr/ +ifneq ($(CONFIG_NET_SWITCHDEV),) +obj-y += switchdev/ +endif
net/switchdev/Kconfig new file +13 -0
@@ -0,0 +1,13 @@ +# +# Configuration for Switch device support +# + +config NET_SWITCHDEV + boolean "Switch (and switch-ish) device support (EXPERIMENTAL)" + depends on INET + ---help--- + This module provides glue between core networking code and device + drivers in order to support hardware switch chips in very generic + meaning of the word "switch". This include devices supporting L2/L3 but + also various flow offloading chips, including switches embedded into + SR-IOV NICs.
net/switchdev/Makefile new file +5 -0
@@ -0,0 +1,5 @@ +# +# Makefile for the Switch device API +# + +obj-$(CONFIG_NET_SWITCHDEV) += switchdev.o
net/switchdev/switchdev.c new file +33 -0
@@ -0,0 +1,33 @@ +/* +* net/switchdev/switchdev.c - Switch device API +* Copyright (c) 2014 Jiri Pirko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <net/switchdev.h> + +/** +* netdev_sw_parent_id_get - Get ID of a switch +* @dev: port device +* @psid: switch ID +* +* Get ID of a switch this port is part of. +*/ +int netdev_sw_parent_id_get (struct net_device *dev, +struct netdev_phys_item_id *psid) +{ +const struct net_device_ops *ops = dev->netdev_ops; + +if (!ops->ndo_sw_parent_id_get) +return -EOPNOTSUPP ; +return ops->ndo_sw_parent_id_get (dev, psid); +} +EXPORT_SYMBOL (netdev_sw_parent_id_get);
Signature · 1 line
1.9.3
Jiri Pirko
maintainerPATCH 3/10 rtnl: expose physical switch id for particular device
In reply to Jiri PirkoThe netdevice represents a port in a switch, it will expose IFLA_PHYS_SWITCH_ID value via rtnl. Two netdevices with the same value belong to one physical switch.
--- include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-)
include/uapi/linux/if_link.h +1 -0
@@ -145,6 +145,7 @@ enum {IFLA_CARRIER ,IFLA_PHYS_PORT_ID ,IFLA_CARRIER_CHANGES , +IFLA_PHYS_SWITCH_ID , __IFLA_MAX };
net/core/rtnetlink.c +25 -1
@@ -43,6 +43,7 @@#include <linux/inet.h> #include <linux/netdevice.h> +#include <net/switchdev.h> #include <net/ip.h> #include <net/protocol.h> #include <net/arp.h>
@@ -868,7 +869,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, +rtnl_port_size (dev, ext_filter_mask)/* IFLA_VF_PORTS + IFLA_PORT_SELF */ +rtnl_link_get_size (dev)/* IFLA_LINKINFO */ +rtnl_link_get_af_size (dev)/* IFLA_AF_SPEC */ - +nla_total_size (MAX_PHYS_ITEM_ID_LEN );/* IFLA_PHYS_PORT_ID */ + +nla_total_size (MAX_PHYS_ITEM_ID_LEN )/* IFLA_PHYS_PORT_ID */ + +nla_total_size (MAX_PHYS_ITEM_ID_LEN );/* IFLA_PHYS_SWITCH_ID */ }static int rtnl_vf_ports_fill (struct sk_buff *skb,struct net_device *dev)
@@ -967,6 +969,24 @@ static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev)return 0 ; } +static int rtnl_phys_switch_id_fill (struct sk_buff *skb,struct net_device *dev) +{ +int err; +struct netdev_phys_item_id psid; + + err =netdev_sw_parent_id_get (dev, &psid); +if (err) { +if (err == -EOPNOTSUPP ) +return 0 ; +return err; + } + +if (nla_put (skb,IFLA_PHYS_SWITCH_ID , psid.id_len, psid.id)) +return -EMSGSIZE ; + +return 0 ; +} +static int rtnl_fill_ifinfo (struct sk_buff *skb,struct net_device *dev,int type,u32 pid,u32 seq,u32 change,unsigned int flags,u32 ext_filter_mask)
@@ -1039,6 +1059,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,if (rtnl_phys_port_id_fill (skb, dev)) goto nla_put_failure; +if (rtnl_phys_switch_id_fill (skb, dev)) + goto nla_put_failure; + attr =nla_reserve (skb,IFLA_STATS ,sizeof (struct rtnl_link_stats ));if (attr ==NULL )
@@ -1198,6 +1221,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */ + [IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, };static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX +1 ] = {
Signature · 1 line
1.9.3
Jiri Pirko
maintainerPATCH 4/10 net-sysfs: expose physical switch id for particular device
In reply to Jiri Pirko--- net/core/net-sysfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
net/core/net-sysfs.c +24 -0
@@ -12,6 +12,7 @@#include <linux/capability.h> #include <linux/kernel.h> #include <linux/netdevice.h> +#include <net/switchdev.h> #include <linux/if_arp.h> #include <linux/slab.h> #include <linux/nsproxy.h>
@@ -399,6 +400,28 @@ static ssize_t phys_port_id_show(struct device *dev, }static DEVICE_ATTR_RO (phys_port_id); +static ssize_t phys_switch_id_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct net_device *netdev =to_net_dev (dev); +ssize_t ret = -EINVAL ; + +if (!rtnl_trylock ()) +return restart_syscall (); + +if (dev_isalive (netdev)) { +struct netdev_phys_item_id ppid; + + ret =netdev_sw_parent_id_get (netdev, &ppid); +if (!ret) + ret =sprintf (buf,"%*phN\n" , ppid.id_len, ppid.id); + } +rtnl_unlock (); + +return ret; +} +static DEVICE_ATTR_RO (phys_switch_id); +static struct attribute *net_class_attrs[] = { &dev_attr_netdev_group.attr, &dev_attr_type.attr,
@@ -423,6 +446,7 @@ static struct attribute *net_class_attrs[] = { &dev_attr_flags.attr, &dev_attr_tx_queue_len.attr, &dev_attr_phys_port_id.attr, + &dev_attr_phys_switch_id.attr,NULL , };ATTRIBUTE_GROUPS (net_class);
Signature · 1 line
1.9.3
Jiri Pirko
maintainerPATCH 5/10 rocker: introduce rocker switch driver
In reply to Jiri PirkoThis patch introduces the first driver to benefit from the switchdev infrastructure and to implement newly introduced switch ndos. This is a driver for emulated switch chip implemented in qemu:
https://github.com/sfeldma/qemu-rocker/
This patch is a result of joint work with Scott Feldman.
--- MAINTAINERS | 7 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/rocker/Kconfig | 27 + drivers/net/ethernet/rocker/Makefile | 5 + drivers/net/ethernet/rocker/rocker.c | 2062 ++++++++++++++++++++++++++++++++++ drivers/net/ethernet/rocker/rocker.h | 427 +++++++ 7 files changed, 2530 insertions(+) create mode 100644 drivers/net/ethernet/rocker/Kconfig create mode 100644 drivers/net/ethernet/rocker/Makefile create mode 100644 drivers/net/ethernet/rocker/rocker.c create mode 100644 drivers/net/ethernet/rocker/rocker.h
MAINTAINERS +7 -0
@@ -7807,6 +7807,13 @@ F: drivers/hid/hid-roccat* F: include/linux/hid-roccat* F: Documentation/ABI/*/sysfs-driver-hid-roccat* +ROCKER DRIVER +M: Jiri Pirko <[email protected]> +M: Scott Feldman <[email protected]> +L: [email protected] +S: Supported +F: drivers/net/ethernet/rocker/ + ROCKETPORT DRIVER P: Comtrol Corp. W: http://www.comtrol.com
drivers/net/ethernet/Kconfig +1 -0
@@ -155,6 +155,7 @@ source "drivers/net/ethernet/qualcomm/Kconfig" source "drivers/net/ethernet/realtek/Kconfig" source "drivers/net/ethernet/renesas/Kconfig" source "drivers/net/ethernet/rdc/Kconfig" +source "drivers/net/ethernet/rocker/Kconfig" config S6GMAC tristate "S6105 GMAC ethernet support"
drivers/net/ethernet/Makefile +1 -0
@@ -65,6 +65,7 @@ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_SH_ETH) += renesas/ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ +obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ obj-$(CONFIG_S6GMAC) += s6gmac.o obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
drivers/net/ethernet/rocker/Kconfig new file +27 -0
@@ -0,0 +1,27 @@ +# +# Rocker device configuration +# + +config NET_VENDOR_ROCKER + bool "Rocker devices" + default y + ---help--- + If you have a network device belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Rocker devices. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_ROCKER + +config ROCKER + tristate "Rocker switch driver (EXPERIMENTAL)" + depends on PCI && NET_SWITCHDEV + ---help--- + This driver supports Rocker switch device. + + To compile this driver as a module, choose M here: the + module will be called rocker. + +endif # NET_VENDOR_ROCKER
drivers/net/ethernet/rocker/Makefile new file +5 -0
@@ -0,0 +1,5 @@ +# +# Makefile for the Rocker network device drivers. +# + +obj-$(CONFIG_ROCKER) += rocker.o
drivers/net/ethernet/rocker/rocker.c new file +2062 -0
2,062 lines not on this page · raw message
drivers/net/ethernet/rocker/rocker.h new file +427 -0
427 lines not on this page · raw message
Signature · 1 line
1.9.3
David Miller
maintainerRe: [patch net-next 05/10] rocker: introduce rocker switch driver
From: Jiri Pirko <[email protected]>
Date: Thu, 6 Nov 2014 10:20:05 +0100
Quoted 1 line
+#include <
generated/utsrelease.h>
The version of this driver is not the version of the kernel, please do not pretend that it is.
Define a proper DRV_MODULE_VERSION and MODULE_VERSION() like other drivers do rather than using UTS_RELEASE or whatever.
Quoted 1 line
+static u32
rocker_msix_vector(struct rocker *rocker, unsigned vector)
Please do not use plain "unsigned", always say "unsigned int".
drivers/net/ethernet/rocker/rocker.c line 1614
Quoted 3 lines
drivers/net/ethernet/rocker/rocker.c new file +3
-0+#ifdef CONFIG_NET_SWITCHDEV + .ndo_sw_parent_id_get = rocker_port_sw_parent_id_get, +#endif
Maybe better to just make the driver depend upon NET_SWITCHDEV?
drivers/net/ethernet/rocker/rocker.h line 97
Quoted 8 lines
drivers/net/ethernet/rocker/rocker.h new file +8
-0+struct rocker_desc { +u64 buf_addr; +u64 cookie; +u16 buf_size; +u16 tlv_size; +u16 resv[5 ]; +u16 comp_err; +} __packed__aligned(8) ;
This __packed seems unnecessary, and __packed should always be avoided when possible because it generates terrible code on some cpus.
drivers/net/ethernet/rocker/rocker.h line 105
Quoted 5 lines
drivers/net/ethernet/rocker/rocker.h new file +5
-0+/* Rocker DMA TLV struct */ +struct rocker_tlv { +u32 type; +u16 len; +} __packed__aligned(8) ;
Likewise.
Jiri Pirko
maintainerPATCH 6/10 bridge: introduce fdb offloading via switchdev
In reply to Jiri PirkoFrom: Scott Feldman <[email protected]>
Add two new ndos: ndo_sw_port_fdb_add/del to offload static bridge fdb entries. Static bridge FDB entries are installed, for example, using iproute2 bridge cmd:
bridge fdb add ADDR dev DEV master vlan VID
This would install ADDR into the bridge's FDB for port DEV on vlan VID. The switch driver implements two ndo_swdev ops to add/delete FDB entries in the switch device:
int ndo_sw_port_fdb_add(struct net_device *dev,
const unsigned char *addr, u16 vid);
int ndo_sw_port_fdb_del(struct net_device *dev,
const unsigned char *addr, u16 vid);
The driver returns 0 on success, negative error code on failure.
Note: the switch driver would not implement ndo_fdb_add/del/dump on a port netdev as these are intended for devices maintaining their own FDB. In our case, we want the Linux bridge to own the FBD.
Note: by default, the bridge does not filter on VLAN and only bridges untagged traffic. To enable VLAN support, turn on VLAN filtering:
echo 1 >/sys/class/net/<bridge>/bridge/vlan_filtering
--- include/linux/netdevice.h | 16 ++++++++++++++++ include/net/switchdev.h | 17 +++++++++++++++++ net/bridge/br_fdb.c | 10 +++++++++- net/switchdev/switchdev.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-)
include/linux/netdevice.h +16 -0
@@ -1023,6 +1023,16 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,* Called to get an ID of the switch chip this port is part of. * If driver implements this, it indicates that it represents a port * of a switch chip. +* +* int (*ndo_sw_port_fdb_add)(struct net_device *dev, +* const unsigned char *addr, +* u16 vid); +* Called to add a fdb to switch device port. +* +* int (*ndo_sw_port_fdb_del)(struct net_device *dev, +* const unsigned char *addr, +* u16 vid); +* Called to delete a fdb from switch device port. */ struct net_device_ops {int (*ndo_init)(struct net_device *dev);
@@ -1177,6 +1187,12 @@ struct net_device_ops {#ifdef CONFIG_NET_SWITCHDEV int (*ndo_sw_parent_id_get)(struct net_device *dev,struct netdev_phys_item_id *psid); +int (*ndo_sw_port_fdb_add)(struct net_device *dev, +const unsigned char *addr, +u16 vid); +int (*ndo_sw_port_fdb_del)(struct net_device *dev, +const unsigned char *addr, +u16 vid);#endif };
include/net/switchdev.h +17 -0
@@ -1,6 +1,7 @@/* * include/net/switchdev.h - Switch device API * Copyright (c) 2014 Jiri Pirko <[email protected]> +* Copyright (c) 2014 Scott Feldman <[email protected]> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by
@@ -16,6 +17,10 @@int netdev_sw_parent_id_get (struct net_device *dev,struct netdev_phys_item_id *psid); +int netdev_sw_port_fdb_add (struct net_device *dev, +const unsigned char *addr,u16 vid); +int netdev_sw_port_fdb_del (struct net_device *dev, +const unsigned char *addr,u16 vid);#else
@@ -25,6 +30,18 @@ static inline int netdev_sw_parent_id_get(struct net_device *dev,return -EOPNOTSUPP ; } +static inline int netdev_sw_port_fdb_add (struct net_device *dev, +const unsigned char *addr,u16 vid) +{ +return -EOPNOTSUPP ; +} + +static inline int netdev_sw_port_fdb_del (struct net_device *dev, +const unsigned char *addr,u16 vid) +{ +return -EOPNOTSUPP ; +} +#endif #endif /* _LINUX_SWITCHDEV_H_ */
net/bridge/br_fdb.c +9 -1
@@ -24,6 +24,7 @@#include <linux/atomic.h> #include <asm/unaligned.h> #include <linux/if_vlan.h> +#include <net/switchdev.h> #include "br_private.h" static struct kmem_cache *br_fdb_cache__read_mostly ;
@@ -132,8 +133,12 @@ static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)static void fdb_delete (struct net_bridge *br,struct net_bridge_fdb_entry *f) { -if (f->is_static) +if (f->is_static) {fdb_del_hw (br, f->addr.addr); +if (f->dst) +netdev_sw_port_fdb_del (f->dst->dev, + f->addr.addr, f->vlan_id); + }hlist_del_rcu (&f->hlist);fdb_notify (br, f,RTM_DELNEIGH );
@@ -755,18 +760,21 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,if (!fdb->is_static) { fdb->is_static =1 ;fdb_add_hw (br, addr); +netdev_sw_port_fdb_add (source->dev, addr, vid); } }else if (state &NUD_NOARP ) { fdb->is_local =0 ;if (!fdb->is_static) { fdb->is_static =1 ;fdb_add_hw (br, addr); +netdev_sw_port_fdb_add (source->dev, addr, vid); } }else { fdb->is_local =0 ;if (fdb->is_static) { fdb->is_static =0 ;fdb_del_hw (br, addr); +netdev_sw_port_fdb_del (source->dev, addr, vid); } }
net/switchdev/switchdev.c +41 -0
50 lines not on this page · raw message
Signature · 1 line
1.9.3
David Miller
maintainerRe: [patch net-next 06/10] bridge: introduce fdb offloading via switchdev
From: Jiri Pirko <[email protected]>
Date: Thu, 6 Nov 2014 10:20:06 +0100
Quoted 4 lines
net/bridge/br_fdb.c +0
-0@@ -132,8 +133,12 @@ static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)static void fdb_delete (struct net_bridge *br,struct net_bridge_fdb_entry *f) {
net/bridge/br_fdb.c static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr) · line 144
Quoted 10 lines
net/bridge/br_fdb.c +5
-1-if (f->is_static) +if (f->is_static) {fdb_del_hw (br, f->addr.addr); +if (f->dst) +netdev_sw_port_fdb_del (f->dst->dev, + f->addr.addr, f->vlan_id); + }hlist_del_rcu (&f->hlist);fdb_notify (br, f,RTM_DELNEIGH );
I think we should now adjust the name of fdb_*_hw() because fdb_del_hw() sounds like a function that would do what netdev_sw_port_fdb_del() actually does, remove the fdb entry from the hardware. But it just removes an address from the device's unicast filter.
Rename them to something like fdb_*_hw_address() of fdb_*_hw_addr().
Thanks.
Jiri Pirko
maintainerPATCH 7/10 bridge: call netdev_sw_port_stp_update when bridge port STP status changes
In reply to Jiri PirkoFrom: Scott Feldman <[email protected]>
To notify switch driver of change in STP state of bridge port, add new .ndo op and provide swdev wrapper func to call ndo op. Use it in bridge code then.
--- include/linux/netdevice.h | 6 ++++++ include/net/switchdev.h | 6 ++++++ net/bridge/br_netlink.c | 2 ++ net/bridge/br_stp.c | 4 ++++ net/bridge/br_stp_if.c | 3 +++ net/bridge/br_stp_timer.c | 2 ++ net/switchdev/switchdev.c | 19 +++++++++++++++++++ 7 files changed, 42 insertions(+)
include/linux/netdevice.h +6 -0
@@ -1033,6 +1033,10 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,* const unsigned char *addr, * u16 vid); * Called to delete a fdb from switch device port. +* +* int (*ndo_sw_port_stp_update)(struct net_device *dev, u8 state); +* Called to notify switch device port of bridge port STP +* state change. */ struct net_device_ops {int (*ndo_init)(struct net_device *dev);
@@ -1193,6 +1197,8 @@ struct net_device_ops {int (*ndo_sw_port_fdb_del)(struct net_device *dev,const unsigned char *addr,u16 vid); +int (*ndo_sw_port_stp_update)(struct net_device *dev, +u8 state);#endif };
include/net/switchdev.h +6 -0
@@ -21,6 +21,7 @@ int netdev_sw_port_fdb_add(struct net_device *dev, const unsigned char *addr, u16 vid);int netdev_sw_port_fdb_del (struct net_device *dev,const unsigned char *addr,u16 vid); +int netdev_sw_port_stp_update (struct net_device *dev,u8 state);#else
@@ -42,6 +43,11 @@ static inline int netdev_sw_port_fdb_del(struct net_device *dev,return -EOPNOTSUPP ; } +static inline int netdev_sw_port_stp_update (struct net_device *dev,u8 state) +{ +return -EOPNOTSUPP ; +} +#endif #endif /* _LINUX_SWITCHDEV_H_ */
net/bridge/br_netlink.c +2 -0
@@ -17,6 +17,7 @@#include <net/net_namespace.h> #include <net/sock.h> #include <uapi/linux/if_bridge.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h"
@@ -304,6 +305,7 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)br_set_state (p, state);br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_port_state_selection (p->br);return 0 ; }
net/bridge/br_stp.c +4 -0
@@ -12,6 +12,7 @@*/ #include <linux/kernel.h> #include <linux/rculist.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h"
@@ -114,6 +115,7 @@ static void br_root_port_block(const struct net_bridge *br,br_set_state (p,BR_STATE_LISTENING );br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_ifinfo_notify (RTM_NEWLINK , p);if (br->forward_delay >0 )
@@ -394,6 +396,7 @@ static void br_make_blocking(struct net_bridge_port *p)br_set_state (p,BR_STATE_BLOCKING );br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_ifinfo_notify (RTM_NEWLINK , p);del_timer (&p->forward_delay_timer);
@@ -419,6 +422,7 @@ static void br_make_forwarding(struct net_bridge_port *p)br_multicast_enable_port (p);br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_ifinfo_notify (RTM_NEWLINK , p);if (br->forward_delay !=0 )
net/bridge/br_stp_if.c +3 -0
@@ -15,6 +15,7 @@#include <linux/kmod.h> #include <linux/etherdevice.h> #include <linux/rtnetlink.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h"
@@ -89,6 +90,7 @@ void br_stp_enable_port(struct net_bridge_port *p)br_init_port (p);br_port_state_selection (p->br);br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_ifinfo_notify (RTM_NEWLINK , p); }
@@ -105,6 +107,7 @@ void br_stp_disable_port(struct net_bridge_port *p) p->config_pending =0 ;br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_ifinfo_notify (RTM_NEWLINK , p);del_timer (&p->message_age_timer);
net/bridge/br_stp_timer.c +2 -0
@@ -13,6 +13,7 @@#include <linux/kernel.h> #include <linux/times.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h"
@@ -97,6 +98,7 @@ static void br_forward_delay_timer_expired(unsigned long arg)netif_carrier_on (br->dev); }br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);br_ifinfo_notify (RTM_NEWLINK , p);spin_unlock (&br->lock); }
net/switchdev/switchdev.c +19 -0
@@ -72,3 +72,22 @@ int netdev_sw_port_fdb_del(struct net_device *dev,return ops->ndo_sw_port_fdb_del (dev, addr, vid); }EXPORT_SYMBOL (netdev_sw_port_fdb_del); + +/** +* netdev_sw_port_stp_update - Notify switch device port of STP +* state change +* @dev: port device +* @state: port STP state +* +* Notify switch device port of bridge port STP state change. +*/ +int netdev_sw_port_stp_update (struct net_device *dev,u8 state) +{ +const struct net_device_ops *ops = dev->netdev_ops; + +if (!ops->ndo_sw_port_stp_update) +return -EOPNOTSUPP ; +WARN_ON (!ops->ndo_sw_parent_id_get); +return ops->ndo_sw_port_stp_update (dev, state); +} +EXPORT_SYMBOL (netdev_sw_port_stp_update);
Signature · 1 line
1.9.3
Re: [patch net-next 07/10] bridge: call netdev_sw_port_stp_update when bridge port STP status changes
Quoted 9 lines · Jiri Pirko
From: Scott Feldman <[email protected]>
To notify switch driver of change in STP state of bridge port, add new .ndo op and provide swdev wrapper func to call ndo op. Use it in bridge code then.
Signed-off-by Scott Feldman <[email protected]>Signed-off-by Jiri Pirko <[email protected]>---
[snip]
Quoted 14 lines
#endif /* _LINUX_SWITCHDEV_H_ */
net/bridge/br_netlink.c +1
-0@@ -17,6 +17,7 @@#include <net/net_namespace.h> #include <net/sock.h> #include <uapi/linux/if_bridge.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h"
net/bridge/br_netlink.c static int br_set_port_state(struct net_bridge_port *p, u8 state) · line 308
Quoted 4 lines
net/bridge/br_netlink.c +1
-0@@ -304,6 +305,7 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)br_set_state (p, state);br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);
Is there a reason netdev_sw_port_stp_update() is not folded in br_set_state()? Are we missing calls to br_set_state() in some locations? --
Florian
Re: [patch net-next 07/10] bridge: call netdev_sw_port_stp_update when bridge port STP status changes
Quoted 28 lines · Florian Fainelli
Quoted 9 lines · Jiri Pirko
From: Scott Feldman <[email protected]>
To notify switch driver of change in STP state of bridge port, add new .ndo op and provide swdev wrapper func to call ndo op. Use it in bridge code then.
Signed-off-by Scott Feldman <[email protected]>Signed-off-by Jiri Pirko <[email protected]>---
[snip]
Quoted 15 lines
#endif /* _LINUX_SWITCHDEV_H_ */
net/bridge/br_netlink.c +1
-0@@ -17,6 +17,7 @@#include <net/net_namespace.h> #include <net/sock.h> #include <uapi/linux/if_bridge.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h" @@ -304,6 +305,7 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
Quoted 6 lines
net/bridge/br_netlink.c static int br_set_port_state(struct net_bridge_port *p, u8 state) · line 308
Quoted 3 lines
net/bridge/br_netlink.c +1
-0br_set_state (p, state);br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);Is there a reason
netdev_sw_port_stp_update()is not folded inbr_set_state()? Are we missing calls tobr_set_state()in some locations?
I put the netdev_sw call at the same level as br_log_state() and br_ifinfo_notify(), but now that you bring up the question, I agree it would be cleaner/safer if netdev_sw call was from br_set_state().
Quoted 3 lines
--
Florian
A message that is not in the archive (sent off-list or not yet received): CAE4R7bBarGQPsuM5js20RwPLOBF24AtiiHM-f2EvkHNObp7NDQ@mail.gmail.com
Jiri Pirko
maintainerRe: [patch net-next 07/10] bridge: call netdev_sw_port_stp_update when bridge port STP status changes
Quoted 36 lines · Thu, Nov 06, 2014 at 07:53:35PM CET, [email protected] wrote:
Quoted 33 lines · Florian Fainelli
Quoted 9 lines · Jiri Pirko
From: Scott Feldman <[email protected]>
To notify switch driver of change in STP state of bridge port, add new .ndo op and provide swdev wrapper func to call ndo op. Use it in bridge code then.
Signed-off-by Scott Feldman <[email protected]>Signed-off-by Jiri Pirko <[email protected]>---
[snip]
net/bridge/br_netlink.c static int br_set_port_state(struct net_bridge_port
Quoted 14 lines
#endif /* _LINUX_SWITCHDEV_H_ */
net/bridge/br_netlink.c +1
-0@@ -17,6 +17,7 @@#include <net/net_namespace.h> #include <net/sock.h> #include <uapi/linux/if_bridge.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h" @@ -304,6 +305,7 @@ static int br_set_port_state(struct net_bridge_port*p, u8 state)
Quoted 4 lines
net/bridge/br_netlink.c +1
-0br_set_state (p, state);br_log_state (p); +netdev_sw_port_stp_update (p->dev, p->state);
Quoted 8 lines
Quoted 3 lines
Is there a reason
netdev_sw_port_stp_update()is not folded inbr_set_state()? Are we missing calls tobr_set_state()in some locations?I put the
netdev_swcall at the same level asbr_log_state()andbr_ifinfo_notify(),
but now that you bring up the question, I agree it would be cleaner/safer ifnetdev_swcall was frombr_set_state().
Not sure about this. netdev_sw_port_stp_update is not called every time br_set_state is called. br_log_state is in simillar position. br_set_state is now just a simple setter. I would probably leave this change for the possible future follow-up. It can move br_log_state call as well.
Quoted 5 lines
Quoted 5 lines
--
Florian
Jiri Pirko
maintainerPATCH 8/10 bridge: add API to notify bridge driver of learned FBD on offloaded device
In reply to Jiri PirkoFrom: Scott Feldman <[email protected]>
When the swdev device learns a new mac/vlan on a port, it sends some async notification to the driver and the driver installs an FDB in the device. To give a holistic system view, the learned mac/vlan should be reflected in the bridge's FBD table, so the user, using normal iproute2 cmds, can view what is currently learned by the device. This API on the bridge driver gives a way for the swdev driver to install an FBD entry in the bridge FBD table. (And remove one).
This is equivalent to the device running these cmds:
bridge fdb [add|del] <mac> dev <dev> vid <vlan id> master
This patch needs some extra eyeballs for review, in paricular around the locking and contexts.
--- include/linux/if_bridge.h | 18 ++++++++++ net/bridge/br_fdb.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+)
include/linux/if_bridge.h +18 -0
@@ -37,6 +37,24 @@ extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __usetypedef int br_should_route_hook_t (struct sk_buff *skb);extern br_should_route_hook_t __rcu *br_should_route_hook; +#if IS_ENABLED (CONFIG_BRIDGE ) +int br_fdb_learn_add (struct net_device *dev, +const unsigned char *addr,u16 vid); +int br_fdb_learn_del (struct net_device *dev, +const unsigned char *addr,u16 vid); +#else +static inline int br_fdb_learn_add (struct net_device *dev, +const unsigned char *addr,u16 vid) +{ +return 0 ; +} +static inline int br_fdb_learn_del (struct net_device *dev, +const unsigned char *addr,u16 vid) +{ +return 0 ; +} +#endif +#if IS_ENABLED (CONFIG_BRIDGE ) &&IS_ENABLED (CONFIG_BRIDGE_IGMP_SNOOPING )int br_multicast_list_adjacent (struct net_device *dev,struct list_head *br_ip_list);
net/bridge/br_fdb.c +84 -0
87 lines not on this page · raw message
Signature · 1 line
1.9.3
Jiri Pirko
maintainerPATCH 9/10 rocker: implement rocker ofdpa flow table manipulation
In reply to Jiri PirkoFrom: Scott Feldman <[email protected]>
The rocker driver maintains 4 hash tables: flows, groups, FDB, and VLANs.
Flow and group tables track the entries installed to OF-DPA tables, per the OF-DPA spec. See OF-DPA spec for full description of fields in each flow and group table. New table entries are pushed to the device with ADD cmd. Updated entries are pushed to the device with MOD cmd. For flow table entries, a crc32 key is made from fields of the particular field. For group table entries, the group_id is used as the key.
The FDB table tracks fdb entries learned by the device or manually pushed to the bridge by the user. A crc32 key is made from the port/mac/vlan tuple for the fdb entry.
The VLAN table tracks the ifindex-to-internal-vlan mapping for untagged pkts. On ingress, an untagged pkt is inserted with an internal VLAN ID based on the input port's current internal VLAN ID. The input port's internal VLAN will either be referenced by the port's ifindex, if not bridged, or the containing bridge's ifindex, if bridged. Since the ifindex space isn't within a fixed range, uses a hash table (with ifindex as key) to track internal VLAN ID for a given ifindex. The internal VLAN ID range is fixed and currently uses the upper 255 VLAN IDs, starting at 0xf00.
--- drivers/net/ethernet/rocker/rocker.c | 1464 +++++++++++++++++++++++++++++++++- 1 file changed, 1462 insertions(+), 2 deletions(-)
drivers/net/ethernet/rocker/rocker.c +1462 -2
@@ -16,6 +16,7 @@#include <linux/sched.h> #include <linux/wait.h> #include <linux/spinlock.h> +#include <linux/hashtable.h> #include <linux/crc32.h> #include <linux/sort.h> #include <linux/random.h>
@@ -41,6 +42,123 @@ static const struct pci_device_id rocker_pci_id_table[] = { {0, } }; +struct rocker_flow_tbl_key { +u32 priority; +enum rocker_of_dpa_table_id tbl_id; +union { +struct { +u32 in_lport; +u32 in_lport_mask; +enum rocker_of_dpa_table_id goto_tbl; + } ig_port; +struct { +u32 in_lport; +__be16 vlan_id; +__be16 vlan_id_mask; +enum rocker_of_dpa_table_id goto_tbl; +bool untagged; +__be16 new_vlan_id; + } vlan; +struct { +u32 in_lport; +u32 in_lport_mask; +__be16 eth_type; +u8 eth_dst[ETH_ALEN ]; +u8 eth_dst_mask[ETH_ALEN ]; +__be16 vlan_id; +__be16 vlan_id_mask; +enum rocker_of_dpa_table_id goto_tbl; +bool copy_to_cpu; + } term_mac; +struct { +__be16 eth_type; +__be32 dst4; +__be32 dst4_mask; +enum rocker_of_dpa_table_id goto_tbl; +u32 group_id; + } ucast_routing; +struct { +u8 eth_dst[ETH_ALEN ]; +u8 eth_dst_mask[ETH_ALEN ]; +int has_eth_dst; +int has_eth_dst_mask; +__be16 vlan_id; +u32 tunnel_id; +enum rocker_of_dpa_table_id goto_tbl; +u32 group_id; +bool copy_to_cpu; + } bridge; +struct { +u32 in_lport; +u32 in_lport_mask; +u8 eth_src[ETH_ALEN ]; +u8 eth_src_mask[ETH_ALEN ]; +u8 eth_dst[ETH_ALEN ]; +u8 eth_dst_mask[ETH_ALEN ]; +__be16 eth_type; +__be16 vlan_id; +__be16 vlan_id_mask; +u8 ip_proto; +u8 ip_proto_mask; +u8 ip_tos; +u8 ip_tos_mask; +u32 group_id; + } acl; + }; +}; + +struct rocker_flow_tbl_entry { +struct hlist_node entry; +u32 ref_count; +u64 cookie; +struct rocker_flow_tbl_key key; +u32 key_crc32;/* key */ +}; + +struct rocker_group_tbl_entry { +struct hlist_node entry; +u32 cmd; +u32 group_id;/* key */ +u16 group_count; +u32 *group_ids; +union { +struct { +u8 pop_vlan; + } l2_interface; +struct { +u8 eth_src[ETH_ALEN ]; +u8 eth_dst[ETH_ALEN ]; +__be16 vlan_id; +u32 group_id; + } l2_rewrite; +struct { +u8 eth_src[ETH_ALEN ]; +u8 eth_dst[ETH_ALEN ]; +__be16 vlan_id; +bool ttl_check; +u32 group_id; + } l3_unicast; + }; +}; + +struct rocker_fdb_tbl_entry { +struct hlist_node entry; +u32 key_crc32;/* key */ +bool learned; +struct rocker_fdb_tbl_key { +u32 lport; +u8 addr[ETH_ALEN ]; +__be16 vlan_id; + } key; +}; + +struct rocker_internal_vlan_tbl_entry { +struct hlist_node entry; +int ifindex;/* key */ +u32 ref_count; +__be16 vlan_id; +}; +struct rocker_desc_info {char *data;/* mapped */ size_t data_size;
@@ -61,11 +179,28 @@ struct rocker_dma_ring_info {struct rocker ; +enum { +ROCKER_CTRL_LINK_LOCAL_MCAST , +ROCKER_CTRL_LOCAL_ARP , +ROCKER_CTRL_IPV4_MCAST , +ROCKER_CTRL_IPV6_MCAST , +ROCKER_CTRL_DFLT_BRIDGING , +ROCKER_CTRL_MAX , +}; + +#define ROCKER_INTERNAL_VLAN_ID_BASE 0x0f00 +#define ROCKER_N_INTERNAL_VLANS 255 +#define ROCKER_VLAN_BITMAP_LEN BITS_TO_LONGS(VLAN_N_VID) +#define ROCKER_INTERNAL_VLAN_BITMAP_LEN BITS_TO_LONGS(ROCKER_N_INTERNAL_VLANS) +struct rocker_port {struct net_device *dev;struct rocker *rocker;unsigned port_number;u32 lport; +__be16 internal_vlan_id; +bool ctrls[ROCKER_CTRL_MAX ]; +unsigned long vlan_bitmap[ROCKER_VLAN_BITMAP_LEN ];struct napi_struct napi_tx;struct napi_struct napi_rx;struct rocker_dma_ring_info tx_ring;
@@ -84,8 +219,76 @@ struct rocker {spinlock_t cmd_ring_lock;struct rocker_dma_ring_info cmd_ring;struct rocker_dma_ring_info event_ring; +DECLARE_HASHTABLE (flow_tbl,16 ); +spinlock_t flow_tbl_lock; +u64 flow_tbl_next_cookie; +DECLARE_HASHTABLE (group_tbl,16 ); +spinlock_t group_tbl_lock; +DECLARE_HASHTABLE (fdb_tbl,16 ); +spinlock_t fdb_tbl_lock; +unsigned long internal_vlan_bitmap[ROCKER_INTERNAL_VLAN_BITMAP_LEN ]; +DECLARE_HASHTABLE (internal_vlan_tbl,8 ); +spinlock_t internal_vlan_tbl_lock; }; +static const u8 zero_mac[ETH_ALEN ] = {0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 }; +static const u8 ff_mac[ETH_ALEN ] = {0xff ,0xff ,0xff ,0xff ,0xff ,0xff }; +static const u8 ll_mac[ETH_ALEN ] = {0x01 ,0x80 ,0xc2 ,0x00 ,0x00 ,0x00 }; +static const u8 ll_mask[ETH_ALEN ] = {0xff ,0xff ,0xff ,0xff ,0xff ,0xf0 }; +static const u8 mcast_mac[ETH_ALEN ] = {0x01 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 }; +static const u8 ipv4_mcast[ETH_ALEN ] = {0x01 ,0x00 ,0x5e ,0x00 ,0x00 ,0x00 }; +static const u8 ipv4_mask[ETH_ALEN ] = {0xff ,0xff ,0xff ,0x80 ,0x00 ,0x00 }; +static const u8 ipv6_mcast[ETH_ALEN ] = {0x33 ,0x33 ,0x00 ,0x00 ,0x00 ,0x00 }; +static const u8 ipv6_mask[ETH_ALEN ] = {0xff ,0xff ,0x00 ,0x00 ,0x00 ,0x00 }; + +/* Rocker priority levels for flow table entries. Higher +* priority match takes precedence over lower priority match. +*/ + +enum { +ROCKER_PRIORITY_UNKNOWN =0 , +ROCKER_PRIORITY_IG_PORT =1 , +ROCKER_PRIORITY_VLAN =1 , +ROCKER_PRIORITY_TERM_MAC_UCAST =0 , +ROCKER_PRIORITY_TERM_MAC_MCAST =1 , +ROCKER_PRIORITY_UNICAST_ROUTING =1 , +ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_EXACT =1 , +ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_WILD =2 , +ROCKER_PRIORITY_BRIDGING_VLAN =3 , +ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_EXACT =1 , +ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_WILD =2 , +ROCKER_PRIORITY_BRIDGING_TENANT =3 , +ROCKER_PRIORITY_ACL_CTRL =3 , +ROCKER_PRIORITY_ACL_NORMAL =2 , +ROCKER_PRIORITY_ACL_DFLT =1 , +}; + +static bool rocker_vlan_id_is_internal (__be16 vlan_id) +{ +u16 start =ROCKER_INTERNAL_VLAN_ID_BASE ; +u16 end =0xffe ; +u16 _vlan_id =ntohs (vlan_id); + +return (_vlan_id >= start && _vlan_id <= end); +} + +static __be16 rocker_port_vid_to_vlan (struct rocker_port *rocker_port, +u16 vid,bool *pop_vlan) +{ +__be16 vlan_id; + +if (pop_vlan) + *pop_vlan = false; + vlan_id =htons (vid); +if (!vlan_id) { + vlan_id = rocker_port->internal_vlan_id; +if (pop_vlan) + *pop_vlan = true; + } + +return vlan_id; +} +struct rocker_wait {wait_queue_head_t wait;bool done;
@@ -1094,6 +1297,10 @@ static int rocker_event_link_change(struct rocker *rocker,return 0 ; } +#define ROCKER_OP_FLAG_REMOVE (1 << 0) +#define ROCKER_OP_FLAG_NOWAIT (1 << 1) +#define ROCKER_OP_FLAG_LEARNED (1 << 2) +static int rocker_event_process (struct rocker *rocker,struct rocker_desc_info *desc_info) {
@@ -1399,6 +1606,1236 @@ static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, macaddr, NULL, NULL, false); } +static int rocker_cmd_flow_tbl_add_ig_port (struct rocker_desc_info *desc_info, +struct rocker_flow_tbl_entry *entry) +{ +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT , + entry->key.ig_port.in_lport)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT_MASK , + entry->key.ig_port.in_lport_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_GOTO_TABLE_ID , + entry->key.ig_port.goto_tbl)) +return -EMSGSIZE ; + +return 0 ; +} + +static int rocker_cmd_flow_tbl_add_vlan (struct rocker_desc_info *desc_info, +struct rocker_flow_tbl_entry *entry) +{ +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT , + entry->key.vlan.in_lport)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID , + entry->key.vlan.vlan_id)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID_MASK , + entry->key.vlan.vlan_id_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_GOTO_TABLE_ID , + entry->key.vlan.goto_tbl)) +return -EMSGSIZE ; +if (entry->key.vlan.untagged && +rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_NEW_VLAN_ID , + entry->key.vlan.new_vlan_id)) +return -EMSGSIZE ; + +return 0 ; +} + +static int rocker_cmd_flow_tbl_add_term_mac (struct rocker_desc_info *desc_info, +struct rocker_flow_tbl_entry *entry) +{ +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT , + entry->key.term_mac.in_lport)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT_MASK , + entry->key.term_mac.in_lport_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_ETHERTYPE , + entry->key.term_mac.eth_type)) +return -EMSGSIZE ; +if (rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC , +ETH_ALEN , entry->key.term_mac.eth_dst)) +return -EMSGSIZE ; +if (rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC_MASK , +ETH_ALEN , entry->key.term_mac.eth_dst_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID , + entry->key.term_mac.vlan_id)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID_MASK , + entry->key.term_mac.vlan_id_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_GOTO_TABLE_ID , + entry->key.term_mac.goto_tbl)) +return -EMSGSIZE ; +if (entry->key.term_mac.copy_to_cpu && +rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_COPY_CPU_ACTION , + entry->key.term_mac.copy_to_cpu)) +return -EMSGSIZE ; + +return 0 ; +} + +static int +rocker_cmd_flow_tbl_add_ucast_routing (struct rocker_desc_info *desc_info, +struct rocker_flow_tbl_entry *entry) +{ +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_ETHERTYPE , + entry->key.ucast_routing.eth_type)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_DST_IP , + entry->key.ucast_routing.dst4)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_DST_IP_MASK , + entry->key.ucast_routing.dst4_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_GOTO_TABLE_ID , + entry->key.ucast_routing.goto_tbl)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID , + entry->key.ucast_routing.group_id)) +return -EMSGSIZE ; + +return 0 ; +} + +static int rocker_cmd_flow_tbl_add_bridge (struct rocker_desc_info *desc_info, +struct rocker_flow_tbl_entry *entry) +{ +if (entry->key.bridge.has_eth_dst && +rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC , +ETH_ALEN , entry->key.bridge.eth_dst)) +return -EMSGSIZE ; +if (entry->key.bridge.has_eth_dst_mask && +rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC_MASK , +ETH_ALEN , entry->key.bridge.eth_dst_mask)) +return -EMSGSIZE ; +if (entry->key.bridge.vlan_id && +rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID , + entry->key.bridge.vlan_id)) +return -EMSGSIZE ; +if (entry->key.bridge.tunnel_id && +rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_TUNNEL_ID , + entry->key.bridge.tunnel_id)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_GOTO_TABLE_ID , + entry->key.bridge.goto_tbl)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID , + entry->key.bridge.group_id)) +return -EMSGSIZE ; +if (entry->key.bridge.copy_to_cpu && +rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_COPY_CPU_ACTION , + entry->key.bridge.copy_to_cpu)) +return -EMSGSIZE ; + +return 0 ; +} + +static int rocker_cmd_flow_tbl_add_acl (struct rocker_desc_info *desc_info, +struct rocker_flow_tbl_entry *entry) +{ +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT , + entry->key.acl.in_lport)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_IN_LPORT_MASK , + entry->key.acl.in_lport_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_SRC_MAC , +ETH_ALEN , entry->key.acl.eth_src)) +return -EMSGSIZE ; +if (rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_SRC_MAC_MASK , +ETH_ALEN , entry->key.acl.eth_src_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC , +ETH_ALEN , entry->key.acl.eth_dst)) +return -EMSGSIZE ; +if (rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC_MASK , +ETH_ALEN , entry->key.acl.eth_dst_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_ETHERTYPE , + entry->key.acl.eth_type)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID , + entry->key.acl.vlan_id)) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID_MASK , + entry->key.acl.vlan_id_mask)) +return -EMSGSIZE ; + +switch (ntohs (entry->key.acl.eth_type)) { +case ETH_P_IP : +case ETH_P_IPV6 : +if (rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_IP_PROTO , + entry->key.acl.ip_proto)) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info, +ROCKER_TLV_OF_DPA_IP_PROTO_MASK , + entry->key.acl.ip_proto_mask)) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_IP_DSCP , + entry->key.acl.ip_tos &0x3f )) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info, +ROCKER_TLV_OF_DPA_IP_DSCP_MASK , + entry->key.acl.ip_tos_mask &0x3f )) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_IP_ECN , + (entry->key.acl.ip_tos &0xc0 ) >>6 )) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info, +ROCKER_TLV_OF_DPA_IP_ECN_MASK , + (entry->key.acl.ip_tos_mask &0xc0 ) >>6 )) +return -EMSGSIZE ; +break ; + } + +if (entry->key.acl.group_id !=ROCKER_GROUP_NONE && +rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID , + entry->key.acl.group_id)) +return -EMSGSIZE ; + +return 0 ; +} + +static int rocker_cmd_flow_tbl_add (struct rocker *rocker, +struct rocker_port *rocker_port, +struct rocker_desc_info *desc_info, +void *priv) +{ +struct rocker_flow_tbl_entry *entry = priv; +struct rocker_tlv *cmd_info; +int err =0 ; + +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_CMD_TYPE , +ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD )) +return -EMSGSIZE ; + cmd_info =rocker_tlv_nest_start (desc_info,ROCKER_TLV_CMD_INFO ); +if (!cmd_info) +return -EMSGSIZE ; +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_TABLE_ID , + entry->key.tbl_id)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_PRIORITY , + entry->key.priority)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_HARDTIME ,0 )) +return -EMSGSIZE ; +if (rocker_tlv_put_u64 (desc_info,ROCKER_TLV_OF_DPA_COOKIE , + entry->cookie)) +return -EMSGSIZE ; + +switch (entry->key.tbl_id) { +case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT : + err =rocker_cmd_flow_tbl_add_ig_port (desc_info, entry); +break ; +case ROCKER_OF_DPA_TABLE_ID_VLAN : + err =rocker_cmd_flow_tbl_add_vlan (desc_info, entry); +break ; +case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC : + err =rocker_cmd_flow_tbl_add_term_mac (desc_info, entry); +break ; +case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING : + err =rocker_cmd_flow_tbl_add_ucast_routing (desc_info, entry); +break ; +case ROCKER_OF_DPA_TABLE_ID_BRIDGING : + err =rocker_cmd_flow_tbl_add_bridge (desc_info, entry); +break ; +case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY : + err =rocker_cmd_flow_tbl_add_acl (desc_info, entry); +break ; +default : + err = -ENOTSUPP ; +break ; + } + +if (err) +return err; + +rocker_tlv_nest_end (desc_info, cmd_info); + +return 0 ; +} + +static int rocker_cmd_flow_tbl_del (struct rocker *rocker, +struct rocker_port *rocker_port, +struct rocker_desc_info *desc_info, +void *priv) +{ +const struct rocker_flow_tbl_entry *entry = priv; +struct rocker_tlv *cmd_info; + +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_CMD_TYPE , +ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL )) +return -EMSGSIZE ; + cmd_info =rocker_tlv_nest_start (desc_info,ROCKER_TLV_CMD_INFO ); +if (!cmd_info) +return -EMSGSIZE ; +if (rocker_tlv_put_u64 (desc_info,ROCKER_TLV_OF_DPA_COOKIE , + entry->cookie)) +return -EMSGSIZE ; +rocker_tlv_nest_end (desc_info, cmd_info); + +return 0 ; +} + +static int +rocker_cmd_group_tbl_add_l2_interface (struct rocker_desc_info *desc_info, +struct rocker_group_tbl_entry *entry) +{ +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_OUT_LPORT , +ROCKER_GROUP_PORT_GET (entry->group_id))) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_POP_VLAN , + entry->l2_interface.pop_vlan)) +return -EMSGSIZE ; + +return 0 ; +} + +static int +rocker_cmd_group_tbl_add_l2_rewrite (struct rocker_desc_info *desc_info, +struct rocker_group_tbl_entry *entry) +{ +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID_LOWER , + entry->l2_rewrite.group_id)) +return -EMSGSIZE ; +if (!is_zero_ether_addr (entry->l2_rewrite.eth_src) && +rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_SRC_MAC , +ETH_ALEN , entry->l2_rewrite.eth_src)) +return -EMSGSIZE ; +if (!is_zero_ether_addr (entry->l2_rewrite.eth_dst) && +rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC , +ETH_ALEN , entry->l2_rewrite.eth_dst)) +return -EMSGSIZE ; +if (entry->l2_rewrite.vlan_id && +rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID , + entry->l2_rewrite.vlan_id)) +return -EMSGSIZE ; + +return 0 ; +} + +static int +rocker_cmd_group_tbl_add_group_ids (struct rocker_desc_info *desc_info, +struct rocker_group_tbl_entry *entry) +{ +int i; +struct rocker_tlv *group_ids; + +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_GROUP_COUNT , + entry->group_count)) +return -EMSGSIZE ; + + group_ids =rocker_tlv_nest_start (desc_info, +ROCKER_TLV_OF_DPA_GROUP_IDS ); +if (!group_ids) +return -EMSGSIZE ; + +for (i =0 ; i < entry->group_count; i++) +/* Note TLV array is 1-based */ +if (rocker_tlv_put_u32 (desc_info, i +1 , entry->group_ids[i])) +return -EMSGSIZE ; + +rocker_tlv_nest_end (desc_info, group_ids); + +return 0 ; +} + +static int +rocker_cmd_group_tbl_add_l3_unicast (struct rocker_desc_info *desc_info, +struct rocker_group_tbl_entry *entry) +{ +if (!is_zero_ether_addr (entry->l3_unicast.eth_src) && +rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_SRC_MAC , +ETH_ALEN , entry->l3_unicast.eth_src)) +return -EMSGSIZE ; +if (!is_zero_ether_addr (entry->l3_unicast.eth_dst) && +rocker_tlv_put (desc_info,ROCKER_TLV_OF_DPA_DST_MAC , +ETH_ALEN , entry->l3_unicast.eth_dst)) +return -EMSGSIZE ; +if (entry->l3_unicast.vlan_id && +rocker_tlv_put_u16 (desc_info,ROCKER_TLV_OF_DPA_VLAN_ID , + entry->l3_unicast.vlan_id)) +return -EMSGSIZE ; +if (rocker_tlv_put_u8 (desc_info,ROCKER_TLV_OF_DPA_TTL_CHECK , + entry->l3_unicast.ttl_check)) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID_LOWER , + entry->l3_unicast.group_id)) +return -EMSGSIZE ; + +return 0 ; +} + +static int rocker_cmd_group_tbl_add (struct rocker *rocker, +struct rocker_port *rocker_port, +struct rocker_desc_info *desc_info, +void *priv) +{ +struct rocker_group_tbl_entry *entry = priv; +struct rocker_tlv *cmd_info; +int err =0 ; + +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_CMD_TYPE , entry->cmd)) +return -EMSGSIZE ; + cmd_info =rocker_tlv_nest_start (desc_info,ROCKER_TLV_CMD_INFO ); +if (!cmd_info) +return -EMSGSIZE ; + +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID , + entry->group_id)) +return -EMSGSIZE ; + +switch (ROCKER_GROUP_TYPE_GET (entry->group_id)) { +case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE : + err =rocker_cmd_group_tbl_add_l2_interface (desc_info, entry); +break ; +case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE : + err =rocker_cmd_group_tbl_add_l2_rewrite (desc_info, entry); +break ; +case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD : +case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST : + err =rocker_cmd_group_tbl_add_group_ids (desc_info, entry); +break ; +case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST : + err =rocker_cmd_group_tbl_add_l3_unicast (desc_info, entry); +break ; +default : + err = -ENOTSUPP ; +break ; + } + +if (err) +return err; + +rocker_tlv_nest_end (desc_info, cmd_info); + +return 0 ; +} + +static int rocker_cmd_group_tbl_del (struct rocker *rocker, +struct rocker_port *rocker_port, +struct rocker_desc_info *desc_info, +void *priv) +{ +const struct rocker_group_tbl_entry *entry = priv; +struct rocker_tlv *cmd_info; + +if (rocker_tlv_put_u16 (desc_info,ROCKER_TLV_CMD_TYPE , entry->cmd)) +return -EMSGSIZE ; + cmd_info =rocker_tlv_nest_start (desc_info,ROCKER_TLV_CMD_INFO ); +if (!cmd_info) +return -EMSGSIZE ; +if (rocker_tlv_put_u32 (desc_info,ROCKER_TLV_OF_DPA_GROUP_ID , + entry->group_id)) +return -EMSGSIZE ; +rocker_tlv_nest_end (desc_info, cmd_info); + +return 0 ; +} + +/***************************************** +* Flow, group, FDB, internal VLAN tables +*****************************************/ + +static int rocker_init_tbls (struct rocker *rocker) +{ +hash_init (rocker->flow_tbl); +spin_lock_init (&rocker->flow_tbl_lock); + +hash_init (rocker->group_tbl); +spin_lock_init (&rocker->group_tbl_lock); + +hash_init (rocker->fdb_tbl); +spin_lock_init (&rocker->fdb_tbl_lock); + +hash_init (rocker->internal_vlan_tbl); +spin_lock_init (&rocker->internal_vlan_tbl_lock); + +return 0 ; +} + +static void rocker_free_tbls (struct rocker *rocker) +{ +unsigned long flags; +struct rocker_flow_tbl_entry *flow_entry; +struct rocker_group_tbl_entry *group_entry; +struct rocker_fdb_tbl_entry *fdb_entry; +struct rocker_internal_vlan_tbl_entry *internal_vlan_entry; +struct hlist_node *tmp; +int bkt; + +spin_lock_irqsave (&rocker->flow_tbl_lock, flags); +hash_for_each_safe (rocker->flow_tbl, bkt, tmp, flow_entry, entry) +hash_del (&flow_entry->entry); +spin_unlock_irqrestore (&rocker->flow_tbl_lock, flags); + +spin_lock_irqsave (&rocker->group_tbl_lock, flags); +hash_for_each_safe (rocker->group_tbl, bkt, tmp, group_entry, entry) +hash_del (&group_entry->entry); +spin_unlock_irqrestore (&rocker->group_tbl_lock, flags); + +spin_lock_irqsave (&rocker->fdb_tbl_lock, flags); +hash_for_each_safe (rocker->fdb_tbl, bkt, tmp, fdb_entry, entry) +hash_del (&fdb_entry->entry); +spin_unlock_irqrestore (&rocker->fdb_tbl_lock, flags); + +spin_lock_irqsave (&rocker->internal_vlan_tbl_lock, flags); +hash_for_each_safe (rocker->internal_vlan_tbl, bkt, + tmp, internal_vlan_entry, entry) +hash_del (&internal_vlan_entry->entry); +spin_unlock_irqrestore (&rocker->internal_vlan_tbl_lock, flags); +} + +static struct rocker_flow_tbl_entry * +rocker_flow_tbl_find (struct rocker *rocker,struct rocker_flow_tbl_entry *match) +{ +struct rocker_flow_tbl_entry *found; + +hash_for_each_possible (rocker->flow_tbl, found, entry, match->key_crc32) +if (memcmp (&found->key, &match->key,sizeof (found->key)) ==0 ) +return found; + +return NULL ; +} + +static int rocker_flow_tbl_add (struct rocker_port *rocker_port, +struct rocker_flow_tbl_entry *match, +bool nowait) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_flow_tbl_entry *found; +unsigned long flags; +bool add_to_hw = false; +int err =0 ; + + match->key_crc32 =crc32 (~0 , &match->key,sizeof (match->key)); + +spin_lock_irqsave (&rocker->flow_tbl_lock, flags); + + found =rocker_flow_tbl_find (rocker, match); + +if (found) { +kfree (match); + }else { + found = match; + found->cookie = rocker->flow_tbl_next_cookie++; +hash_add (rocker->flow_tbl, &found->entry, found->key_crc32); + add_to_hw = true; + } + + found->ref_count++; + +spin_unlock_irqrestore (&rocker->flow_tbl_lock, flags); + +if (add_to_hw) { + err =rocker_cmd_exec (rocker, rocker_port, + rocker_cmd_flow_tbl_add, + found,NULL ,NULL , nowait); +if (err) { +spin_lock_irqsave (&rocker->flow_tbl_lock, flags); +hash_del (&found->entry); +spin_unlock_irqrestore (&rocker->flow_tbl_lock, flags); +kfree (found); + } + } + +return err; +} + +static int rocker_flow_tbl_del (struct rocker_port *rocker_port, +struct rocker_flow_tbl_entry *match, +bool nowait) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_flow_tbl_entry *found; +unsigned long flags; +bool del_from_hw = false; +int err =0 ; + + match->key_crc32 =crc32 (~0 , &match->key,sizeof (match->key)); + +spin_lock_irqsave (&rocker->flow_tbl_lock, flags); + + found =rocker_flow_tbl_find (rocker, match); + +if (found) { + found->ref_count--; +if (found->ref_count ==0 ) { +hash_del (&found->entry); + del_from_hw = true; + } + } + +spin_unlock_irqrestore (&rocker->flow_tbl_lock, flags); + +kfree (match); + +if (del_from_hw) { + err =rocker_cmd_exec (rocker, rocker_port, + rocker_cmd_flow_tbl_del, + found,NULL ,NULL , nowait); +kfree (found); + } + +return err; +} + +static gfp_t rocker_op_flags_gfp (int flags) +{ +return flags &ROCKER_OP_FLAG_NOWAIT ?GFP_ATOMIC :GFP_KERNEL ; +} + +static int rocker_flow_tbl_do (struct rocker_port *rocker_port, +int flags,struct rocker_flow_tbl_entry *entry) +{ +bool nowait = flags &ROCKER_OP_FLAG_NOWAIT ; + +if (flags &ROCKER_OP_FLAG_REMOVE ) +return rocker_flow_tbl_del (rocker_port, entry, nowait); +else +return rocker_flow_tbl_add (rocker_port, entry, nowait); +} + +static int rocker_flow_tbl_ig_port (struct rocker_port *rocker_port, +int flags,u32 in_lport,u32 in_lport_mask, +enum rocker_of_dpa_table_id goto_tbl) +{ +struct rocker_flow_tbl_entry *entry; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + + entry->key.priority =ROCKER_PRIORITY_IG_PORT ; + entry->key.tbl_id =ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT ; + entry->key.ig_port.in_lport = in_lport; + entry->key.ig_port.in_lport_mask = in_lport_mask; + entry->key.ig_port.goto_tbl = goto_tbl; + +return rocker_flow_tbl_do (rocker_port, flags, entry); +} + +static int rocker_flow_tbl_vlan (struct rocker_port *rocker_port, +int flags,u32 in_lport, +__be16 vlan_id,__be16 vlan_id_mask, +enum rocker_of_dpa_table_id goto_tbl, +bool untagged,__be16 new_vlan_id) +{ +struct rocker_flow_tbl_entry *entry; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + + entry->key.priority =ROCKER_PRIORITY_VLAN ; + entry->key.tbl_id =ROCKER_OF_DPA_TABLE_ID_VLAN ; + entry->key.vlan.in_lport = in_lport; + entry->key.vlan.vlan_id = vlan_id; + entry->key.vlan.vlan_id_mask = vlan_id_mask; + entry->key.vlan.goto_tbl = goto_tbl; + + entry->key.vlan.untagged = untagged; + entry->key.vlan.new_vlan_id = new_vlan_id; + +return rocker_flow_tbl_do (rocker_port, flags, entry); +} + +static int rocker_flow_tbl_term_mac (struct rocker_port *rocker_port, +u32 in_lport,u32 in_lport_mask, +__be16 eth_type,const u8 *eth_dst, +const u8 *eth_dst_mask,__be16 vlan_id, +__be16 vlan_id_mask,bool copy_to_cpu, +int flags) +{ +struct rocker_flow_tbl_entry *entry; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + +if (is_multicast_ether_addr (eth_dst)) { + entry->key.priority =ROCKER_PRIORITY_TERM_MAC_MCAST ; + entry->key.term_mac.goto_tbl = +ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING ; + }else { + entry->key.priority =ROCKER_PRIORITY_TERM_MAC_UCAST ; + entry->key.term_mac.goto_tbl = +ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING ; + } + + entry->key.tbl_id =ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC ; + entry->key.term_mac.in_lport = in_lport; + entry->key.term_mac.in_lport_mask = in_lport_mask; + entry->key.term_mac.eth_type = eth_type; +ether_addr_copy (entry->key.term_mac.eth_dst, eth_dst); +ether_addr_copy (entry->key.term_mac.eth_dst_mask, eth_dst_mask); + entry->key.term_mac.vlan_id = vlan_id; + entry->key.term_mac.vlan_id_mask = vlan_id_mask; + entry->key.term_mac.copy_to_cpu = copy_to_cpu; + +return rocker_flow_tbl_do (rocker_port, flags, entry); +} + +static int rocker_flow_tbl_bridge (struct rocker_port *rocker_port, +int flags, +const u8 *eth_dst,const u8 *eth_dst_mask, +__be16 vlan_id,u32 tunnel_id, +enum rocker_of_dpa_table_id goto_tbl, +u32 group_id,bool copy_to_cpu) +{ +struct rocker_flow_tbl_entry *entry; +u32 priority; +bool vlan_bridging = !!vlan_id; +bool dflt = !eth_dst || (eth_dst && eth_dst_mask); +bool wild = false; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + + entry->key.tbl_id =ROCKER_OF_DPA_TABLE_ID_BRIDGING ; + +if (eth_dst) { + entry->key.bridge.has_eth_dst =1 ; +ether_addr_copy (entry->key.bridge.eth_dst, eth_dst); + } +if (eth_dst_mask) { + entry->key.bridge.has_eth_dst_mask =1 ; +ether_addr_copy (entry->key.bridge.eth_dst_mask, eth_dst_mask); +if (memcmp (eth_dst_mask, ff_mac,ETH_ALEN )) + wild = true; + } + + priority =ROCKER_PRIORITY_UNKNOWN ; +if (vlan_bridging & dflt & wild) + priority =ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_WILD ; +else if (vlan_bridging & dflt & !wild) + priority =ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_EXACT ; +else if (vlan_bridging & !dflt) + priority =ROCKER_PRIORITY_BRIDGING_VLAN ; +else if (!vlan_bridging & dflt & wild) + priority =ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_WILD ; +else if (!vlan_bridging & dflt & !wild) + priority =ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_EXACT ; +else if (!vlan_bridging & !dflt) + priority =ROCKER_PRIORITY_BRIDGING_TENANT ; + + entry->key.priority = priority; + entry->key.bridge.vlan_id = vlan_id; + entry->key.bridge.tunnel_id = tunnel_id; + entry->key.bridge.goto_tbl = goto_tbl; + entry->key.bridge.group_id = group_id; + entry->key.bridge.copy_to_cpu = copy_to_cpu; + +return rocker_flow_tbl_do (rocker_port, flags, entry); +} + +static int rocker_flow_tbl_acl (struct rocker_port *rocker_port, +int flags,u32 in_lport, +u32 in_lport_mask, +const u8 *eth_src,const u8 *eth_src_mask, +const u8 *eth_dst,const u8 *eth_dst_mask, +__be16 eth_type, +__be16 vlan_id,__be16 vlan_id_mask, +u8 ip_proto,u8 ip_proto_mask, +u8 ip_tos,u8 ip_tos_mask, +u32 group_id) +{ +u32 priority; +struct rocker_flow_tbl_entry *entry; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + + priority =ROCKER_PRIORITY_ACL_NORMAL ; +if (eth_dst && eth_dst_mask) { +if (memcmp (eth_dst_mask, mcast_mac,ETH_ALEN ) ==0 ) + priority =ROCKER_PRIORITY_ACL_DFLT ; +else if (is_link_local_ether_addr (eth_dst)) + priority =ROCKER_PRIORITY_ACL_CTRL ; + } + + entry->key.priority = priority; + entry->key.tbl_id =ROCKER_OF_DPA_TABLE_ID_ACL_POLICY ; + entry->key.acl.in_lport = in_lport; + entry->key.acl.in_lport_mask = in_lport_mask; + +if (eth_src) +ether_addr_copy (entry->key.acl.eth_src, eth_src); +if (eth_src_mask) +ether_addr_copy (entry->key.acl.eth_src_mask, eth_src_mask); +if (eth_dst) +ether_addr_copy (entry->key.acl.eth_dst, eth_dst); +if (eth_dst_mask) +ether_addr_copy (entry->key.acl.eth_dst_mask, eth_dst_mask); + + entry->key.acl.eth_type = eth_type; + entry->key.acl.vlan_id = vlan_id; + entry->key.acl.vlan_id_mask = vlan_id_mask; + entry->key.acl.ip_proto = ip_proto; + entry->key.acl.ip_proto_mask = ip_proto_mask; + entry->key.acl.ip_tos = ip_tos; + entry->key.acl.ip_tos_mask = ip_tos_mask; + entry->key.acl.group_id = group_id; + +return rocker_flow_tbl_do (rocker_port, flags, entry); +} + +static struct rocker_group_tbl_entry * +rocker_group_tbl_find (struct rocker *rocker, +struct rocker_group_tbl_entry *match) +{ +struct rocker_group_tbl_entry *found; + +hash_for_each_possible (rocker->group_tbl, found, + entry, match->group_id) +if (found->group_id == match->group_id) +return found; + +return NULL ; +} + +static void rocker_group_tbl_entry_free (struct rocker_group_tbl_entry *entry) +{ +switch (ROCKER_GROUP_TYPE_GET (entry->group_id)) { +case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD : +case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST : +kfree (entry->group_ids); +break ; +default : +break ; + } +kfree (entry); +} + +static int rocker_group_tbl_add (struct rocker_port *rocker_port, +struct rocker_group_tbl_entry *match, +bool nowait) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_group_tbl_entry *found; +unsigned long flags; +int err =0 ; + +spin_lock_irqsave (&rocker->group_tbl_lock, flags); + + found =rocker_group_tbl_find (rocker, match); + +if (found) { +hash_del (&found->entry); +rocker_group_tbl_entry_free (found); + found = match; + found->cmd =ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD ; + }else { + found = match; + found->cmd =ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD ; + } + +hash_add (rocker->group_tbl, &found->entry, found->group_id); + +spin_unlock_irqrestore (&rocker->group_tbl_lock, flags); + +if (found->cmd) + err =rocker_cmd_exec (rocker, rocker_port, + rocker_cmd_group_tbl_add, + found,NULL ,NULL , nowait); + +return err; +} + +static int rocker_group_tbl_del (struct rocker_port *rocker_port, +struct rocker_group_tbl_entry *match, +bool nowait) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_group_tbl_entry *found; +unsigned long flags; +int err =0 ; + +spin_lock_irqsave (&rocker->group_tbl_lock, flags); + + found =rocker_group_tbl_find (rocker, match); + +if (found) { +hash_del (&found->entry); + found->cmd =ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL ; + } + +spin_unlock_irqrestore (&rocker->group_tbl_lock, flags); + +rocker_group_tbl_entry_free (match); + +if (found) { + err =rocker_cmd_exec (rocker, rocker_port, + rocker_cmd_group_tbl_del, + found,NULL ,NULL , nowait); +rocker_group_tbl_entry_free (found); + } + +return err; +} + +static int rocker_group_tbl_do (struct rocker_port *rocker_port, +int flags,struct rocker_group_tbl_entry *entry) +{ +bool nowait = flags &ROCKER_OP_FLAG_NOWAIT ; + +if (flags &ROCKER_OP_FLAG_REMOVE ) +return rocker_group_tbl_del (rocker_port, entry, nowait); +else +return rocker_group_tbl_add (rocker_port, entry, nowait); +} + +static int rocker_group_l2_interface (struct rocker_port *rocker_port, +int flags,__be16 vlan_id, +u32 out_lport,int pop_vlan) +{ +struct rocker_group_tbl_entry *entry; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + + entry->group_id =ROCKER_GROUP_L2_INTERFACE (vlan_id, out_lport); + entry->l2_interface.pop_vlan = pop_vlan; + +return rocker_group_tbl_do (rocker_port, flags, entry); +} + +static int rocker_group_l2_fan_out (struct rocker_port *rocker_port, +int flags,u8 group_count, +u32 *group_ids,u32 group_id) +{ +struct rocker_group_tbl_entry *entry; + + entry =kzalloc (sizeof (*entry),rocker_op_flags_gfp (flags)); +if (!entry) +return -ENOMEM ; + + entry->group_id = group_id; + entry->group_count = group_count; + + entry->group_ids =kcalloc (group_count,sizeof (u32), +rocker_op_flags_gfp (flags)); +if (!entry->group_ids) { +kfree (entry); +return -ENOMEM ; + } +memcpy (entry->group_ids, group_ids, group_count *sizeof (u32)); + +return rocker_group_tbl_do (rocker_port, flags, entry); +} + +static int rocker_group_l2_flood (struct rocker_port *rocker_port, +int flags,__be16 vlan_id, +u8 group_count,u32 *group_ids, +u32 group_id) +{ +return rocker_group_l2_fan_out (rocker_port, flags, + group_count, group_ids, + group_id); +} + +static struct rocker_ctrl { +const u8 *eth_dst; +const u8 *eth_dst_mask; +u16 eth_type; +bool acl; +bool bridge; +bool term; +bool copy_to_cpu; +} rocker_ctrls[] = { + [ROCKER_CTRL_LINK_LOCAL_MCAST ] = { +/* pass link local multicast pkts up to CPU for filtering */ + .eth_dst = ll_mac, + .eth_dst_mask = ll_mask, + .acl = true, + }, + [ROCKER_CTRL_LOCAL_ARP ] = { +/* pass local ARP pkts up to CPU */ + .eth_dst = zero_mac, + .eth_dst_mask = zero_mac, + .eth_type =htons (ETH_P_ARP ), + .acl = true, + }, + [ROCKER_CTRL_IPV4_MCAST ] = { +/* pass IPv4 mcast pkts up to CPU, RFC 1112 */ + .eth_dst = ipv4_mcast, + .eth_dst_mask = ipv4_mask, + .eth_type =htons (ETH_P_IP ), + .term = true, + .copy_to_cpu = true, + }, + [ROCKER_CTRL_IPV6_MCAST ] = { +/* pass IPv6 mcast pkts up to CPU, RFC 2464 */ + .eth_dst = ipv6_mcast, + .eth_dst_mask = ipv6_mask, + .eth_type =htons (ETH_P_IPV6 ), + .term = true, + .copy_to_cpu = true, + }, + [ROCKER_CTRL_DFLT_BRIDGING ] = { +/* flood any pkts on vlan */ + .bridge = true, + .copy_to_cpu = true, + }, +}; + +static int rocker_port_ctrl_vlan_acl (struct rocker_port *rocker_port, +int flags,struct rocker_ctrl *ctrl, +__be16 vlan_id) +{ +u32 in_lport = rocker_port->lport; +u32 in_lport_mask =0xffffffff ; +u32 out_lport =0 ; +u8 *eth_src =NULL ; +u8 *eth_src_mask =NULL ; +__be16 vlan_id_mask =htons (0xffff ); +u8 ip_proto =0 ; +u8 ip_proto_mask =0 ; +u8 ip_tos =0 ; +u8 ip_tos_mask =0 ; +u32 group_id =ROCKER_GROUP_L2_INTERFACE (vlan_id, out_lport); +int err; + + err =rocker_flow_tbl_acl (rocker_port, flags, + in_lport, in_lport_mask, + eth_src, eth_src_mask, + ctrl->eth_dst, ctrl->eth_dst_mask, + ctrl->eth_type, + vlan_id, vlan_id_mask, + ip_proto, ip_proto_mask, + ip_tos, ip_tos_mask, + group_id); + +if (err) +netdev_err (rocker_port->dev,"Error (%d) ctrl ACL\n" , err); + +return err; +} + +static int rocker_port_ctrl_vlan_term (struct rocker_port *rocker_port, +int flags,struct rocker_ctrl *ctrl, +__be16 vlan_id) +{ +u32 in_lport_mask =0xffffffff ; +__be16 vlan_id_mask =htons (0xffff ); +int err; + +if (ntohs (vlan_id) ==0 ) + vlan_id = rocker_port->internal_vlan_id; + + err =rocker_flow_tbl_term_mac (rocker_port, + rocker_port->lport, in_lport_mask, + ctrl->eth_type, ctrl->eth_dst, + ctrl->eth_dst_mask, vlan_id, + vlan_id_mask, ctrl->copy_to_cpu, + flags); + +if (err) +netdev_err (rocker_port->dev,"Error (%d) ctrl term\n" , err); + +return err; +} + +static int rocker_port_ctrl_vlan (struct rocker_port *rocker_port,int flags, +struct rocker_ctrl *ctrl,__be16 vlan_id) +{ +if (ctrl->acl) +return rocker_port_ctrl_vlan_acl (rocker_port, flags, + ctrl, vlan_id); + +if (ctrl->term) +return rocker_port_ctrl_vlan_term (rocker_port, flags, + ctrl, vlan_id); + +return -EOPNOTSUPP ; +} + +static int rocker_port_ctrl_vlan_add (struct rocker_port *rocker_port, +int flags,__be16 vlan_id) +{ +int err =0 ; +int i; + +for (i =0 ; i <ROCKER_CTRL_MAX ; i++) { +if (rocker_port->ctrls[i]) { + err =rocker_port_ctrl_vlan (rocker_port, flags, + &rocker_ctrls[i], vlan_id); +if (err) +return err; + } + } + +return err; +} + +static int rocker_port_ctrl (struct rocker_port *rocker_port,int flags, +struct rocker_ctrl *ctrl) +{ +u16 vid; +int err =0 ; + +for (vid =1 ; vid <VLAN_N_VID ; vid++) { +if (!test_bit (vid, rocker_port->vlan_bitmap)) +continue ; + err =rocker_port_ctrl_vlan (rocker_port, flags, + ctrl,htons (vid)); +if (err) +break ; + } + +return err; +} + +static int rocker_port_ig_tbl (struct rocker_port *rocker_port,int flags) +{ +enum rocker_of_dpa_table_id goto_tbl; +u32 in_lport; +u32 in_lport_mask; +int err; + +/* Normal Ethernet Frames. Matches pkts from any local physical +* ports. Goto VLAN tbl. +*/ + + in_lport =0 ; + in_lport_mask =0xffff0000 ; + goto_tbl =ROCKER_OF_DPA_TABLE_ID_VLAN ; + + err =rocker_flow_tbl_ig_port (rocker_port, flags, + in_lport, in_lport_mask, + goto_tbl); +if (err) +netdev_err (rocker_port->dev, +"Error (%d) ingress port table entry\n" , err); + +return err; +} + +static int rocker_port_router_mac (struct rocker_port *rocker_port, +int flags,__be16 vlan_id) +{ +u32 in_lport_mask =0xffffffff ; +__be16 eth_type; +const u8 *dst_mac_mask = ff_mac; +__be16 vlan_id_mask =htons (0xffff ); +bool copy_to_cpu = false; +int err; + +if (ntohs (vlan_id) ==0 ) + vlan_id = rocker_port->internal_vlan_id; + + eth_type =htons (ETH_P_IP ); + err =rocker_flow_tbl_term_mac (rocker_port, + rocker_port->lport, in_lport_mask, + eth_type, rocker_port->dev->dev_addr, + dst_mac_mask, vlan_id, vlan_id_mask, + copy_to_cpu, flags); +if (err) +return err; + + eth_type =htons (ETH_P_IPV6 ); + err =rocker_flow_tbl_term_mac (rocker_port, + rocker_port->lport, in_lport_mask, + eth_type, rocker_port->dev->dev_addr, + dst_mac_mask, vlan_id, vlan_id_mask, + copy_to_cpu, flags); + +return err; +} + +static struct rocker_internal_vlan_tbl_entry * +rocker_internal_vlan_tbl_find (struct rocker *rocker,int ifindex) +{ +struct rocker_internal_vlan_tbl_entry *found; + +hash_for_each_possible (rocker->internal_vlan_tbl, + found, entry, ifindex) +if (found->ifindex == ifindex) +return found; + +return NULL ; +} + +static __be16 rocker_port_internal_vlan_id_get (struct rocker_port *rocker_port, +int ifindex) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_internal_vlan_tbl_entry *entry; +struct rocker_internal_vlan_tbl_entry *found; +unsigned long lock_flags; +int i; + + entry =kzalloc (sizeof (*entry),GFP_KERNEL ); +if (!entry) +return 0 ; + + entry->ifindex = ifindex; + +spin_lock_irqsave (&rocker->internal_vlan_tbl_lock, lock_flags); + + found =rocker_internal_vlan_tbl_find (rocker, ifindex); +if (found) { +kfree (entry); + goto found; + } + + found = entry; +hash_add (rocker->internal_vlan_tbl, &found->entry, found->ifindex); + +for (i =0 ; i <ROCKER_N_INTERNAL_VLANS ; i++) { +if (test_and_set_bit (i, rocker->internal_vlan_bitmap)) +continue ; + found->vlan_id =htons (ROCKER_INTERNAL_VLAN_ID_BASE + i); + goto found; + } + +netdev_err (rocker_port->dev,"Out of internal VLAN IDs\n" ); + +found: + found->ref_count++; +spin_unlock_irqrestore (&rocker->internal_vlan_tbl_lock, lock_flags); + +return found->vlan_id; +} + +static void rocker_port_internal_vlan_id_put (struct rocker_port *rocker_port, +int ifindex) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_internal_vlan_tbl_entry *found; +unsigned long lock_flags; +unsigned long bit; + +spin_lock_irqsave (&rocker->internal_vlan_tbl_lock, lock_flags); + + found =rocker_internal_vlan_tbl_find (rocker, ifindex); +if (!found) { +netdev_err (rocker_port->dev, +"ifindex (%d) not found in internal VLAN tbl\n" , + ifindex); + goto not_found; + } + +if (--found->ref_count <=0 ) { + bit =ntohs (found->vlan_id) -ROCKER_INTERNAL_VLAN_ID_BASE ; +clear_bit (bit, rocker->internal_vlan_bitmap); +hash_del (&found->entry); +kfree (found); + } + +not_found: +spin_unlock_irqrestore (&rocker->internal_vlan_tbl_lock, lock_flags); +} +/***************** * Net device ops *****************/
@@ -1770,10 +3207,14 @@ static void rocker_carrier_init(struct rocker_port *rocker_port)static void rocker_remove_ports (struct rocker *rocker) { +struct rocker_port *rocker_port;int i; -for (i =0 ; i < rocker->port_count; i++) -unregister_netdev (rocker->ports[i]->dev); +for (i =0 ; i < rocker->port_count; i++) { + rocker_port = rocker->ports[i]; +rocker_port_ig_tbl (rocker_port,ROCKER_OP_FLAG_REMOVE ); +unregister_netdev (rocker_port->dev); + }kfree (rocker->ports); }
@@ -1825,8 +3266,18 @@ static int rocker_probe_port(struct rocker *rocker, unsigned port_number) } rocker->ports[port_number] = rocker_port; + rocker_port->internal_vlan_id = +rocker_port_internal_vlan_id_get (rocker_port, dev->ifindex); + err =rocker_port_ig_tbl (rocker_port,0 ); +if (err) { +dev_err (&pdev->dev,"install ig port table failed\n" ); + goto err_port_ig_tbl; + } +return 0 ; +err_port_ig_tbl: +unregister_netdev (dev); err_register_netdev:free_netdev (dev);return err;
@@ -1983,6 +3434,12 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) rocker->hw.id =rocker_read64 (rocker,SWITCH_ID ); + err =rocker_init_tbls (rocker); +if (err) { +dev_err (&pdev->dev,"cannot init rocker tables\n" ); + goto err_init_tbls; + } + err =rocker_probe_ports (rocker);if (err) {dev_err (&pdev->dev,"failed to probe ports\n" );
@@ -1994,6 +3451,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)return 0 ; err_probe_ports: +rocker_free_tbls (rocker); +err_init_tbls:free_irq (rocker_msix_vector (rocker,ROCKER_MSIX_VEC_EVENT ), rocker); err_request_event_irq:free_irq (rocker_msix_vector (rocker,ROCKER_MSIX_VEC_CMD ), rocker);
@@ -2019,6 +3478,7 @@ static void rocker_remove(struct pci_dev *pdev) {struct rocker *rocker =pci_get_drvdata (pdev); +rocker_free_tbls (rocker);rocker_write32 (rocker,CONTROL ,ROCKER_CONTROL_RESET );rocker_remove_ports (rocker);free_irq (rocker_msix_vector (rocker,ROCKER_MSIX_VEC_EVENT ), rocker);
Signature · 1 line
1.9.3
David Miller
maintainerRe: [patch net-next 09/10] rocker: implement rocker ofdpa flow table manipulation
From: Jiri Pirko <[email protected]>
Date: Thu, 6 Nov 2014 10:20:09 +0100
drivers/net/ethernet/rocker/rocker.c static int rocker_event_link_change(struct rocker *rocker, · line 1302
Quoted 3 lines
drivers/net/ethernet/rocker/rocker.c +3
-0+#define ROCKER_OP_FLAG_REMOVE (1 << 0) +#define ROCKER_OP_FLAG_NOWAIT (1 << 1) +#define ROCKER_OP_FLAG_LEARNED (1 << 2)
These can be defined to BIT(0), BIT(1), BIT(2).
drivers/net/ethernet/rocker/rocker.c static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, · line 2400
Quoted 4 lines
drivers/net/ethernet/rocker/rocker.c +4
-0+hash_for_each_possible (rocker->group_tbl, found, + entry, match->group_id) +if (found->group_id == match->group_id) +return found;
Please enclose multi-line basic blocks inside of curly braces.
Joe Perches
maintainerPATCH 1/1 checkpatch: Add --strict preference for #defines using BIT(foo)
Using BIT(foo) and BIT_ULL(bar) is more common now. Suggest using these macros over #defines with 1<<value.
Add a --fix option too.
--- scripts/checkpatch.pl | 11 +++++++++++ 1 file changed, 11 insertions(+)
scripts/checkpatch.pl +11 -0
@@ -4973,6 +4973,17 @@ sub process { } } +# check for #defines like: 1 << <digit> that could be BIT(digit) + if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { + my $ull = ""; + $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); + if (CHK("BIT_MACRO", + "Prefer using the BIT$ull macro\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; + } + } + # check for case / default statements not preceded by break/fallthrough/switch if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { my $has_break = 0;
Jiri Pirko
maintainerRe: [PATCH] checkpatch: Add --strict preference for #defines using BIT(foo)
Quoted 2 lines · Fri, Nov 07, 2014 at 10:15:39PM CET, [email protected] wrote:
Using BIT(foo) and
BIT_ULL(bar)is more common now. Suggest using these macros over #defines with 1<<value.
Joe, regarding the other Dave's comment, the multiline one, that is also not covered by checkpatch. Would please you take care of that as well?
Thanks.
Quoted 32 lines
Add a --fix option too.
Signed-off-by Joe Perches <[email protected]>---
scripts/checkpatch.pl| 11 +++++++++++1 file changed, 11 insertions(+)scripts/checkpatch.pl +11
-0@@ -4973,6 +4973,17 @@ sub process { } } +# check for #defines like: 1 << <digit> that could be BIT(digit) + if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { + my $ull = ""; + $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); + if (CHK("BIT_MACRO", + "Prefer using the BIT$ull macro\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; + } + } + # check for case / default statements not preceded by break/fallthrough/switch if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { my $has_break = 0;
Joe Perches
maintainerRe: [PATCH] checkpatch: Add --strict preference for #defines using BIT(foo)
Quoted 2 lines · Jiri Pirko
Joe, regarding the other Dave's comment, the multiline one, that is also not covered by checkpatch. Would please you take care of that as well?
No, as far as I know, it's not feasible given
insertion/deletion, but you are welcome to try.
Re: [PATCH] checkpatch: Add --strict preference for #defines using BIT(foo)
In reply to Joe PerchesQuoted 2 lines · On Fri, 07 Nov 2014 13:15:39 -0800 Joe Perches <[email protected]> wrote:
Using BIT(foo) and
BIT_ULL(bar)is more common now. Suggest using these macros over #defines with 1<<value.
urgh. I'm counting eightish implementations of BIT(), an unknown number of which are actually being used. Many use 1<<n, some use 1UL<<N, another uses 1ULL<<n. I'm a bit reluctant to recommend that anyone should use BIT() until it has has some vigorous scrubbing :(
Is it actually an improvement? If I see
#define X (1U << 7)
then I know exactly what it does. Whereas when I see
#define X BIT(7)
I know neither the size or the signedness of X so I have to go look it up.
I have no strong feelings either way, but I'm wondering what might have inspired this change?
Joe Perches
maintainerRe: [PATCH] checkpatch: Add --strict preference for #defines using BIT(foo)
Quoted 12 lines · Andrew Morton
Quoted 2 lines · On Fri, 07 Nov 2014 13:15:39 -0800 Joe Perches <[email protected]> wrote:
Using BIT(foo) and
BIT_ULL(bar)is more common now. Suggest using these macros over #defines with 1<<value.urgh. I'm counting eightish implementations of
BIT(), an unknown number of which are actually being used. Many use 1<<n, some use 1UL<<N, another uses 1ULL<<n. I'm a bit reluctant to recommend that anyone should useBIT()until it has has some vigorous scrubbing :(Is it actually an improvement? If I see
Quoted 8 lines
#define X (1U << 7)
then I know exactly what it does. Whereas when I see
#define X BIT(7)
I know neither the size or the signedness of X so I have to go look it up.
I'm not sure the signedness or type of X matters much as the non-64bit comparisons are done by promotion to at least int or unsigned int anyway.
The BIT macro makes sure a single bit is set.
The 'good' one is in bitops.h. It also has #define BIT_ULL
include/linux/bitops.h:#define BIT(nr) (1UL << (nr))
include/linux/bitops.h-#define BIT_ULL(nr) (1ULL << (nr))
The ones in tools/ are independent and should not be changed. Excluding tools/, the others should probably be removed
$ git grep -E "define\s+BIT\b" arch/arm/mach-davinci/sleep.S:#define BIT(nr) (1 << (nr)) drivers/staging/rtl8188eu/include/rtl8188e_spec.h:#define BIT(x) (1 << (x)) drivers/staging/rtl8188eu/include/wifi.h:#define BIT(x) (1 << (x)) drivers/staging/rtl8192e/rtl8192e/rtl_core.h:#define BIT(_i) (1<<(_i)) drivers/staging/rtl8712/osdep_service.h: #define BIT(x) (1 << (x)) drivers/staging/rtl8712/wifi.h:#define BIT(x) (1 << (x))
Quoted 2 lines
I have no strong feelings either way, but I'm wondering what might have inspired this change?
David Miller commented on a netdev patch where 1<<foo was being used in a #define and suggested using BIT.
http://article.gmane.org/gmane.linux.network/337535/match=bit
Using BIT _is_ more common in recent patches too.
Jiri Pirko
maintainerPATCH 10/10 rocker: implement L2 bridge offloading
In reply to Jiri PirkoFrom: Scott Feldman <[email protected]>
Add L2 bridge offloading support to rocker driver. Here, the Linux bridge driver is used to collect swdev ports into a tagged (or untagged) VLAN bridge. The swdev will offload from the bridge driver the following L2 bridging functions:
- Learning of neighbor MAC addresses on VLAN X Learned mac/vlan is
installed in bridge FDB. (And removed when device unlearns mac/vlan). Learning must be turned off on each bridge port to disable the feature in the bridge driver.
- Flooding of multicast/broadcast and unknown unicast pkts to (STP) active ports in bridge. The bridge driver is unaware of the flooding happening at the device level. Flooding must be turned off on each bridge port to disable the feature on the bridge driver.
- STP port state is pushed down to driver/device. The bridge still processes STP BDPUs and maintains port STP state (for all VLANs in bridge), but the driver/device must be notified of port STP state change to program the device.
Multiple (VLAN) bridges are supported. The device (implemented per the OF-DPA spec) must use a portion of the VLAN namespace for internal VLANs. Right now, the upper 255 VLANs (0xf00 to 0xffe) are used as internal VLAN IDs for untagged traffic and are not available as port VLANs.
The driver uses the following interfaces:
1. To track VLAN add/del on ports in bridge:
.ndo_vlan_rx_add_vid
.ndo_vlan_rx_kill_vid
2. To track port add/del membership in bridge:
NETDEV_CHANGEUPPER netdevice notifier
3. To catch static FDB entries installed on bridge/vlan by user using netlink:
.ndo_sw_port_fdb_add
.ndo_sw_port_fdb_del
4. To be notified on port STP state change:
.ndo_sw_port_stp_update
5. To notify bridge driver on learned/forgotten mac/vlans on bridge port:
br_fdb_learn_addbr_fdb_learn_del
--- drivers/net/ethernet/rocker/rocker.c | 660 ++++++++++++++++++++++++++++++++++- 1 file changed, 659 insertions(+), 1 deletion(-)
drivers/net/ethernet/rocker/rocker.c +659 -1
@@ -28,6 +28,7 @@#include <linux/ethtool.h> #include <linux/if_ether.h> #include <linux/if_vlan.h> +#include <linux/if_bridge.h> #include <net/switchdev.h> #include <net/rtnetlink.h> #include <asm-generic/io-64-nonatomic-lo-hi.h>
@@ -195,10 +196,12 @@ enum {struct rocker_port {struct net_device *dev; +struct net_device *bridge_dev;struct rocker *rocker;unsigned port_number;u32 lport;__be16 internal_vlan_id; +int stp_state;bool ctrls[ROCKER_CTRL_MAX ];unsigned long vlan_bitmap[ROCKER_VLAN_BITMAP_LEN ];struct napi_struct napi_tx;
@@ -289,6 +292,20 @@ static __be16 rocker_port_vid_to_vlan(struct rocker_port *rocker_port,return vlan_id; } +static u16 rocker_port_vlan_to_vid (struct rocker_port *rocker_port, +__be16 vlan_id) +{ +if (rocker_vlan_id_is_internal (vlan_id)) +return 0 ; + +return ntohs (vlan_id); +} + +static bool rocker_port_is_bridged (struct rocker_port *rocker_port) +{ +return !!rocker_port->bridge_dev; +} +struct rocker_wait {wait_queue_head_t wait;bool done;
@@ -1301,6 +1318,42 @@ static int rocker_event_link_change(struct rocker *rocker,#define ROCKER_OP_FLAG_NOWAIT (1 << 1)#define ROCKER_OP_FLAG_LEARNED (1 << 2) +static int rocker_port_fdb (struct rocker_port *rocker_port, +const unsigned char *addr, +__be16 vlan_id,int flags); + +static int rocker_event_mac_vlan_seen (struct rocker *rocker, +const struct rocker_tlv *info) +{ +struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAX +1 ]; +unsigned port_number; +struct rocker_port *rocker_port; +unsigned char *addr; +int flags =ROCKER_OP_FLAG_NOWAIT |ROCKER_OP_FLAG_LEARNED ; +__be16 vlan_id; + +rocker_tlv_parse_nested (attrs,ROCKER_TLV_EVENT_MAC_VLAN_MAX , info); +if (!attrs[ROCKER_TLV_EVENT_MAC_VLAN_LPORT ] || + !attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC ] || + !attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID ]) +return -EIO ; + port_number = +rocker_tlv_get_u32 (attrs[ROCKER_TLV_EVENT_MAC_VLAN_LPORT ]) -1 ; + addr =rocker_tlv_data (attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC ]); + vlan_id =rocker_tlv_get_u16 (attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID ]); + +if (port_number >= rocker->port_count) +return -EINVAL ; + + rocker_port = rocker->ports[port_number]; + +if (rocker_port->stp_state !=BR_STATE_LEARNING && + rocker_port->stp_state !=BR_STATE_FORWARDING ) +return 0 ; + +return rocker_port_fdb (rocker_port, addr, vlan_id, flags); +} +static int rocker_event_process (struct rocker *rocker,struct rocker_desc_info *desc_info) {
@@ -1319,6 +1372,8 @@ static int rocker_event_process(struct rocker *rocker,switch (type) {case ROCKER_TLV_EVENT_TYPE_LINK_CHANGED :return rocker_event_link_change (rocker, info); +case ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN : +return rocker_event_mac_vlan_seen (rocker, info); }return -EOPNOTSUPP ;
@@ -2543,6 +2598,104 @@ static int rocker_group_l2_flood(struct rocker_port *rocker_port, group_id); } +static int rocker_port_vlan_flood_group (struct rocker_port *rocker_port, +int flags,__be16 vlan_id) +{ +struct rocker_port *p; +struct rocker *rocker = rocker_port->rocker; +u32 group_id =ROCKER_GROUP_L2_FLOOD (vlan_id,0 ); +u32 group_ids[rocker->port_count]; +u8 group_count =0 ; +int err; +int i; + +/* Adjust the flood group for this VLAN. The flood group +* references an L2 interface group for each port in this +* VLAN. +*/ + +for (i =0 ; i < rocker->port_count; i++) { + p = rocker->ports[i]; +if (!rocker_port_is_bridged (p)) +continue ; +if (test_bit (ntohs (vlan_id), p->vlan_bitmap)) { + group_ids[group_count++] = +ROCKER_GROUP_L2_INTERFACE (vlan_id, + p->lport); + } + } + +/* If there are no bridged ports in this VLAN, we're done */ +if (group_count ==0 ) +return 0 ; + + err =rocker_group_l2_flood (rocker_port, flags, vlan_id, + group_count, group_ids, + group_id); +if (err) +netdev_err (rocker_port->dev, +"Error (%d) port VLAN l2 flood group\n" , err); + +return err; +} + +static int rocker_port_vlan_l2_groups (struct rocker_port *rocker_port, +int flags,__be16 vlan_id, +bool pop_vlan) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_port *p; +bool adding = !(flags &ROCKER_OP_FLAG_REMOVE ); +u32 out_lport; +int ref =0 ; +int err; +int i; + +/* An L2 interface group for this port in this VLAN, but +* only when port STP state is LEARNING|FORWARDING. +*/ + +if (rocker_port->stp_state ==BR_STATE_LEARNING || + rocker_port->stp_state ==BR_STATE_FORWARDING ) { + out_lport = rocker_port->lport; + err =rocker_group_l2_interface (rocker_port, flags, + vlan_id, out_lport, + pop_vlan); +if (err) { +netdev_err (rocker_port->dev, +"Error (%d) port VLAN l2 group for lport %d\n" , + err, out_lport); +return err; + } + } + +/* An L2 interface group for this VLAN to CPU port. +* Add when first port joins this VLAN and destroy when +* last port leaves this VLAN. +*/ + +for (i =0 ; i < rocker->port_count; i++) { + p = rocker->ports[i]; +if (test_bit (ntohs (vlan_id), p->vlan_bitmap)) + ref++; + } + +if ((!adding || ref !=1 ) && (adding || ref !=0 )) +return 0 ; + + out_lport =0 ; + err =rocker_group_l2_interface (rocker_port, flags, + vlan_id, out_lport, + pop_vlan); +if (err) { +netdev_err (rocker_port->dev, +"Error (%d) port VLAN l2 group for CPU port\n" , err); +return err; + } + +return 0 ; +} + static struct rocker_ctrl { const u8 *eth_dst; const u8 *eth_dst_mask;
@@ -2621,6 +2774,30 @@ static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port,return err; } +static int rocker_port_ctrl_vlan_bridge (struct rocker_port *rocker_port, +int flags,struct rocker_ctrl *ctrl, +__be16 vlan_id) +{ +enum rocker_of_dpa_table_id goto_tbl = +ROCKER_OF_DPA_TABLE_ID_ACL_POLICY ; +u32 group_id =ROCKER_GROUP_L2_FLOOD (vlan_id,0 ); +u32 tunnel_id =0 ; +int err; + +if (!rocker_port_is_bridged (rocker_port)) +return 0 ; + + err =rocker_flow_tbl_bridge (rocker_port, flags, + ctrl->eth_dst, ctrl->eth_dst_mask, + vlan_id, tunnel_id, + goto_tbl, group_id, ctrl->copy_to_cpu); + +if (err) +netdev_err (rocker_port->dev,"Error (%d) ctrl FLOOD\n" , err); + +return err; +} +static int rocker_port_ctrl_vlan_term (struct rocker_port *rocker_port,int flags,struct rocker_ctrl *ctrl,__be16 vlan_id)
@@ -2651,6 +2828,9 @@ static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, int flags,if (ctrl->acl)return rocker_port_ctrl_vlan_acl (rocker_port, flags, ctrl, vlan_id); +if (ctrl->bridge) +return rocker_port_ctrl_vlan_bridge (rocker_port, flags, + ctrl, vlan_id);if (ctrl->term) return rocker_port_ctrl_vlan_term(rocker_port, flags,
@@ -2695,6 +2875,64 @@ static int rocker_port_ctrl(struct rocker_port *rocker_port, int flags,return err; } +static int rocker_port_vlan (struct rocker_port *rocker_port,int flags, +u16 vid) +{ +enum rocker_of_dpa_table_id goto_tbl = +ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC ; +u32 in_lport = rocker_port->lport; +__be16 vlan_id =htons (vid); +__be16 vlan_id_mask =htons (0xffff ); +__be16 internal_vlan_id; +bool untagged; +bool adding = !(flags &ROCKER_OP_FLAG_REMOVE ); +int err; + + internal_vlan_id =rocker_port_vid_to_vlan (rocker_port, vid, &untagged); + +if (adding &&test_and_set_bit (ntohs (internal_vlan_id), + rocker_port->vlan_bitmap)) +return 0 ;/* already added */ +else if (!adding && !test_and_clear_bit (ntohs (internal_vlan_id), + rocker_port->vlan_bitmap)) +return 0 ;/* already removed */ + +if (adding) { + err =rocker_port_ctrl_vlan_add (rocker_port, flags, + internal_vlan_id); +if (err) { +netdev_err (rocker_port->dev, +"Error (%d) port ctrl vlan add\n" , err); +return err; + } + } + + err =rocker_port_vlan_l2_groups (rocker_port, flags, + internal_vlan_id, untagged); +if (err) { +netdev_err (rocker_port->dev, +"Error (%d) port VLAN l2 groups\n" , err); +return err; + } + + err =rocker_port_vlan_flood_group (rocker_port, flags, + internal_vlan_id); +if (err) { +netdev_err (rocker_port->dev, +"Error (%d) port VLAN l2 flood group\n" , err); +return err; + } + + err =rocker_flow_tbl_vlan (rocker_port, flags, + in_lport, vlan_id, vlan_id_mask, + goto_tbl, untagged, internal_vlan_id); +if (err) +netdev_err (rocker_port->dev, +"Error (%d) port VLAN table\n" , err); + +return err; +} +static int rocker_port_ig_tbl (struct rocker_port *rocker_port,int flags) {enum rocker_of_dpa_table_id goto_tbl;
@@ -2720,6 +2958,158 @@ static int rocker_port_ig_tbl(struct rocker_port *rocker_port, int flags)return err; } +struct rocker_fdb_learn_work { +struct work_struct work; +struct net_device *dev; +int flags; +u8 addr[ETH_ALEN ]; +u16 vid; +}; + +static void rocker_port_fdb_learn_work (struct work_struct *work) +{ +struct rocker_fdb_learn_work *lw = + container_of(work, struct rocker_fdb_learn_work, work); +bool removing = (lw->flags &ROCKER_OP_FLAG_REMOVE ); +bool learned = (lw->flags &ROCKER_OP_FLAG_LEARNED ); + +if (learned & removing) +br_fdb_learn_del (lw->dev, lw->addr, lw->vid); +else if (learned & !removing) +br_fdb_learn_add (lw->dev, lw->addr, lw->vid); + +kfree (work); +} + +static int rocker_port_fdb_learn (struct rocker_port *rocker_port, +int flags,const u8 *addr,__be16 vlan_id) +{ +struct rocker_fdb_learn_work *lw; +enum rocker_of_dpa_table_id goto_tbl = +ROCKER_OF_DPA_TABLE_ID_ACL_POLICY ; +u32 out_lport = rocker_port->lport; +u32 tunnel_id =0 ; +u32 group_id =ROCKER_GROUP_NONE ; +bool copy_to_cpu = false; +int err; + +if (rocker_port_is_bridged (rocker_port)) + group_id =ROCKER_GROUP_L2_INTERFACE (vlan_id, out_lport); + + err =rocker_flow_tbl_bridge (rocker_port, flags, addr,NULL , + vlan_id, tunnel_id, goto_tbl, + group_id, copy_to_cpu); +if (err) +return err; + +if (!rocker_port_is_bridged (rocker_port)) +return 0 ; + + lw =kmalloc (sizeof (*lw),rocker_op_flags_gfp (flags)); +if (!lw) +return -ENOMEM ; + +INIT_WORK (&lw->work, rocker_port_fdb_learn_work); + + lw->dev = rocker_port->dev; + lw->flags = flags; +ether_addr_copy (lw->addr, addr); + lw->vid =rocker_port_vlan_to_vid (rocker_port, vlan_id); + +schedule_work (&lw->work); + +return 0 ; +} + +static struct rocker_fdb_tbl_entry * +rocker_fdb_tbl_find (struct rocker *rocker,struct rocker_fdb_tbl_entry *match) +{ +struct rocker_fdb_tbl_entry *found; + +hash_for_each_possible (rocker->fdb_tbl, found, entry, match->key_crc32) +if (memcmp (&found->key, &match->key,sizeof (found->key)) ==0 ) +return found; + +return NULL ; +} + +static int rocker_port_fdb (struct rocker_port *rocker_port, +const unsigned char *addr, +__be16 vlan_id,int flags) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_fdb_tbl_entry *fdb; +struct rocker_fdb_tbl_entry *found; +bool removing = (flags &ROCKER_OP_FLAG_REMOVE ); +unsigned long lock_flags; + + fdb =kzalloc (sizeof (*fdb),rocker_op_flags_gfp (flags)); +if (!fdb) +return -ENOMEM ; + + fdb->learned = (flags &ROCKER_OP_FLAG_LEARNED ); + fdb->key.lport = rocker_port->lport; +ether_addr_copy (fdb->key.addr, addr); + fdb->key.vlan_id = vlan_id; + fdb->key_crc32 =crc32 (~0 , &fdb->key,sizeof (fdb->key)); + +spin_lock_irqsave (&rocker->fdb_tbl_lock, lock_flags); + + found =rocker_fdb_tbl_find (rocker, fdb); + +if (removing && found) { +kfree (fdb); +hash_del (&found->entry); + }else if (!removing && !found) { +hash_add (rocker->fdb_tbl, &fdb->entry, fdb->key_crc32); + } + +spin_unlock_irqrestore (&rocker->fdb_tbl_lock, lock_flags); + +/* Check if adding and already exists, or removing and can't find */ +if (!found != !removing) { +kfree (fdb); +return 0 ; + } + +return rocker_port_fdb_learn (rocker_port, flags, addr, vlan_id); +} + +static int rocker_port_fdb_flush (struct rocker_port *rocker_port) +{ +struct rocker *rocker = rocker_port->rocker; +struct rocker_fdb_tbl_entry *found; +unsigned long lock_flags; +int flags =ROCKER_OP_FLAG_NOWAIT |ROCKER_OP_FLAG_REMOVE ; +struct hlist_node *tmp; +int bkt; +int err =0 ; + +if (rocker_port->stp_state ==BR_STATE_LEARNING || + rocker_port->stp_state ==BR_STATE_FORWARDING ) +return 0 ; + +spin_lock_irqsave (&rocker->fdb_tbl_lock, lock_flags); + +hash_for_each_safe (rocker->fdb_tbl, bkt, tmp, found, entry) { +if (found->key.lport != rocker_port->lport) +continue ; +if (!found->learned) +continue ; + err =rocker_port_fdb_learn (rocker_port, flags, + found->key.addr, + found->key.vlan_id); +if (err) + goto err_out; +hash_del (&found->entry); + } + +err_out: +spin_unlock_irqrestore (&rocker->fdb_tbl_lock, lock_flags); + +return err; +} +static int rocker_port_router_mac (struct rocker_port *rocker_port,int flags,__be16 vlan_id) {
@@ -2752,6 +3142,98 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port,return err; } +static int rocker_port_fwding (struct rocker_port *rocker_port) +{ +bool pop_vlan; +u32 out_lport; +__be16 vlan_id; +u16 vid; +int flags =ROCKER_OP_FLAG_NOWAIT ; +int err; + +/* Port will be forwarding-enabled if its STP state is LEARNING +* or FORWARDING. Traffic from CPU can still egress, regardless of +* port STP state. Use L2 interface group on port VLANs as a way +* to toggle port forwarding: if forwarding is disabled, L2 +* interface group will not exist. +*/ + +if (rocker_port->stp_state !=BR_STATE_LEARNING && + rocker_port->stp_state !=BR_STATE_FORWARDING ) + flags |=ROCKER_OP_FLAG_REMOVE ; + + out_lport = rocker_port->lport; +for (vid =1 ; vid <VLAN_N_VID ; vid++) { +if (!test_bit (vid, rocker_port->vlan_bitmap)) +continue ; + vlan_id =htons (vid); + pop_vlan =rocker_vlan_id_is_internal (vlan_id); + err =rocker_group_l2_interface (rocker_port, flags, + vlan_id, out_lport, + pop_vlan); +if (err) { +netdev_err (rocker_port->dev, +"Error (%d) port VLAN l2 group for lport %d\n" , + err, out_lport); +return err; + } + } + +return 0 ; +} + +static int rocker_port_stp_update (struct net_device *dev,u8 state) +{ +struct rocker_port *rocker_port =netdev_priv (dev); +bool want[ROCKER_CTRL_MAX ] = {0 , }; +int flags; +int err; +int i; + +if (rocker_port->stp_state == state) +return 0 ; + + rocker_port->stp_state = state; + +switch (state) { +case BR_STATE_DISABLED : +/* port is completely disabled */ +break ; +case BR_STATE_LISTENING : +case BR_STATE_BLOCKING : + want[ROCKER_CTRL_LINK_LOCAL_MCAST ] = true; +break ; +case BR_STATE_LEARNING : +case BR_STATE_FORWARDING : + want[ROCKER_CTRL_LINK_LOCAL_MCAST ] = true; + want[ROCKER_CTRL_IPV4_MCAST ] = true; + want[ROCKER_CTRL_IPV6_MCAST ] = true; +if (rocker_port_is_bridged (rocker_port)) + want[ROCKER_CTRL_DFLT_BRIDGING ] = true; +else + want[ROCKER_CTRL_LOCAL_ARP ] = true; +break ; + } + +for (i =0 ; i <ROCKER_CTRL_MAX ; i++) { +if (want[i] != rocker_port->ctrls[i]) { + flags =ROCKER_OP_FLAG_NOWAIT | + (want[i] ?0 :ROCKER_OP_FLAG_REMOVE ); + err =rocker_port_ctrl (rocker_port, flags, + &rocker_ctrls[i]); +if (err) +return err; + rocker_port->ctrls[i] = want[i]; + } + } + + err =rocker_port_fdb_flush (rocker_port); +if (err) +return err; + +return rocker_port_fwding (rocker_port); +} +static struct rocker_internal_vlan_tbl_entry *rocker_internal_vlan_tbl_find (struct rocker *rocker,int ifindex) {
@@ -2843,6 +3325,8 @@ not_found:static int rocker_port_open (struct net_device *dev) {struct rocker_port *rocker_port =netdev_priv (dev); +u8 stp_state =rocker_port_is_bridged (rocker_port) ? +BR_STATE_BLOCKING :BR_STATE_FORWARDING ;int err; err =rocker_port_dma_rings_init (rocker_port);
@@ -2865,12 +3349,18 @@ static int rocker_port_open(struct net_device *dev) goto err_request_rx_irq; } + err =rocker_port_stp_update (dev, stp_state); +if (err) + goto err_stp_update; +napi_enable (&rocker_port->napi_tx);napi_enable (&rocker_port->napi_rx);rocker_port_set_enable (rocker_port, true);netif_start_queue (dev);return 0 ; +err_stp_update: +free_irq (rocker_msix_rx_vector (rocker_port), rocker_port); err_request_rx_irq:free_irq (rocker_msix_tx_vector (rocker_port), rocker_port); err_request_tx_irq:
@@ -2886,6 +3376,7 @@ static int rocker_port_stop(struct net_device *dev)rocker_port_set_enable (rocker_port, false);napi_disable (&rocker_port->napi_rx);napi_disable (&rocker_port->napi_tx); +rocker_port_stp_update (dev,BR_STATE_DISABLED );free_irq (rocker_msix_rx_vector (rocker_port), rocker_port);free_irq (rocker_msix_tx_vector (rocker_port), rocker_port);rocker_port_dma_rings_fini (rocker_port);
@@ -3030,6 +3521,33 @@ static int rocker_port_set_mac_address(struct net_device *dev, void *p)return 0 ; } +static int rocker_port_vlan_rx_add_vid (struct net_device *dev, +__be16 proto,u16 vid) +{ +struct rocker_port *rocker_port =netdev_priv (dev); +int err; + + err =rocker_port_vlan (rocker_port,0 , vid); +if (err) +return err; + +return rocker_port_router_mac (rocker_port,0 ,htons (vid)); +} + +static int rocker_port_vlan_rx_kill_vid (struct net_device *dev, +__be16 proto,u16 vid) +{ +struct rocker_port *rocker_port =netdev_priv (dev); +int err; + + err =rocker_port_router_mac (rocker_port,ROCKER_OP_FLAG_REMOVE , +htons (vid)); +if (err) +return err; + +return rocker_port_vlan (rocker_port,ROCKER_OP_FLAG_REMOVE , vid); +} +static int rocker_port_sw_parent_id_get (struct net_device *dev,struct netdev_phys_item_id *psid) {
@@ -3041,13 +3559,49 @@ static int rocker_port_sw_parent_id_get(struct net_device *dev,return 0 ; } +static int rocker_port_sw_port_fdb_add (struct net_device *dev, +const unsigned char *addr,u16 vid) +{ +struct rocker_port *rocker_port =netdev_priv (dev); +__be16 vlan_id =rocker_port_vid_to_vlan (rocker_port, vid,NULL ); +int flags =0 ; + +if (!rocker_port_is_bridged (rocker_port)) +return -EINVAL ; + +return rocker_port_fdb (rocker_port, addr, vlan_id, flags); +} + +static int rocker_port_sw_port_fdb_del (struct net_device *dev, +const unsigned char *addr,u16 vid) +{ +struct rocker_port *rocker_port =netdev_priv (dev); +__be16 vlan_id =rocker_port_vid_to_vlan (rocker_port, vid,NULL ); +int flags =ROCKER_OP_FLAG_REMOVE ; + +if (!rocker_port_is_bridged (rocker_port)) +return -EINVAL ; + +return rocker_port_fdb (rocker_port, addr, vlan_id, flags); +} + +static int rocker_port_sw_port_stp_update (struct net_device *dev,u8 state) +{ +return rocker_port_stp_update (dev, state); +} +static const struct net_device_ops rocker_port_netdev_ops = { .ndo_open = rocker_port_open, .ndo_stop = rocker_port_stop, .ndo_start_xmit = rocker_port_xmit, .ndo_set_mac_address = rocker_port_set_mac_address, + .ndo_vlan_rx_add_vid = rocker_port_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = rocker_port_vlan_rx_kill_vid, #ifdef CONFIG_NET_SWITCHDEV .ndo_sw_parent_id_get = rocker_port_sw_parent_id_get, + .ndo_sw_port_fdb_add = rocker_port_sw_port_fdb_add, + .ndo_sw_port_fdb_del = rocker_port_sw_port_fdb_del, + .ndo_sw_port_stp_update = rocker_port_sw_port_stp_update, #endif };
@@ -3498,17 +4052,121 @@ static struct pci_driver rocker_pci_driver = { .remove = rocker_remove, }; +/************************************ +* Net device notifier event handler +************************************/ + +static bool rocker_port_dev_check (struct net_device *dev) +{ +return dev->netdev_ops == &rocker_port_netdev_ops; +} + +static int rocker_port_bridge_join (struct rocker_port *rocker_port, +struct net_device *bridge) +{ +int err; + +rocker_port_internal_vlan_id_put (rocker_port, + rocker_port->dev->ifindex); + + rocker_port->bridge_dev = bridge; + +/* Use bridge internal VLAN ID for untagged pkts */ + err =rocker_port_vlan (rocker_port,ROCKER_OP_FLAG_REMOVE ,0 ); +if (err) +return err; + rocker_port->internal_vlan_id = +rocker_port_internal_vlan_id_get (rocker_port, + bridge->ifindex); + err =rocker_port_vlan (rocker_port,0 ,0 ); + +return err; +} + +static int rocker_port_bridge_leave (struct rocker_port *rocker_port) +{ +int err; + +rocker_port_internal_vlan_id_put (rocker_port, + rocker_port->bridge_dev->ifindex); + + rocker_port->bridge_dev =NULL ; + +/* Use port internal VLAN ID for untagged pkts */ + err =rocker_port_vlan (rocker_port,ROCKER_OP_FLAG_REMOVE ,0 ); +if (err) +return err; + rocker_port->internal_vlan_id = +rocker_port_internal_vlan_id_get (rocker_port, + rocker_port->dev->ifindex); + err =rocker_port_vlan (rocker_port,0 ,0 ); + +return err; +} + +static int rocker_port_master_changed (struct net_device *dev) +{ +struct rocker_port *rocker_port =netdev_priv (dev); +struct net_device *master =netdev_master_upper_dev_get (dev); +int err =0 ; + +if (master && master->rtnl_link_ops && + !strcmp (master->rtnl_link_ops->kind,"bridge" )) + err =rocker_port_bridge_join (rocker_port, master); +else + err =rocker_port_bridge_leave (rocker_port); + +return err; +} + +static int rocker_netdevice_event (struct notifier_block *unused, +unsigned long event,void *ptr) +{ +struct net_device *dev; +int err; + +switch (event) { +case NETDEV_CHANGEUPPER : + dev =netdev_notifier_info_to_dev (ptr); +if (!rocker_port_dev_check (dev)) +return NOTIFY_DONE ; + err =rocker_port_master_changed (dev); +if (err) +netdev_warn (dev, +"failed to reflect master change (err %d)\n" , + err); +break ; + } + +return NOTIFY_DONE ; +} + +static struct notifier_block rocker_netdevice_nb__read_mostly = { + .notifier_call = rocker_netdevice_event, +}; +/*********************** * Module init and exit ***********************/ static int __init rocker_module_init (void ) { -return pci_register_driver (&rocker_pci_driver); +int err; + +register_netdevice_notifier (&rocker_netdevice_nb); + err =pci_register_driver (&rocker_pci_driver); +if (err) + goto err_pci_register_driver; +return 0 ; + +err_pci_register_driver: +unregister_netdevice_notifier (&rocker_netdevice_nb); +return err; } static void __exit rocker_module_exit(void) { +unregister_netdevice_notifier (&rocker_netdevice_nb);pci_unregister_driver (&rocker_pci_driver); }
Signature · 1 line
1.9.3
David Miller
maintainerRe: [patch net-next 10/10] rocker: implement L2 bridge offloading
From: Jiri Pirko <[email protected]>
Date: Thu, 6 Nov 2014 10:20:10 +0100
Quoted 1 line
unsigned
port_number;
"unsigned int"
drivers/net/ethernet/rocker/rocker.c static int rocker_event_link_change(struct rocker *rocker, · line 1329
Quoted 1 line
drivers/net/ethernet/rocker/rocker.c +1
-0+unsigned port_number;
Likewise.
Quoted 4 lines · Jiri Pirko
As to the discussion if there is need to have specific class of device representing the switch itself, so far we found no need to introduce that. But we are generally ok with the idea and when the time comes and it will be needed, it can be easily introduced without any disturbance.
I remember that concerns were raised in Duesseldorf and before that reusing net_device would imply the expectation of packet rx/tx on that net_device which might not necessarily be possible.
Whereas the model of representing the hardware switch with a combination of Linux bridge + switchdev class would ease fullfiling existing expecations towards net_device.
That said, I don't see why this shouldn't happen as an evolutionary step on top of these patches.
Jiri Pirko
maintainerQuoted 9 lines · Thu, Nov 06, 2014 at 03:11:30PM CET, [email protected] wrote:
Quoted 4 lines · Jiri Pirko
As to the discussion if there is need to have specific class of device representing the switch itself, so far we found no need to introduce that. But we are generally ok with the idea and when the time comes and it will be needed, it can be easily introduced without any disturbance.
I remember that concerns were raised in Duesseldorf and before that reusing
net_devicewould imply the expectation of packet rx/tx on thatnet_devicewhich might not necessarily be possible.
For switch object there was an agreement to not use net_device but do something similar to what wireless does. But this is not subject of this patchset.
For switch ports, I believe that there is a general agreement to represent them as net_devices. This patchset does that. Also, you are able to use this devs as ordinary NICs, set ip on each of tnem and rx/tx. But of course, the primary usecase is to put them in bridge.
Quoted 7 lines
Whereas the model of representing the hardware switch with a combination of Linux bridge + switchdev class would ease fullfiling existing expecations towards
net_device.That said, I don't see why this shouldn't happen as an evolutionary step on top of these patches.
I agree.