From dc0ecabd6231f6cec2493783a4111f7a85175c8a Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:10 +0200 Subject: [PATCH 01/19] net: switchdev: Add support for querying supported bridge flags by hardware This is done as a preparation stage before setting the bridge port flags from the bridge code. Currently the device can be queried for the bridge flags state, but the querier cannot distinguish if the flag is disabled or if it is not supported at all. Thus, add new attr and a bit-mask which include information regarding the support on a per-flag basis. Drivers that support bridge offload but not support bridge flags should return zeroed bitmask. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Reviewed-by: Ivan Vecera Signed-off-by: Jiri Pirko Reviewed-by: Ivan Vecera Signed-off-by: David S. Miller --- include/net/switchdev.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 929d6af321cd..63a754d4ff9b 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -46,6 +46,7 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_PORT_PARENT_ID, SWITCHDEV_ATTR_ID_PORT_STP_STATE, SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, + SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT, SWITCHDEV_ATTR_ID_PORT_MROUTER, SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, @@ -62,6 +63,7 @@ struct switchdev_attr { struct netdev_phys_item_id ppid; /* PORT_PARENT_ID */ u8 stp_state; /* PORT_STP_STATE */ unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */ + unsigned long brport_flags_support; /* PORT_BRIDGE_FLAGS_SUPPORT */ bool mrouter; /* PORT_MROUTER */ clock_t ageing_time; /* BRIDGE_AGEING_TIME */ bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */ From 3922285d96e79231817227439c214728edfbe406 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:11 +0200 Subject: [PATCH 02/19] net: bridge: Add support for offloading port attributes Currently the flood, learning and learning_sync port attributes are offloaded by setting the SELF flag. Add support for offloading the flood and learning attribute through the bridge code. In case of setting an unsupported flag on a offloded port the operation will fail. The learning_sync attribute doesn't have any software representation and cannot be offloaded through the bridge code. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Reviewed-by: Ivan Vecera Signed-off-by: David S. Miller --- net/bridge/br_netlink.c | 87 +++++++++++++++++++++++++++++---------- net/bridge/br_private.h | 10 +++++ net/bridge/br_switchdev.c | 43 +++++++++++++++++++ 3 files changed, 119 insertions(+), 21 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 3bcda556971e..63dca347b73b 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -662,16 +662,26 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state) } /* Set/clear or port flags based on attribute */ -static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], - int attrtype, unsigned long mask) +static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], + int attrtype, unsigned long mask) { - if (tb[attrtype]) { - u8 flag = nla_get_u8(tb[attrtype]); - if (flag) - p->flags |= mask; - else - p->flags &= ~mask; - } + unsigned long flags; + int err; + + if (!tb[attrtype]) + return 0; + + if (nla_get_u8(tb[attrtype])) + flags = p->flags | mask; + else + flags = p->flags & ~mask; + + err = br_switchdev_set_port_flag(p, flags, mask); + if (err) + return err; + + p->flags = flags; + return 0; } /* Process bridge protocol info on port */ @@ -681,20 +691,55 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) bool br_vlan_tunnel_old = false; int err; - br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); - br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); - br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE); - br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); - br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); - br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST); - br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); - br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); + err = br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); + if (err) + return err; + + err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); + if (err) + return err; br_vlan_tunnel_old = (p->flags & BR_VLAN_TUNNEL) ? true : false; - br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL); + err = br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL); + if (err) + return err; + if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL)) nbp_vlan_tunnel_info_flush(p); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 20626927f433..7f439927a66d 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1076,6 +1076,9 @@ void nbp_switchdev_frame_mark(const struct net_bridge_port *p, struct sk_buff *skb); bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, const struct sk_buff *skb); +int br_switchdev_set_port_flag(struct net_bridge_port *p, + unsigned long flags, + unsigned long mask); #else static inline int nbp_switchdev_mark_set(struct net_bridge_port *p) { @@ -1092,6 +1095,13 @@ static inline bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, { return true; } + +static inline int br_switchdev_set_port_flag(struct net_bridge_port *p, + unsigned long flags, + unsigned long mask) +{ + return 0; +} #endif /* CONFIG_NET_SWITCHDEV */ #endif diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index f4097b900de1..b975959ac15a 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -55,3 +55,46 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, return !skb->offload_fwd_mark || BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark; } + +/* Flags that can be offloaded to hardware */ +#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ + BR_MCAST_FLOOD | BR_BCAST_FLOOD) + +int br_switchdev_set_port_flag(struct net_bridge_port *p, + unsigned long flags, + unsigned long mask) +{ + struct switchdev_attr attr = { + .orig_dev = p->dev, + .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT, + }; + int err; + + if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD) + return 0; + + err = switchdev_port_attr_get(p->dev, &attr); + if (err == -EOPNOTSUPP) + return 0; + if (err) + return err; + + /* Check if specific bridge flag attribute offload is supported */ + if (!(attr.u.brport_flags_support & mask)) { + br_warn(p->br, "bridge flag offload is not supported %u(%s)\n", + (unsigned int)p->port_no, p->dev->name); + return -EOPNOTSUPP; + } + + attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; + attr.flags = SWITCHDEV_F_DEFER; + attr.u.brport_flags = flags; + err = switchdev_port_attr_set(p->dev, &attr); + if (err) { + br_warn(p->br, "error setting offload flag on port %u(%s)\n", + (unsigned int)p->port_no, p->dev->name); + return err; + } + + return 0; +} From 0baa10fff2c8a8a0e3bca8fb43112ed93c179c38 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:12 +0200 Subject: [PATCH 03/19] net: bridge: Add support for calling FDB external learning under rcu This is done as a preparation to moving the switchdev notifier chain to be atomic. The FDB external learning should be called under rtnl or rcu. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/bridge/br.c | 4 ++-- net/bridge/br_fdb.c | 2 -- net/bridge/br_private.h | 6 ++++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/bridge/br.c b/net/bridge/br.c index 889e5640455f..e962fff8c0d9 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -121,7 +121,7 @@ static struct notifier_block br_device_notifier = { .notifier_call = br_device_event }; -/* called with RTNL */ +/* called with RTNL or RCU */ static int br_switchdev_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -131,7 +131,7 @@ static int br_switchdev_event(struct notifier_block *unused, struct switchdev_notifier_fdb_info *fdb_info; int err = NOTIFY_DONE; - p = br_port_get_rtnl(dev); + p = br_port_get_rtnl_rcu(dev); if (!p) goto out; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index ab0c7cc8448f..5c780cdee93a 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -1075,7 +1075,6 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, struct net_bridge_fdb_entry *fdb; int err = 0; - ASSERT_RTNL(); spin_lock_bh(&br->hash_lock); head = &br->hash[br_mac_hash(addr, vid)]; @@ -1110,7 +1109,6 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, struct net_bridge_fdb_entry *fdb; int err = 0; - ASSERT_RTNL(); spin_lock_bh(&br->hash_lock); fdb = br_fdb_find(br, addr, vid); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 7f439927a66d..a122684b6a41 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -284,6 +284,12 @@ static inline struct net_bridge_port *br_port_get_rtnl(const struct net_device * rtnl_dereference(dev->rx_handler_data) : NULL; } +static inline struct net_bridge_port *br_port_get_rtnl_rcu(const struct net_device *dev) +{ + return br_port_exists(dev) ? + rcu_dereference_rtnl(dev->rx_handler_data) : NULL; +} + struct net_bridge { spinlock_t lock; spinlock_t hash_lock; From ff5cf100110c6ea722d63dfa3d747ceb179e8da4 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:13 +0200 Subject: [PATCH 04/19] net: switchdev: Change notifier chain to be atomic In order to use the switchdev notifier chain for FDB sync with the device it has to be changed to atomic. The is done because the bridge can learn new FDBs in atomic context. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Reviewed-by: Ivan Vecera Signed-off-by: Jiri Pirko Reviewed-by: Ivan Vecera Signed-off-by: David S. Miller --- net/switchdev/switchdev.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 8d40a7d31c99..25dc67ef9d37 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -571,24 +571,17 @@ int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj, } EXPORT_SYMBOL_GPL(switchdev_port_obj_dump); -static RAW_NOTIFIER_HEAD(switchdev_notif_chain); +static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); /** * register_switchdev_notifier - Register notifier * @nb: notifier_block * - * Register switch device notifier. This should be used by code - * which needs to monitor events happening in particular device. - * Return values are same as for atomic_notifier_chain_register(). + * Register switch device notifier. */ int register_switchdev_notifier(struct notifier_block *nb) { - int err; - - rtnl_lock(); - err = raw_notifier_chain_register(&switchdev_notif_chain, nb); - rtnl_unlock(); - return err; + return atomic_notifier_chain_register(&switchdev_notif_chain, nb); } EXPORT_SYMBOL_GPL(register_switchdev_notifier); @@ -597,16 +590,10 @@ EXPORT_SYMBOL_GPL(register_switchdev_notifier); * @nb: notifier_block * * Unregister switch device notifier. - * Return values are same as for atomic_notifier_chain_unregister(). */ int unregister_switchdev_notifier(struct notifier_block *nb) { - int err; - - rtnl_lock(); - err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb); - rtnl_unlock(); - return err; + return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb); } EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); @@ -616,18 +603,13 @@ EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); * @dev: port device * @info: notifier information data * - * Call all network notifier blocks. This should be called by driver - * when it needs to propagate hardware event. - * Return values are same as for atomic_notifier_call_chain(). - * rtnl_lock must be held. + * Call all network notifier blocks. */ int call_switchdev_notifiers(unsigned long val, struct net_device *dev, struct switchdev_notifier_info *info) { - ASSERT_RTNL(); - info->dev = dev; - return raw_notifier_call_chain(&switchdev_notif_chain, val, info); + return atomic_notifier_call_chain(&switchdev_notif_chain, val, info); } EXPORT_SYMBOL_GPL(call_switchdev_notifiers); From 6b26b51b1d13c62a09f55d745b06a8e964900715 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:14 +0200 Subject: [PATCH 05/19] net: bridge: Add support for notifying devices about FDB add/del Currently the bridge doesn't notify the underlying devices about new FDBs learned. The FDB sync is placed on the switchdev notifier chain because devices may potentially learn FDB that are not directly related to their ports, for example: 1. Mixed SW/HW bridge - FDBs that point to the ASICs external devices should be offloaded as CPU traps in order to perform forwarding in slow path. 2. EVPN - Externally learned FDBs for the vtep device. Notification is sent only about static FDB add/del. This is done due to fact that currently this is the only scenario supported by switch drivers. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Reviewed-by: Ivan Vecera Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 2 +- drivers/net/ethernet/rocker/rocker_ofdpa.c | 4 +-- include/net/switchdev.h | 6 ++-- net/bridge/br.c | 4 +-- net/bridge/br_fdb.c | 2 ++ net/bridge/br_private.h | 7 ++++ net/bridge/br_switchdev.c | 33 +++++++++++++++++++ 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index edcc273d7597..0111a77c36e8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1867,7 +1867,7 @@ static void mlxsw_sp_fdb_call_notifiers(bool learning_sync, bool adding, if (learning_sync) { info.addr = mac; info.vid = vid; - notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL; + notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; call_switchdev_notifiers(notifier_type, dev, &info.info); } } diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 2ae852454780..f659dad818e2 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1939,10 +1939,10 @@ static void ofdpa_port_fdb_learn_work(struct work_struct *work) rtnl_lock(); if (learned && removing) - call_switchdev_notifiers(SWITCHDEV_FDB_DEL, + call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, lw->ofdpa_port->dev, &info.info); else if (learned && !removing) - call_switchdev_notifiers(SWITCHDEV_FDB_ADD, + call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, lw->ofdpa_port->dev, &info.info); rtnl_unlock(); diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 63a754d4ff9b..8165ed93c58b 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -155,8 +155,10 @@ struct switchdev_ops { }; enum switchdev_notifier_type { - SWITCHDEV_FDB_ADD = 1, - SWITCHDEV_FDB_DEL, + SWITCHDEV_FDB_ADD_TO_BRIDGE = 1, + SWITCHDEV_FDB_DEL_TO_BRIDGE, + SWITCHDEV_FDB_ADD_TO_DEVICE, + SWITCHDEV_FDB_DEL_TO_DEVICE, }; struct switchdev_notifier_info { diff --git a/net/bridge/br.c b/net/bridge/br.c index e962fff8c0d9..96d209caf6db 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -138,14 +138,14 @@ static int br_switchdev_event(struct notifier_block *unused, br = p->br; switch (event) { - case SWITCHDEV_FDB_ADD: + case SWITCHDEV_FDB_ADD_TO_BRIDGE: fdb_info = ptr; err = br_fdb_external_learn_add(br, p, fdb_info->addr, fdb_info->vid); if (err) err = notifier_from_errno(err); break; - case SWITCHDEV_FDB_DEL: + case SWITCHDEV_FDB_DEL_TO_BRIDGE: fdb_info = ptr; err = br_fdb_external_learn_del(br, p, fdb_info->addr, fdb_info->vid); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 5c780cdee93a..26a1dae2d434 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -690,6 +690,8 @@ static void fdb_notify(struct net_bridge *br, struct sk_buff *skb; int err = -ENOBUFS; + br_switchdev_fdb_notify(fdb, type); + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); if (skb == NULL) goto errout; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a122684b6a41..98410ea032cb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1085,6 +1085,8 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, int br_switchdev_set_port_flag(struct net_bridge_port *p, unsigned long flags, unsigned long mask); +void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, + int type); #else static inline int nbp_switchdev_mark_set(struct net_bridge_port *p) { @@ -1108,6 +1110,11 @@ static inline int br_switchdev_set_port_flag(struct net_bridge_port *p, { return 0; } + +static inline void +br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) +{ +} #endif /* CONFIG_NET_SWITCHDEV */ #endif diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index b975959ac15a..181a44d0f1da 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -98,3 +98,36 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, return 0; } + +static void +br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, + u16 vid, struct net_device *dev) +{ + struct switchdev_notifier_fdb_info info; + unsigned long notifier_type; + + info.addr = mac; + info.vid = vid; + notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; + call_switchdev_notifiers(notifier_type, dev, &info.info); +} + +void +br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) +{ + if (!fdb->added_by_user) + return; + + switch (type) { + case RTM_DELNEIGH: + br_switchdev_fdb_call_notifiers(false, fdb->addr.addr, + fdb->vlan_id, + fdb->dst->dev); + break; + case RTM_NEWNEIGH: + br_switchdev_fdb_call_notifiers(true, fdb->addr.addr, + fdb->vlan_id, + fdb->dst->dev); + break; + } +} From 9fe8bcec0dbc19604acc3a2cd469febf96f0d59a Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:15 +0200 Subject: [PATCH 06/19] net: bridge: Receive notification about successful FDB offload When a new static FDB is added to the bridge a notification is sent to the driver for offload. In case of successful offload the driver should notify the bridge back, which in turn should mark the FDB as offloaded. Currently, externally learned is equivalent for being offloaded which is not correct due to the fact that FDBs which are added from user-space are also marked as externally learned. In order to specify if an FDB was successfully offloaded a new flag is introduced. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/switchdev.h | 1 + include/uapi/linux/neighbour.h | 1 + net/bridge/br.c | 11 ++++++++++- net/bridge/br_fdb.c | 22 +++++++++++++++++++++- net/bridge/br_private.h | 5 ++++- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 8165ed93c58b..c784a6ac6ef1 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -159,6 +159,7 @@ enum switchdev_notifier_type { SWITCHDEV_FDB_DEL_TO_BRIDGE, SWITCHDEV_FDB_ADD_TO_DEVICE, SWITCHDEV_FDB_DEL_TO_DEVICE, + SWITCHDEV_FDB_OFFLOADED, }; struct switchdev_notifier_info { diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index f3d16dbe09d6..3199d28980b3 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -41,6 +41,7 @@ enum { #define NTF_MASTER 0x04 #define NTF_PROXY 0x08 /* == ATF_PUBL */ #define NTF_EXT_LEARNED 0x10 +#define NTF_OFFLOADED 0x20 #define NTF_ROUTER 0x80 /* diff --git a/net/bridge/br.c b/net/bridge/br.c index 96d209caf6db..1407d1ba7577 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -142,8 +142,12 @@ static int br_switchdev_event(struct notifier_block *unused, fdb_info = ptr; err = br_fdb_external_learn_add(br, p, fdb_info->addr, fdb_info->vid); - if (err) + if (err) { err = notifier_from_errno(err); + break; + } + br_fdb_offloaded_set(br, p, fdb_info->addr, + fdb_info->vid); break; case SWITCHDEV_FDB_DEL_TO_BRIDGE: fdb_info = ptr; @@ -152,6 +156,11 @@ static int br_switchdev_event(struct notifier_block *unused, if (err) err = notifier_from_errno(err); break; + case SWITCHDEV_FDB_OFFLOADED: + fdb_info = ptr; + br_fdb_offloaded_set(br, p, fdb_info->addr, + fdb_info->vid); + break; } out: diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 26a1dae2d434..fef7872a320b 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -511,6 +511,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, fdb->is_static = is_static; fdb->added_by_user = 0; fdb->added_by_external_learn = 0; + fdb->offloaded = 0; fdb->updated = fdb->used = jiffies; hlist_add_head_rcu(&fdb->hlist, head); } @@ -647,11 +648,16 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ndm->ndm_family = AF_BRIDGE; ndm->ndm_pad1 = 0; ndm->ndm_pad2 = 0; - ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0; + ndm->ndm_flags = 0; ndm->ndm_type = 0; ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; ndm->ndm_state = fdb_to_nud(br, fdb); + if (fdb->offloaded) + ndm->ndm_flags |= NTF_OFFLOADED; + if (fdb->added_by_external_learn) + ndm->ndm_flags |= NTF_EXT_LEARNED; + if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr)) goto nla_put_failure; if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex)) @@ -1123,3 +1129,17 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, return err; } + +void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, + const unsigned char *addr, u16 vid) +{ + struct net_bridge_fdb_entry *fdb; + + spin_lock_bh(&br->hash_lock); + + fdb = br_fdb_find(br, addr, vid); + if (fdb) + fdb->offloaded = 1; + + spin_unlock_bh(&br->hash_lock); +} diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 98410ea032cb..c18682f804a0 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -169,7 +169,8 @@ struct net_bridge_fdb_entry { unsigned char is_local:1, is_static:1, added_by_user:1, - added_by_external_learn:1; + added_by_external_learn:1, + offloaded:1; /* write-heavy members should not affect lookups */ unsigned long updated ____cacheline_aligned_in_smp; @@ -536,6 +537,8 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid); int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid); +void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, + const unsigned char *addr, u16 vid); /* br_forward.c */ enum br_pkt_type { From a989cdb473c2fd9cd0187fa9f7da8e4a7c989332 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:16 +0200 Subject: [PATCH 07/19] mlxsw: spectrum: Remove support for bridge FDB learning sync Currently the mlxsw driver supports an option for disabling syncing the hardware learned FDBs with the software bridge. This behavior breaks the bridge offload model and thus it is removed. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 0111a77c36e8..eb88b721d2f7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1857,19 +1857,17 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); } -static void mlxsw_sp_fdb_call_notifiers(bool learning_sync, bool adding, +static void mlxsw_sp_fdb_call_notifiers(bool adding, char *mac, u16 vid, struct net_device *dev) { struct switchdev_notifier_fdb_info info; unsigned long notifier_type; - if (learning_sync) { - info.addr = mac; - info.vid = vid; - notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; - call_switchdev_notifiers(notifier_type, dev, &info.info); - } + info.addr = mac; + info.vid = vid; + notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; + call_switchdev_notifiers(notifier_type, dev, &info.info); } static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, @@ -1918,8 +1916,8 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, if (!do_notification) return; - mlxsw_sp_fdb_call_notifiers(bridge_port->flags & BR_LEARNING_SYNC, - adding, mac, vid, bridge_port->dev); + mlxsw_sp_fdb_call_notifiers(adding, mac, vid, bridge_port->dev); + return; just_remove: @@ -1976,8 +1974,8 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, if (!do_notification) return; - mlxsw_sp_fdb_call_notifiers(bridge_port->flags & BR_LEARNING_SYNC, - adding, mac, vid, bridge_port->dev); + mlxsw_sp_fdb_call_notifiers(adding, mac, vid, bridge_port->dev); + return; just_remove: From c7b566cd2e7433785c8879b004940de3793901e1 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:17 +0200 Subject: [PATCH 08/19] mlxsw: spectrum_switchdev: Add support for querying supported bridge flags Add support for querying supported bridge flags. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index eb88b721d2f7..033349364a6d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -456,6 +456,9 @@ static int mlxsw_sp_port_attr_get(struct net_device *dev, mlxsw_sp_port_bridge_flags_get(mlxsw_sp->bridge, attr->orig_dev, &attr->u.brport_flags); break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: + attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD; + break; default: return -EOPNOTSUPP; } From 10e23eb299fa39c6f343bffc9e3d77c7d9f0492f Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:18 +0200 Subject: [PATCH 09/19] mlxsw: spectrum: Remove support for bypass bridge port attributes/vlan set The bridge port attributes/vlan for mlxsw devices should be set only from bridge code. The vlans are synced totally with the bridge so there is no need to special dump support. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 3 -- .../mellanox/mlxsw/spectrum_switchdev.c | 41 ------------------- 2 files changed, 44 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index a2316d038810..db7127abce44 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1759,9 +1759,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { .ndo_fdb_add = switchdev_port_fdb_add, .ndo_fdb_del = switchdev_port_fdb_del, .ndo_fdb_dump = switchdev_port_fdb_dump, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_get_phys_port_name = mlxsw_sp_port_get_phys_port_name, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 033349364a6d..6257b0b8c8d1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -102,8 +102,6 @@ struct mlxsw_sp_bridge_vlan { struct list_head list; struct list_head port_vlan_list; u16 vid; - u8 egress_untagged:1, - pvid:1; }; struct mlxsw_sp_bridge_ops { @@ -1003,8 +1001,6 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, goto err_port_vlan_bridge_join; bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid); - bridge_vlan->egress_untagged = is_untagged; - bridge_vlan->pvid = is_pvid; return 0; @@ -1629,39 +1625,6 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, return stored_err ? stored_err : err; } -static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - struct net_device *orig_dev = vlan->obj.orig_dev; - struct mlxsw_sp_bridge_port *bridge_port; - struct mlxsw_sp_bridge_vlan *bridge_vlan; - int err = 0; - - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; - - if (!bridge_port->bridge_device->vlan_enabled) - return 0; - - list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) { - vlan->flags = 0; - if (bridge_vlan->pvid) - vlan->flags |= BRIDGE_VLAN_INFO_PVID; - if (bridge_vlan->egress_untagged) - vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; - vlan->vid_begin = bridge_vlan->vid; - vlan->vid_end = bridge_vlan->vid; - err = cb(&vlan->obj); - if (err) - break; - } - - return err; -} - static int mlxsw_sp_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj, switchdev_obj_dump_cb_t *cb) @@ -1670,10 +1633,6 @@ static int mlxsw_sp_port_obj_dump(struct net_device *dev, int err = 0; switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port, - SWITCHDEV_OBJ_PORT_VLAN(obj), cb); - break; case SWITCHDEV_OBJ_ID_PORT_FDB: err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_FDB(obj), cb); From 1b40dc3d86724c7f1e69d20020ffa4eb21d119ae Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:19 +0200 Subject: [PATCH 10/19] mlxsw: spectrum_switchdev: Change switchdev notifier API The current API for sending switchdev notifications implies only FDB add/del. In order to support notification about successful FDB offload the API is changed. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 6257b0b8c8d1..0b50d1d2b517 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1819,17 +1819,16 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); } -static void mlxsw_sp_fdb_call_notifiers(bool adding, - char *mac, u16 vid, - struct net_device *dev) +static void +mlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type, + const char *mac, u16 vid, + struct net_device *dev) { struct switchdev_notifier_fdb_info info; - unsigned long notifier_type; info.addr = mac; info.vid = vid; - notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; - call_switchdev_notifiers(notifier_type, dev, &info.info); + call_switchdev_notifiers(type, dev, &info.info); } static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, @@ -1840,6 +1839,7 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_bridge_port *bridge_port; struct mlxsw_sp_port *mlxsw_sp_port; + enum switchdev_notifier_type type; char mac[ETH_ALEN]; u8 local_port; u16 vid, fid; @@ -1878,7 +1878,8 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, if (!do_notification) return; - mlxsw_sp_fdb_call_notifiers(adding, mac, vid, bridge_port->dev); + type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; + mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev); return; @@ -1896,6 +1897,7 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_bridge_port *bridge_port; struct mlxsw_sp_port *mlxsw_sp_port; + enum switchdev_notifier_type type; char mac[ETH_ALEN]; u16 lag_vid = 0; u16 lag_id; @@ -1936,7 +1938,8 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, if (!do_notification) return; - mlxsw_sp_fdb_call_notifiers(adding, mac, vid, bridge_port->dev); + type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; + mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev); return; From af061378924f6d2f368b3769fd59fd95875dc942 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:20 +0200 Subject: [PATCH 11/19] mlxsw: spectrum_switchdev: Add support for learning FDB through notification Add support for learning FDB through notification. The driver defers the hardware update via ordered work queue. Support for stacked devices is also provided. In case of a successful FDB add a notification is sent back to bridge. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- .../net/ethernet/mellanox/mlxsw/spectrum.h | 1 + .../mellanox/mlxsw/spectrum_switchdev.c | 137 ++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index db7127abce44..581cb99daec6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3706,7 +3706,7 @@ struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) return mlxsw_sp_port ? mlxsw_sp_port->mlxsw_sp : NULL; } -static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev) +struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev) { struct mlxsw_sp_port *mlxsw_sp_port; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 4a7a39a9f1a1..5ef98d4d0ab6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -361,6 +361,7 @@ struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev); struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev); struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev); void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port); +struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev); /* spectrum_dcb.c */ #ifdef CONFIG_MLXSW_SPECTRUM_DCB diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 0b50d1d2b517..0297c136d040 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1181,6 +1181,43 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, true, false); } +static int +mlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_notifier_fdb_info *fdb_info, bool adding) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct net_device *orig_dev = fdb_info->info.dev; + struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; + struct mlxsw_sp_bridge_device *bridge_device; + struct mlxsw_sp_bridge_port *bridge_port; + u16 fid_index, vid; + + bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); + if (!bridge_port) + return -EINVAL; + + bridge_device = bridge_port->bridge_device; + mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, + bridge_device, + fdb_info->vid); + if (!mlxsw_sp_port_vlan) + return 0; + + fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); + vid = mlxsw_sp_port_vlan->vid; + + if (!bridge_port->lagged) + return mlxsw_sp_port_fdb_uc_op(mlxsw_sp, + bridge_port->system_port, + fdb_info->addr, fid_index, + adding, false); + else + return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, + bridge_port->lag_id, + fdb_info->addr, fid_index, + vid, adding, false); +} + static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, u16 fid, u16 mid, bool adding) { @@ -2013,6 +2050,97 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work) mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); } +struct mlxsw_sp_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct net_device *dev; + unsigned long event; +}; + +static void mlxsw_sp_switchdev_event_work(struct work_struct *work) +{ + struct mlxsw_sp_switchdev_event_work *switchdev_work = + container_of(work, struct mlxsw_sp_switchdev_event_work, work); + struct net_device *dev = switchdev_work->dev; + struct switchdev_notifier_fdb_info *fdb_info; + struct mlxsw_sp_port *mlxsw_sp_port; + int err; + + rtnl_lock(); + mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev); + if (!mlxsw_sp_port) + goto out; + + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true); + if (err) + break; + mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, + fdb_info->addr, + fdb_info->vid, dev); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false); + break; + } + +out: + rtnl_unlock(); + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(dev); +} + +/* Called under rcu_read_lock() */ +static int mlxsw_sp_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + struct mlxsw_sp_switchdev_event_work *switchdev_work; + struct switchdev_notifier_fdb_info *fdb_info = ptr; + + if (!mlxsw_sp_port_dev_lower_find_rcu(dev)) + return NOTIFY_DONE; + + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, mlxsw_sp_switchdev_event_work); + switchdev_work->dev = dev; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + /* Take a reference on the device. This can be either + * upper device containig mlxsw_sp_port or just a + * mlxsw_sp_port + */ + dev_hold(dev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + mlxsw_core_schedule_work(&switchdev_work->work); + + return NOTIFY_DONE; +} + +static struct notifier_block mlxsw_sp_switchdev_notifier = { + .notifier_call = mlxsw_sp_switchdev_event, +}; + static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; @@ -2023,6 +2151,13 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n"); return err; } + + err = register_switchdev_notifier(&mlxsw_sp_switchdev_notifier); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev notifier\n"); + return err; + } + INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work); bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); @@ -2032,6 +2167,8 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) { cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw); + unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); + } int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) From be7432b952bcbe2d7ac27a9f8545cce4cc74a4df Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:21 +0200 Subject: [PATCH 12/19] mlxsw: spectrum: Remove support for bridge bypass FDB add/del The FDB add/del are now done through the notification chain. The FDBs are synced with the bridge and there is no need for extra dumping. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 3 - .../mellanox/mlxsw/spectrum_switchdev.c | 200 ------------------ 2 files changed, 203 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 581cb99daec6..ecc73525529d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1756,9 +1756,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { .ndo_get_offload_stats = mlxsw_sp_port_get_offload_stats, .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_get_phys_port_name = mlxsw_sp_port_get_phys_port_name, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 0297c136d040..cd89a3e6cd81 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1140,47 +1140,6 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, return err; } -static int -mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - struct net_device *orig_dev = fdb->obj.orig_dev; - struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; - struct mlxsw_sp_bridge_device *bridge_device; - struct mlxsw_sp_bridge_port *bridge_port; - u16 fid_index, vid; - - if (switchdev_trans_ph_prepare(trans)) - return 0; - - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; - - bridge_device = bridge_port->bridge_device; - mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, - bridge_device, - fdb->vid); - if (!mlxsw_sp_port_vlan) - return 0; - - fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); - vid = mlxsw_sp_port_vlan->vid; - - if (!mlxsw_sp_port->lagged) - return mlxsw_sp_port_fdb_uc_op(mlxsw_sp, - mlxsw_sp_port->local_port, - fdb->addr, fid_index, true, - false); - else - return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, - mlxsw_sp_port->lag_id, - fdb->addr, fid_index, vid, - true, false); -} - static int mlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port, struct switchdev_notifier_fdb_info *fdb_info, bool adding) @@ -1385,11 +1344,6 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev, SWITCHDEV_OBJ_PORT_VLAN(obj), trans); break; - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = mlxsw_sp_port_fdb_static_add(mlxsw_sp_port, - SWITCHDEV_OBJ_PORT_FDB(obj), - trans); - break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = mlxsw_sp_port_mdb_add(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_MDB(obj), @@ -1441,43 +1395,6 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, return 0; } -static int -mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, - const struct switchdev_obj_port_fdb *fdb) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - struct net_device *orig_dev = fdb->obj.orig_dev; - struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; - struct mlxsw_sp_bridge_device *bridge_device; - struct mlxsw_sp_bridge_port *bridge_port; - u16 fid_index, vid; - - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; - - bridge_device = bridge_port->bridge_device; - mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, - bridge_device, - fdb->vid); - if (!mlxsw_sp_port_vlan) - return 0; - - fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); - vid = mlxsw_sp_port_vlan->vid; - - if (!mlxsw_sp_port->lagged) - return mlxsw_sp_port_fdb_uc_op(mlxsw_sp, - mlxsw_sp_port->local_port, - fdb->addr, fid_index, false, - false); - else - return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, - mlxsw_sp_port->lag_id, - fdb->addr, fid_index, vid, - false, false); -} - static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_mdb *mdb) { @@ -1537,10 +1454,6 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev, err = mlxsw_sp_port_vlans_del(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port, - SWITCHDEV_OBJ_PORT_FDB(obj)); - break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = mlxsw_sp_port_mdb_del(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_MDB(obj)); @@ -1570,124 +1483,11 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp, return NULL; } -static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - struct net_device *orig_dev = fdb->obj.orig_dev; - struct mlxsw_sp_bridge_port *bridge_port; - u16 lag_id, fid_index; - char mac[ETH_ALEN]; - int stored_err = 0; - char *sfd_pl; - u8 num_rec; - int err; - - bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); - if (!bridge_port) - return 0; - - sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); - if (!sfd_pl) - return -ENOMEM; - - mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0); - do { - struct mlxsw_sp_port *tmp; - u8 local_port; - int i; - - mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT); - err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); - if (err) - goto out; - - num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); - - /* Even in case of error, we have to run the dump to the end - * so the session in firmware is finished. - */ - if (stored_err) - continue; - - for (i = 0; i < num_rec; i++) { - switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) { - case MLXSW_REG_SFD_REC_TYPE_UNICAST: - mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, - &fid_index, - &local_port); - if (bridge_port->lagged) - continue; - if (bridge_port->system_port != local_port) - continue; - if (bridge_port->bridge_device->vlan_enabled) - fdb->vid = fid_index; - else - fdb->vid = 0; - ether_addr_copy(fdb->addr, mac); - fdb->ndm_state = NUD_REACHABLE; - err = cb(&fdb->obj); - if (err) - stored_err = err; - break; - case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG: - mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i, - mac, &fid_index, - &lag_id); - if (!bridge_port->lagged) - continue; - if (bridge_port->lag_id != lag_id) - continue; - tmp = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id); - if (tmp->local_port != - mlxsw_sp_port->local_port) - continue; - if (bridge_port->bridge_device->vlan_enabled) - fdb->vid = fid_index; - else - fdb->vid = 0; - ether_addr_copy(fdb->addr, mac); - fdb->ndm_state = NUD_REACHABLE; - err = cb(&fdb->obj); - if (err) - stored_err = err; - break; - } - } - } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT); - -out: - kfree(sfd_pl); - return stored_err ? stored_err : err; -} - -static int mlxsw_sp_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); - int err = 0; - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port, - SWITCHDEV_OBJ_PORT_FDB(obj), cb); - break; - default: - err = -EOPNOTSUPP; - break; - } - - return err; -} - static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = { .switchdev_port_attr_get = mlxsw_sp_port_attr_get, .switchdev_port_attr_set = mlxsw_sp_port_attr_set, .switchdev_port_obj_add = mlxsw_sp_port_obj_add, .switchdev_port_obj_del = mlxsw_sp_port_obj_del, - .switchdev_port_obj_dump = mlxsw_sp_port_obj_dump, }; static int From eca59f691566ca4dacfe78714108dd98043e3d0b Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:22 +0200 Subject: [PATCH 13/19] net: Remove support for bridge bypass ndos from stacked devices Remove support for bridge bypass ndos from stacked devices. At this point no driver which supports stack device behavior offload supports operation with SELF flag. The case for upper device is already taken care of in both of the following cases: 1. FDB add/del - driver should check at the notification cb if the stacked device contains his ports. 2. Port attribute - calls switchdev code directly which checks for case of stack device. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 ------ drivers/net/team/team.c | 6 ------ net/8021q/vlan_dev.c | 6 ------ 3 files changed, 18 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d4484d1a8164..7d9474352c36 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4175,12 +4175,6 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_add_slave = bond_enslave, .ndo_del_slave = bond_release, .ndo_fix_features = bond_fix_features, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_features_check = passthru_features_check, }; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 6c5d5ef46f75..a3ec1892a286 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2005,12 +2005,6 @@ static const struct net_device_ops team_netdev_ops = { .ndo_del_slave = team_del_slave, .ndo_fix_features = team_fix_features, .ndo_change_carrier = team_change_carrier, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_features_check = passthru_features_check, }; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 953b6728bd00..56d4b6977d03 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -797,12 +797,6 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, #endif .ndo_fix_features = vlan_dev_fix_features, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_get_lock_subclass = vlan_dev_get_lock_subclass, .ndo_get_iflink = vlan_dev_get_iflink, }; From 8cc186a4856b6ef4e54c3464e67ca2e1b79d20aa Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:23 +0200 Subject: [PATCH 14/19] rocker: Remove support for bridge FDB learning sync Currently the rocker driver supports an option for disabling syncing the hardware learned FDBs with the software bridge. This behavior breaks the bridge offload model and thus it is removed. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_ofdpa.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index f659dad818e2..0b5a58fee9bf 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1959,7 +1959,6 @@ static int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, u32 out_pport = ofdpa_port->pport; u32 tunnel_id = 0; u32 group_id = ROCKER_GROUP_NONE; - bool syncing = !!(ofdpa_port->brport_flags & BR_LEARNING_SYNC); bool copy_to_cpu = false; int err; @@ -1974,9 +1973,6 @@ static int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, return err; } - if (!syncing) - return 0; - if (!ofdpa_port_is_bridged(ofdpa_port)) return 0; @@ -2550,7 +2546,7 @@ static int ofdpa_port_pre_init(struct rocker_port *rocker_port) ofdpa_port->rocker_port = rocker_port; ofdpa_port->dev = rocker_port->dev; ofdpa_port->pport = rocker_port->pport; - ofdpa_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC; + ofdpa_port->brport_flags = BR_LEARNING; ofdpa_port->ageing_time = BR_DEFAULT_AGEING_TIME; return 0; } From 96673a30440a60559a63a16a6e7eb4ced02d7fe9 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:24 +0200 Subject: [PATCH 15/19] rocker: Add support for querying supported bridge flags Add support for querying supported bridge flags. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker.h | 4 ++++ drivers/net/ethernet/rocker/rocker_main.c | 18 ++++++++++++++++++ drivers/net/ethernet/rocker/rocker_ofdpa.c | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index ee9675db5bf9..c25e331f2061 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -112,6 +112,10 @@ struct rocker_world_ops { struct switchdev_trans *trans); int (*port_attr_bridge_flags_get)(const struct rocker_port *rocker_port, unsigned long *p_brport_flags); + int (*port_attr_bridge_flags_support_get)(const struct rocker_port * + rocker_port, + unsigned long * + p_brport_flags); int (*port_attr_bridge_ageing_time_set)(struct rocker_port *rocker_port, u32 ageing_time, struct switchdev_trans *trans); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index bab13613b138..a741e5163d04 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1584,6 +1584,20 @@ rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port, return wops->port_attr_bridge_flags_get(rocker_port, p_brport_flags); } +static int +rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port * + rocker_port, + unsigned long * + p_brport_flags_support) +{ + struct rocker_world_ops *wops = rocker_port->rocker->wops; + + if (!wops->port_attr_bridge_flags_support_get) + return -EOPNOTSUPP; + return wops->port_attr_bridge_flags_support_get(rocker_port, + p_brport_flags_support); +} + static int rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, u32 ageing_time, @@ -2053,6 +2067,10 @@ static int rocker_port_attr_get(struct net_device *dev, err = rocker_world_port_attr_bridge_flags_get(rocker_port, &attr->u.brport_flags); break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: + err = rocker_world_port_attr_bridge_flags_support_get(rocker_port, + &attr->u.brport_flags_support); + break; default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 0b5a58fee9bf..7528ee7453f5 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -2642,6 +2642,16 @@ ofdpa_port_attr_bridge_flags_get(const struct rocker_port *rocker_port, return 0; } +static int +ofdpa_port_attr_bridge_flags_support_get(const struct rocker_port * + rocker_port, + unsigned long * + p_brport_flags_support) +{ + *p_brport_flags_support = BR_LEARNING; + return 0; +} + static int ofdpa_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, u32 ageing_time, @@ -2989,6 +2999,7 @@ struct rocker_world_ops rocker_ofdpa_ops = { .port_attr_stp_state_set = ofdpa_port_attr_stp_state_set, .port_attr_bridge_flags_set = ofdpa_port_attr_bridge_flags_set, .port_attr_bridge_flags_get = ofdpa_port_attr_bridge_flags_get, + .port_attr_bridge_flags_support_get = ofdpa_port_attr_bridge_flags_support_get, .port_attr_bridge_ageing_time_set = ofdpa_port_attr_bridge_ageing_time_set, .port_obj_vlan_add = ofdpa_port_obj_vlan_add, .port_obj_vlan_del = ofdpa_port_obj_vlan_del, From 00fc0c51e35b783f6542d14a30a5de1ce6549887 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:25 +0200 Subject: [PATCH 16/19] rocker: Change world_ops API and implementation to be switchdev independant Currently the switchdev_trans struct is embedded in the world_ops API. In order to add support for adding FDB via a notfication chain the API should be switchdev independent. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker.h | 11 +- drivers/net/ethernet/rocker/rocker_main.c | 28 +- drivers/net/ethernet/rocker/rocker_ofdpa.c | 535 ++++++++------------- 3 files changed, 235 insertions(+), 339 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index c25e331f2061..a0f7782ddfdd 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -105,8 +105,7 @@ struct rocker_world_ops { int (*port_open)(struct rocker_port *rocker_port); void (*port_stop)(struct rocker_port *rocker_port); int (*port_attr_stp_state_set)(struct rocker_port *rocker_port, - u8 state, - struct switchdev_trans *trans); + u8 state); int (*port_attr_bridge_flags_set)(struct rocker_port *rocker_port, unsigned long brport_flags, struct switchdev_trans *trans); @@ -120,18 +119,16 @@ struct rocker_world_ops { u32 ageing_time, struct switchdev_trans *trans); int (*port_obj_vlan_add)(struct rocker_port *rocker_port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans); + const struct switchdev_obj_port_vlan *vlan); int (*port_obj_vlan_del)(struct rocker_port *rocker_port, const struct switchdev_obj_port_vlan *vlan); int (*port_obj_vlan_dump)(const struct rocker_port *rocker_port, struct switchdev_obj_port_vlan *vlan, switchdev_obj_dump_cb_t *cb); int (*port_obj_fdb_add)(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + u16 vid, const unsigned char *addr); int (*port_obj_fdb_del)(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb); + u16 vid, const unsigned char *addr); int (*port_obj_fdb_dump)(const struct rocker_port *rocker_port, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index a741e5163d04..9f0154dc2cf7 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1557,7 +1557,11 @@ static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port, if (!wops->port_attr_stp_state_set) return -EOPNOTSUPP; - return wops->port_attr_stp_state_set(rocker_port, state, trans); + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return wops->port_attr_stp_state_set(rocker_port, state); } static int @@ -1569,6 +1573,10 @@ rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, if (!wops->port_attr_bridge_flags_set) return -EOPNOTSUPP; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + return wops->port_attr_bridge_flags_set(rocker_port, brport_flags, trans); } @@ -1608,6 +1616,10 @@ rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, if (!wops->port_attr_bridge_ageing_time_set) return -EOPNOTSUPP; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time, trans); } @@ -1621,7 +1633,11 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, if (!wops->port_obj_vlan_add) return -EOPNOTSUPP; - return wops->port_obj_vlan_add(rocker_port, vlan, trans); + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return wops->port_obj_vlan_add(rocker_port, vlan); } static int @@ -1656,7 +1672,11 @@ rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port, if (!wops->port_obj_fdb_add) return -EOPNOTSUPP; - return wops->port_obj_fdb_add(rocker_port, fdb, trans); + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return wops->port_obj_fdb_add(rocker_port, fdb->vid, fdb->addr); } static int @@ -1667,7 +1687,7 @@ rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port, if (!wops->port_obj_fdb_del) return -EOPNOTSUPP; - return wops->port_obj_fdb_del(rocker_port, fdb); + return wops->port_obj_fdb_del(rocker_port, fdb->vid, fdb->addr); } static int diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 7528ee7453f5..184a478fadab 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -300,64 +300,6 @@ static bool ofdpa_flags_nowait(int flags) return flags & OFDPA_OP_FLAG_NOWAIT; } -static void *__ofdpa_mem_alloc(struct switchdev_trans *trans, int flags, - size_t size) -{ - struct switchdev_trans_item *elem = NULL; - gfp_t gfp_flags = (flags & OFDPA_OP_FLAG_NOWAIT) ? - GFP_ATOMIC : GFP_KERNEL; - - /* If in transaction prepare phase, allocate the memory - * and enqueue it on a transaction. If in transaction - * commit phase, dequeue the memory from the transaction - * rather than re-allocating the memory. The idea is the - * driver code paths for prepare and commit are identical - * so the memory allocated in the prepare phase is the - * memory used in the commit phase. - */ - - if (!trans) { - elem = kzalloc(size + sizeof(*elem), gfp_flags); - } else if (switchdev_trans_ph_prepare(trans)) { - elem = kzalloc(size + sizeof(*elem), gfp_flags); - if (!elem) - return NULL; - switchdev_trans_item_enqueue(trans, elem, kfree, elem); - } else { - elem = switchdev_trans_item_dequeue(trans); - } - - return elem ? elem + 1 : NULL; -} - -static void *ofdpa_kzalloc(struct switchdev_trans *trans, int flags, - size_t size) -{ - return __ofdpa_mem_alloc(trans, flags, size); -} - -static void *ofdpa_kcalloc(struct switchdev_trans *trans, int flags, - size_t n, size_t size) -{ - return __ofdpa_mem_alloc(trans, flags, n * size); -} - -static void ofdpa_kfree(struct switchdev_trans *trans, const void *mem) -{ - struct switchdev_trans_item *elem; - - /* Frees are ignored if in transaction prepare phase. The - * memory remains on the per-port list until freed in the - * commit phase. - */ - - if (switchdev_trans_ph_prepare(trans)) - return; - - elem = (struct switchdev_trans_item *) mem - 1; - kfree(elem); -} - /************************************************************* * Flow, group, FDB, internal VLAN and neigh command prepares *************************************************************/ @@ -815,8 +757,7 @@ ofdpa_flow_tbl_find(const struct ofdpa *ofdpa, } static int ofdpa_flow_tbl_add(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - struct ofdpa_flow_tbl_entry *match) + int flags, struct ofdpa_flow_tbl_entry *match) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; struct ofdpa_flow_tbl_entry *found; @@ -831,9 +772,8 @@ static int ofdpa_flow_tbl_add(struct ofdpa_port *ofdpa_port, if (found) { match->cookie = found->cookie; - if (!switchdev_trans_ph_prepare(trans)) - hash_del(&found->entry); - ofdpa_kfree(trans, found); + hash_del(&found->entry); + kfree(found); found = match; found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD; } else { @@ -842,22 +782,18 @@ static int ofdpa_flow_tbl_add(struct ofdpa_port *ofdpa_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD; } - if (!switchdev_trans_ph_prepare(trans)) - hash_add(ofdpa->flow_tbl, &found->entry, found->key_crc32); - + hash_add(ofdpa->flow_tbl, &found->entry, found->key_crc32); spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, lock_flags); - if (!switchdev_trans_ph_prepare(trans)) - return rocker_cmd_exec(ofdpa_port->rocker_port, - ofdpa_flags_nowait(flags), - ofdpa_cmd_flow_tbl_add, - found, NULL, NULL); + return rocker_cmd_exec(ofdpa_port->rocker_port, + ofdpa_flags_nowait(flags), + ofdpa_cmd_flow_tbl_add, + found, NULL, NULL); return 0; } static int ofdpa_flow_tbl_del(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - struct ofdpa_flow_tbl_entry *match) + int flags, struct ofdpa_flow_tbl_entry *match) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; struct ofdpa_flow_tbl_entry *found; @@ -872,45 +808,41 @@ static int ofdpa_flow_tbl_del(struct ofdpa_port *ofdpa_port, found = ofdpa_flow_tbl_find(ofdpa, match); if (found) { - if (!switchdev_trans_ph_prepare(trans)) - hash_del(&found->entry); + hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL; } spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, lock_flags); - ofdpa_kfree(trans, match); + kfree(match); if (found) { - if (!switchdev_trans_ph_prepare(trans)) - err = rocker_cmd_exec(ofdpa_port->rocker_port, - ofdpa_flags_nowait(flags), - ofdpa_cmd_flow_tbl_del, - found, NULL, NULL); - ofdpa_kfree(trans, found); + err = rocker_cmd_exec(ofdpa_port->rocker_port, + ofdpa_flags_nowait(flags), + ofdpa_cmd_flow_tbl_del, + found, NULL, NULL); + kfree(found); } return err; } -static int ofdpa_flow_tbl_do(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_flow_tbl_do(struct ofdpa_port *ofdpa_port, int flags, struct ofdpa_flow_tbl_entry *entry) { if (flags & OFDPA_OP_FLAG_REMOVE) - return ofdpa_flow_tbl_del(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_del(ofdpa_port, flags, entry); else - return ofdpa_flow_tbl_add(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_add(ofdpa_port, flags, entry); } -static int ofdpa_flow_tbl_ig_port(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_flow_tbl_ig_port(struct ofdpa_port *ofdpa_port, int flags, u32 in_pport, u32 in_pport_mask, enum rocker_of_dpa_table_id goto_tbl) { struct ofdpa_flow_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -920,11 +852,11 @@ static int ofdpa_flow_tbl_ig_port(struct ofdpa_port *ofdpa_port, entry->key.ig_port.in_pport_mask = in_pport_mask; entry->key.ig_port.goto_tbl = goto_tbl; - return ofdpa_flow_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); } static int ofdpa_flow_tbl_vlan(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, + int flags, u32 in_pport, __be16 vlan_id, __be16 vlan_id_mask, enum rocker_of_dpa_table_id goto_tbl, @@ -932,7 +864,7 @@ static int ofdpa_flow_tbl_vlan(struct ofdpa_port *ofdpa_port, { struct ofdpa_flow_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -946,11 +878,10 @@ static int ofdpa_flow_tbl_vlan(struct ofdpa_port *ofdpa_port, entry->key.vlan.untagged = untagged; entry->key.vlan.new_vlan_id = new_vlan_id; - return ofdpa_flow_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); } static int ofdpa_flow_tbl_term_mac(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, u32 in_pport, u32 in_pport_mask, __be16 eth_type, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, @@ -959,7 +890,7 @@ static int ofdpa_flow_tbl_term_mac(struct ofdpa_port *ofdpa_port, { struct ofdpa_flow_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -983,13 +914,13 @@ static int ofdpa_flow_tbl_term_mac(struct ofdpa_port *ofdpa_port, entry->key.term_mac.vlan_id_mask = vlan_id_mask; entry->key.term_mac.copy_to_cpu = copy_to_cpu; - return ofdpa_flow_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); } static int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - const u8 *eth_dst, const u8 *eth_dst_mask, - __be16 vlan_id, u32 tunnel_id, + 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) { @@ -999,7 +930,7 @@ static int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port, bool dflt = !eth_dst || (eth_dst && eth_dst_mask); bool wild = false; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return -ENOMEM; @@ -1037,11 +968,10 @@ static int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port, entry->key.bridge.group_id = group_id; entry->key.bridge.copy_to_cpu = copy_to_cpu; - return ofdpa_flow_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); } static int ofdpa_flow_tbl_ucast4_routing(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, __be16 eth_type, __be32 dst, __be32 dst_mask, u32 priority, enum rocker_of_dpa_table_id goto_tbl, @@ -1050,7 +980,7 @@ static int ofdpa_flow_tbl_ucast4_routing(struct ofdpa_port *ofdpa_port, { struct ofdpa_flow_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -1065,11 +995,10 @@ static int ofdpa_flow_tbl_ucast4_routing(struct ofdpa_port *ofdpa_port, ucast_routing.group_id); entry->fi = fi; - return ofdpa_flow_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); } -static int ofdpa_flow_tbl_acl(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_flow_tbl_acl(struct ofdpa_port *ofdpa_port, int flags, u32 in_pport, u32 in_pport_mask, const u8 *eth_src, const u8 *eth_src_mask, const u8 *eth_dst, const u8 *eth_dst_mask, @@ -1081,7 +1010,7 @@ static int ofdpa_flow_tbl_acl(struct ofdpa_port *ofdpa_port, u32 priority; struct ofdpa_flow_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -1116,7 +1045,7 @@ static int ofdpa_flow_tbl_acl(struct ofdpa_port *ofdpa_port, entry->key.acl.ip_tos_mask = ip_tos_mask; entry->key.acl.group_id = group_id; - return ofdpa_flow_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); } static struct ofdpa_group_tbl_entry * @@ -1134,22 +1063,20 @@ ofdpa_group_tbl_find(const struct ofdpa *ofdpa, return NULL; } -static void ofdpa_group_tbl_entry_free(struct switchdev_trans *trans, - struct ofdpa_group_tbl_entry *entry) +static void ofdpa_group_tbl_entry_free(struct ofdpa_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: - ofdpa_kfree(trans, entry->group_ids); + kfree(entry->group_ids); break; default: break; } - ofdpa_kfree(trans, entry); + kfree(entry); } -static int ofdpa_group_tbl_add(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_group_tbl_add(struct ofdpa_port *ofdpa_port, int flags, struct ofdpa_group_tbl_entry *match) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; @@ -1161,9 +1088,8 @@ static int ofdpa_group_tbl_add(struct ofdpa_port *ofdpa_port, found = ofdpa_group_tbl_find(ofdpa, match); if (found) { - if (!switchdev_trans_ph_prepare(trans)) - hash_del(&found->entry); - ofdpa_group_tbl_entry_free(trans, found); + hash_del(&found->entry); + ofdpa_group_tbl_entry_free(found); found = match; found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD; } else { @@ -1171,21 +1097,17 @@ static int ofdpa_group_tbl_add(struct ofdpa_port *ofdpa_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD; } - if (!switchdev_trans_ph_prepare(trans)) - hash_add(ofdpa->group_tbl, &found->entry, found->group_id); + hash_add(ofdpa->group_tbl, &found->entry, found->group_id); spin_unlock_irqrestore(&ofdpa->group_tbl_lock, lock_flags); - if (!switchdev_trans_ph_prepare(trans)) - return rocker_cmd_exec(ofdpa_port->rocker_port, - ofdpa_flags_nowait(flags), - ofdpa_cmd_group_tbl_add, - found, NULL, NULL); - return 0; + return rocker_cmd_exec(ofdpa_port->rocker_port, + ofdpa_flags_nowait(flags), + ofdpa_cmd_group_tbl_add, + found, NULL, NULL); } -static int ofdpa_group_tbl_del(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_group_tbl_del(struct ofdpa_port *ofdpa_port, int flags, struct ofdpa_group_tbl_entry *match) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; @@ -1198,97 +1120,90 @@ static int ofdpa_group_tbl_del(struct ofdpa_port *ofdpa_port, found = ofdpa_group_tbl_find(ofdpa, match); if (found) { - if (!switchdev_trans_ph_prepare(trans)) - hash_del(&found->entry); + hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL; } spin_unlock_irqrestore(&ofdpa->group_tbl_lock, lock_flags); - ofdpa_group_tbl_entry_free(trans, match); + ofdpa_group_tbl_entry_free(match); if (found) { - if (!switchdev_trans_ph_prepare(trans)) - err = rocker_cmd_exec(ofdpa_port->rocker_port, - ofdpa_flags_nowait(flags), - ofdpa_cmd_group_tbl_del, - found, NULL, NULL); - ofdpa_group_tbl_entry_free(trans, found); + err = rocker_cmd_exec(ofdpa_port->rocker_port, + ofdpa_flags_nowait(flags), + ofdpa_cmd_group_tbl_del, + found, NULL, NULL); + ofdpa_group_tbl_entry_free(found); } return err; } -static int ofdpa_group_tbl_do(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_group_tbl_do(struct ofdpa_port *ofdpa_port, int flags, struct ofdpa_group_tbl_entry *entry) { if (flags & OFDPA_OP_FLAG_REMOVE) - return ofdpa_group_tbl_del(ofdpa_port, trans, flags, entry); + return ofdpa_group_tbl_del(ofdpa_port, flags, entry); else - return ofdpa_group_tbl_add(ofdpa_port, trans, flags, entry); + return ofdpa_group_tbl_add(ofdpa_port, flags, entry); } static int ofdpa_group_l2_interface(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - __be16 vlan_id, u32 out_pport, - int pop_vlan) + int flags, __be16 vlan_id, + u32 out_pport, int pop_vlan) { struct ofdpa_group_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); entry->l2_interface.pop_vlan = pop_vlan; - return ofdpa_group_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_group_tbl_do(ofdpa_port, flags, entry); } static int ofdpa_group_l2_fan_out(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, u8 group_count, const u32 *group_ids, u32 group_id) { struct ofdpa_group_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->group_id = group_id; entry->group_count = group_count; - entry->group_ids = ofdpa_kcalloc(trans, flags, - group_count, sizeof(u32)); + entry->group_ids = kcalloc(flags, group_count, sizeof(u32)); if (!entry->group_ids) { - ofdpa_kfree(trans, entry); + kfree(entry); return -ENOMEM; } memcpy(entry->group_ids, group_ids, group_count * sizeof(u32)); - return ofdpa_group_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_group_tbl_do(ofdpa_port, flags, entry); } static int ofdpa_group_l2_flood(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - __be16 vlan_id, u8 group_count, - const u32 *group_ids, u32 group_id) + int flags, __be16 vlan_id, + u8 group_count, const u32 *group_ids, + u32 group_id) { - return ofdpa_group_l2_fan_out(ofdpa_port, trans, flags, + return ofdpa_group_l2_fan_out(ofdpa_port, flags, group_count, group_ids, group_id); } -static int ofdpa_group_l3_unicast(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_group_l3_unicast(struct ofdpa_port *ofdpa_port, int flags, u32 index, const u8 *src_mac, const u8 *dst_mac, __be16 vlan_id, bool ttl_check, u32 pport) { struct ofdpa_group_tbl_entry *entry; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -1301,7 +1216,7 @@ static int ofdpa_group_l3_unicast(struct ofdpa_port *ofdpa_port, entry->l3_unicast.ttl_check = ttl_check; entry->l3_unicast.group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, pport); - return ofdpa_group_tbl_do(ofdpa_port, trans, flags, entry); + return ofdpa_group_tbl_do(ofdpa_port, flags, entry); } static struct ofdpa_neigh_tbl_entry * @@ -1318,43 +1233,34 @@ ofdpa_neigh_tbl_find(const struct ofdpa *ofdpa, __be32 ip_addr) } static void ofdpa_neigh_add(struct ofdpa *ofdpa, - struct switchdev_trans *trans, struct ofdpa_neigh_tbl_entry *entry) { - if (!switchdev_trans_ph_commit(trans)) - entry->index = ofdpa->neigh_tbl_next_index++; - if (switchdev_trans_ph_prepare(trans)) - return; + entry->index = ofdpa->neigh_tbl_next_index++; entry->ref_count++; hash_add(ofdpa->neigh_tbl, &entry->entry, be32_to_cpu(entry->ip_addr)); } -static void ofdpa_neigh_del(struct switchdev_trans *trans, - struct ofdpa_neigh_tbl_entry *entry) +static void ofdpa_neigh_del(struct ofdpa_neigh_tbl_entry *entry) { - if (switchdev_trans_ph_prepare(trans)) - return; if (--entry->ref_count == 0) { hash_del(&entry->entry); - ofdpa_kfree(trans, entry); + kfree(entry); } } static void ofdpa_neigh_update(struct ofdpa_neigh_tbl_entry *entry, - struct switchdev_trans *trans, const u8 *eth_dst, bool ttl_check) { if (eth_dst) { ether_addr_copy(entry->eth_dst, eth_dst); entry->ttl_check = ttl_check; - } else if (!switchdev_trans_ph_prepare(trans)) { + } else { entry->ref_count++; } } static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, __be32 ip_addr, const u8 *eth_dst) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; @@ -1371,7 +1277,7 @@ static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, bool removing; int err = 0; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -1388,12 +1294,12 @@ static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, entry->dev = ofdpa_port->dev; ether_addr_copy(entry->eth_dst, eth_dst); entry->ttl_check = true; - ofdpa_neigh_add(ofdpa, trans, entry); + ofdpa_neigh_add(ofdpa, entry); } else if (removing) { memcpy(entry, found, sizeof(*entry)); - ofdpa_neigh_del(trans, found); + ofdpa_neigh_del(found); } else if (updating) { - ofdpa_neigh_update(found, trans, eth_dst, true); + ofdpa_neigh_update(found, eth_dst, true); memcpy(entry, found, sizeof(*entry)); } else { err = -ENOENT; @@ -1410,7 +1316,7 @@ static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, * other routes' nexthops. */ - err = ofdpa_group_l3_unicast(ofdpa_port, trans, flags, + err = ofdpa_group_l3_unicast(ofdpa_port, flags, entry->index, ofdpa_port->dev->dev_addr, entry->eth_dst, @@ -1425,7 +1331,7 @@ static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, if (adding || removing) { group_id = ROCKER_GROUP_L3_UNICAST(entry->index); - err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, trans, + err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, eth_type, ip_addr, inet_make_mask(32), priority, goto_tbl, @@ -1438,13 +1344,12 @@ static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, err_out: if (!adding) - ofdpa_kfree(trans, entry); + kfree(entry); return err; } static int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, __be32 ip_addr) { struct net_device *dev = ofdpa_port->dev; @@ -1463,7 +1368,7 @@ static int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port, */ if (n->nud_state & NUD_VALID) - err = ofdpa_port_ipv4_neigh(ofdpa_port, trans, 0, + err = ofdpa_port_ipv4_neigh(ofdpa_port, 0, ip_addr, n->ha); else neigh_event_send(n, NULL); @@ -1473,8 +1378,7 @@ static int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port, } static int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - __be32 ip_addr, u32 *index) + int flags, __be32 ip_addr, u32 *index) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; struct ofdpa_neigh_tbl_entry *entry; @@ -1486,7 +1390,7 @@ static int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port, bool resolved = true; int err = 0; - entry = ofdpa_kzalloc(trans, flags, sizeof(*entry)); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -1501,14 +1405,14 @@ static int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port, if (adding) { entry->ip_addr = ip_addr; entry->dev = ofdpa_port->dev; - ofdpa_neigh_add(ofdpa, trans, entry); + ofdpa_neigh_add(ofdpa, entry); *index = entry->index; resolved = false; } else if (removing) { - ofdpa_neigh_del(trans, found); + ofdpa_neigh_del(found); *index = found->index; } else if (updating) { - ofdpa_neigh_update(found, trans, NULL, false); + ofdpa_neigh_update(found, NULL, false); resolved = !is_zero_ether_addr(found->eth_dst); *index = found->index; } else { @@ -1518,7 +1422,7 @@ static int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port, spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, lock_flags); if (!adding) - ofdpa_kfree(trans, entry); + kfree(entry); if (err) return err; @@ -1526,7 +1430,7 @@ static int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port, /* Resolved means neigh ip_addr is resolved to neigh mac. */ if (!resolved) - err = ofdpa_port_ipv4_resolve(ofdpa_port, trans, ip_addr); + err = ofdpa_port_ipv4_resolve(ofdpa_port, ip_addr); return err; } @@ -1541,7 +1445,6 @@ static struct ofdpa_port *ofdpa_port_get(const struct ofdpa *ofdpa, } static int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, __be16 vlan_id) { struct ofdpa_port *p; @@ -1553,7 +1456,7 @@ static int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port, int err = 0; int i; - group_ids = ofdpa_kcalloc(trans, flags, port_count, sizeof(u32)); + group_ids = kcalloc(flags, port_count, sizeof(u32)); if (!group_ids) return -ENOMEM; @@ -1578,18 +1481,17 @@ static int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port, if (group_count == 0) goto no_ports_in_vlan; - err = ofdpa_group_l2_flood(ofdpa_port, trans, flags, vlan_id, + err = ofdpa_group_l2_flood(ofdpa_port, flags, vlan_id, group_count, group_ids, group_id); if (err) netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 flood group\n", err); no_ports_in_vlan: - ofdpa_kfree(trans, group_ids); + kfree(group_ids); return err; } -static int ofdpa_port_vlan_l2_groups(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_port_vlan_l2_groups(struct ofdpa_port *ofdpa_port, int flags, __be16 vlan_id, bool pop_vlan) { const struct ofdpa *ofdpa = ofdpa_port->ofdpa; @@ -1608,7 +1510,7 @@ static int ofdpa_port_vlan_l2_groups(struct ofdpa_port *ofdpa_port, if (ofdpa_port->stp_state == BR_STATE_LEARNING || ofdpa_port->stp_state == BR_STATE_FORWARDING) { out_pport = ofdpa_port->pport; - err = ofdpa_group_l2_interface(ofdpa_port, trans, flags, + err = ofdpa_group_l2_interface(ofdpa_port, flags, vlan_id, out_pport, pop_vlan); if (err) { netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for pport %d\n", @@ -1632,7 +1534,7 @@ static int ofdpa_port_vlan_l2_groups(struct ofdpa_port *ofdpa_port, return 0; out_pport = 0; - err = ofdpa_group_l2_interface(ofdpa_port, trans, flags, + err = ofdpa_group_l2_interface(ofdpa_port, flags, vlan_id, out_pport, pop_vlan); if (err) { netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for CPU port\n", err); @@ -1693,8 +1595,7 @@ static struct ofdpa_ctrl { }, }; -static int ofdpa_port_ctrl_vlan_acl(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_port_ctrl_vlan_acl(struct ofdpa_port *ofdpa_port, int flags, const struct ofdpa_ctrl *ctrl, __be16 vlan_id) { u32 in_pport = ofdpa_port->pport; @@ -1710,7 +1611,7 @@ static int ofdpa_port_ctrl_vlan_acl(struct ofdpa_port *ofdpa_port, u32 group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); int err; - err = ofdpa_flow_tbl_acl(ofdpa_port, trans, flags, + err = ofdpa_flow_tbl_acl(ofdpa_port, flags, in_pport, in_pport_mask, eth_src, eth_src_mask, ctrl->eth_dst, ctrl->eth_dst_mask, @@ -1727,9 +1628,7 @@ static int ofdpa_port_ctrl_vlan_acl(struct ofdpa_port *ofdpa_port, } static int ofdpa_port_ctrl_vlan_bridge(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, - int flags, - const struct ofdpa_ctrl *ctrl, + int flags, const struct ofdpa_ctrl *ctrl, __be16 vlan_id) { enum rocker_of_dpa_table_id goto_tbl = @@ -1741,7 +1640,7 @@ static int ofdpa_port_ctrl_vlan_bridge(struct ofdpa_port *ofdpa_port, if (!ofdpa_port_is_bridged(ofdpa_port)) return 0; - err = ofdpa_flow_tbl_bridge(ofdpa_port, trans, flags, + err = ofdpa_flow_tbl_bridge(ofdpa_port, flags, ctrl->eth_dst, ctrl->eth_dst_mask, vlan_id, tunnel_id, goto_tbl, group_id, ctrl->copy_to_cpu); @@ -1752,8 +1651,7 @@ static int ofdpa_port_ctrl_vlan_bridge(struct ofdpa_port *ofdpa_port, return err; } -static int ofdpa_port_ctrl_vlan_term(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_port_ctrl_vlan_term(struct ofdpa_port *ofdpa_port, int flags, const struct ofdpa_ctrl *ctrl, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -1763,8 +1661,7 @@ static int ofdpa_port_ctrl_vlan_term(struct ofdpa_port *ofdpa_port, if (ntohs(vlan_id) == 0) vlan_id = ofdpa_port->internal_vlan_id; - err = ofdpa_flow_tbl_term_mac(ofdpa_port, trans, - ofdpa_port->pport, in_pport_mask, + err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, in_pport_mask, ctrl->eth_type, ctrl->eth_dst, ctrl->eth_dst_mask, vlan_id, vlan_id_mask, ctrl->copy_to_cpu, @@ -1776,26 +1673,24 @@ static int ofdpa_port_ctrl_vlan_term(struct ofdpa_port *ofdpa_port, return err; } -static int ofdpa_port_ctrl_vlan(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_port_ctrl_vlan(struct ofdpa_port *ofdpa_port, int flags, const struct ofdpa_ctrl *ctrl, __be16 vlan_id) { if (ctrl->acl) - return ofdpa_port_ctrl_vlan_acl(ofdpa_port, trans, flags, + return ofdpa_port_ctrl_vlan_acl(ofdpa_port, flags, ctrl, vlan_id); if (ctrl->bridge) - return ofdpa_port_ctrl_vlan_bridge(ofdpa_port, trans, flags, + return ofdpa_port_ctrl_vlan_bridge(ofdpa_port, flags, ctrl, vlan_id); if (ctrl->term) - return ofdpa_port_ctrl_vlan_term(ofdpa_port, trans, flags, + return ofdpa_port_ctrl_vlan_term(ofdpa_port, flags, ctrl, vlan_id); return -EOPNOTSUPP; } -static int ofdpa_port_ctrl_vlan_add(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_port_ctrl_vlan_add(struct ofdpa_port *ofdpa_port, int flags, __be16 vlan_id) { int err = 0; @@ -1803,7 +1698,7 @@ static int ofdpa_port_ctrl_vlan_add(struct ofdpa_port *ofdpa_port, for (i = 0; i < OFDPA_CTRL_MAX; i++) { if (ofdpa_port->ctrls[i]) { - err = ofdpa_port_ctrl_vlan(ofdpa_port, trans, flags, + err = ofdpa_port_ctrl_vlan(ofdpa_port, flags, &ofdpa_ctrls[i], vlan_id); if (err) return err; @@ -1813,8 +1708,7 @@ static int ofdpa_port_ctrl_vlan_add(struct ofdpa_port *ofdpa_port, return err; } -static int ofdpa_port_ctrl(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, +static int ofdpa_port_ctrl(struct ofdpa_port *ofdpa_port, int flags, const struct ofdpa_ctrl *ctrl) { u16 vid; @@ -1823,7 +1717,7 @@ static int ofdpa_port_ctrl(struct ofdpa_port *ofdpa_port, for (vid = 1; vid < VLAN_N_VID; vid++) { if (!test_bit(vid, ofdpa_port->vlan_bitmap)) continue; - err = ofdpa_port_ctrl_vlan(ofdpa_port, trans, flags, + err = ofdpa_port_ctrl_vlan(ofdpa_port, flags, ctrl, htons(vid)); if (err) break; @@ -1832,8 +1726,8 @@ static int ofdpa_port_ctrl(struct ofdpa_port *ofdpa_port, return err; } -static int ofdpa_port_vlan(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, u16 vid) +static int ofdpa_port_vlan(struct ofdpa_port *ofdpa_port, int flags, + u16 vid) { enum rocker_of_dpa_table_id goto_tbl = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; @@ -1857,43 +1751,44 @@ static int ofdpa_port_vlan(struct ofdpa_port *ofdpa_port, change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap); if (adding) { - err = ofdpa_port_ctrl_vlan_add(ofdpa_port, trans, flags, + err = ofdpa_port_ctrl_vlan_add(ofdpa_port, flags, internal_vlan_id); if (err) { netdev_err(ofdpa_port->dev, "Error (%d) port ctrl vlan add\n", err); - goto err_out; + goto err_vlan_add; } } - err = ofdpa_port_vlan_l2_groups(ofdpa_port, trans, flags, + err = ofdpa_port_vlan_l2_groups(ofdpa_port, flags, internal_vlan_id, untagged); if (err) { netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 groups\n", err); - goto err_out; + goto err_vlan_l2_groups; } - err = ofdpa_port_vlan_flood_group(ofdpa_port, trans, flags, + err = ofdpa_port_vlan_flood_group(ofdpa_port, flags, internal_vlan_id); if (err) { netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 flood group\n", err); - goto err_out; + goto err_flood_group; } - err = ofdpa_flow_tbl_vlan(ofdpa_port, trans, flags, + err = ofdpa_flow_tbl_vlan(ofdpa_port, flags, in_pport, vlan_id, vlan_id_mask, goto_tbl, untagged, internal_vlan_id); if (err) netdev_err(ofdpa_port->dev, "Error (%d) port VLAN table\n", err); -err_out: - if (switchdev_trans_ph_prepare(trans)) - change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap); + return 0; +err_vlan_add: +err_vlan_l2_groups: +err_flood_group: + change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap); return err; } -static int ofdpa_port_ig_tbl(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags) +static int ofdpa_port_ig_tbl(struct ofdpa_port *ofdpa_port, int flags) { enum rocker_of_dpa_table_id goto_tbl; u32 in_pport; @@ -1908,7 +1803,7 @@ static int ofdpa_port_ig_tbl(struct ofdpa_port *ofdpa_port, in_pport_mask = 0xffff0000; goto_tbl = ROCKER_OF_DPA_TABLE_ID_VLAN; - err = ofdpa_flow_tbl_ig_port(ofdpa_port, trans, flags, + err = ofdpa_flow_tbl_ig_port(ofdpa_port, flags, in_pport, in_pport_mask, goto_tbl); if (err) @@ -1920,7 +1815,6 @@ static int ofdpa_port_ig_tbl(struct ofdpa_port *ofdpa_port, struct ofdpa_fdb_learn_work { struct work_struct work; struct ofdpa_port *ofdpa_port; - struct switchdev_trans *trans; int flags; u8 addr[ETH_ALEN]; u16 vid; @@ -1946,12 +1840,11 @@ static void ofdpa_port_fdb_learn_work(struct work_struct *work) lw->ofdpa_port->dev, &info.info); rtnl_unlock(); - ofdpa_kfree(lw->trans, work); + kfree(work); } static int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - const u8 *addr, __be16 vlan_id) + int flags, const u8 *addr, __be16 vlan_id) { struct ofdpa_fdb_learn_work *lw; enum rocker_of_dpa_table_id goto_tbl = @@ -1966,7 +1859,7 @@ static int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); if (!(flags & OFDPA_OP_FLAG_REFRESH)) { - err = ofdpa_flow_tbl_bridge(ofdpa_port, trans, flags, addr, + err = ofdpa_flow_tbl_bridge(ofdpa_port, flags, addr, NULL, vlan_id, tunnel_id, goto_tbl, group_id, copy_to_cpu); if (err) @@ -1976,23 +1869,18 @@ static int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, if (!ofdpa_port_is_bridged(ofdpa_port)) return 0; - lw = ofdpa_kzalloc(trans, flags, sizeof(*lw)); + lw = kzalloc(sizeof(*lw), GFP_ATOMIC); if (!lw) return -ENOMEM; INIT_WORK(&lw->work, ofdpa_port_fdb_learn_work); lw->ofdpa_port = ofdpa_port; - lw->trans = trans; lw->flags = flags; ether_addr_copy(lw->addr, addr); lw->vid = ofdpa_port_vlan_to_vid(ofdpa_port, vlan_id); - if (switchdev_trans_ph_prepare(trans)) - ofdpa_kfree(trans, lw); - else - schedule_work(&lw->work); - + schedule_work(&lw->work); return 0; } @@ -2010,7 +1898,6 @@ ofdpa_fdb_tbl_find(const struct ofdpa *ofdpa, } static int ofdpa_port_fdb(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags) { @@ -2020,7 +1907,7 @@ static int ofdpa_port_fdb(struct ofdpa_port *ofdpa_port, bool removing = (flags & OFDPA_OP_FLAG_REMOVE); unsigned long lock_flags; - fdb = ofdpa_kzalloc(trans, flags, sizeof(*fdb)); + fdb = kzalloc(sizeof(*fdb), GFP_KERNEL); if (!fdb) return -ENOMEM; @@ -2038,32 +1925,29 @@ static int ofdpa_port_fdb(struct ofdpa_port *ofdpa_port, if (found) { found->touched = jiffies; if (removing) { - ofdpa_kfree(trans, fdb); - if (!switchdev_trans_ph_prepare(trans)) - hash_del(&found->entry); + kfree(fdb); + hash_del(&found->entry); } } else if (!removing) { - if (!switchdev_trans_ph_prepare(trans)) - hash_add(ofdpa->fdb_tbl, &fdb->entry, - fdb->key_crc32); + hash_add(ofdpa->fdb_tbl, &fdb->entry, + fdb->key_crc32); } spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags); /* Check if adding and already exists, or removing and can't find */ if (!found != !removing) { - ofdpa_kfree(trans, fdb); + kfree(fdb); if (!found && removing) return 0; /* Refreshing existing to update aging timers */ flags |= OFDPA_OP_FLAG_REFRESH; } - return ofdpa_port_fdb_learn(ofdpa_port, trans, flags, addr, vlan_id); + return ofdpa_port_fdb_learn(ofdpa_port, flags, addr, vlan_id); } -static int ofdpa_port_fdb_flush(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags) +static int ofdpa_port_fdb_flush(struct ofdpa_port *ofdpa_port, int flags) { struct ofdpa *ofdpa = ofdpa_port->ofdpa; struct ofdpa_fdb_tbl_entry *found; @@ -2085,13 +1969,12 @@ static int ofdpa_port_fdb_flush(struct ofdpa_port *ofdpa_port, continue; if (!found->learned) continue; - err = ofdpa_port_fdb_learn(ofdpa_port, trans, flags, + err = ofdpa_port_fdb_learn(ofdpa_port, flags, found->key.addr, found->key.vlan_id); if (err) goto err_out; - if (!switchdev_trans_ph_prepare(trans)) - hash_del(&found->entry); + hash_del(&found->entry); } err_out: @@ -2121,8 +2004,8 @@ static void ofdpa_fdb_cleanup(unsigned long data) ofdpa_port = entry->key.ofdpa_port; expires = entry->touched + ofdpa_port->ageing_time; if (time_before_eq(expires, jiffies)) { - ofdpa_port_fdb_learn(ofdpa_port, NULL, - flags, entry->key.addr, + ofdpa_port_fdb_learn(ofdpa_port, flags, + entry->key.addr, entry->key.vlan_id); hash_del(&entry->entry); } else if (time_before(expires, next_timer)) { @@ -2136,8 +2019,7 @@ static void ofdpa_fdb_cleanup(unsigned long data) } static int ofdpa_port_router_mac(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, - __be16 vlan_id) + int flags, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; __be16 eth_type; @@ -2150,26 +2032,25 @@ static int ofdpa_port_router_mac(struct ofdpa_port *ofdpa_port, vlan_id = ofdpa_port->internal_vlan_id; eth_type = htons(ETH_P_IP); - err = ofdpa_flow_tbl_term_mac(ofdpa_port, trans, - ofdpa_port->pport, in_pport_mask, - eth_type, ofdpa_port->dev->dev_addr, + err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, + in_pport_mask, eth_type, + ofdpa_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 = ofdpa_flow_tbl_term_mac(ofdpa_port, trans, - ofdpa_port->pport, in_pport_mask, - eth_type, ofdpa_port->dev->dev_addr, + err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, + in_pport_mask, eth_type, + ofdpa_port->dev->dev_addr, dst_mac_mask, vlan_id, vlan_id_mask, copy_to_cpu, flags); return err; } -static int ofdpa_port_fwding(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags) +static int ofdpa_port_fwding(struct ofdpa_port *ofdpa_port, int flags) { bool pop_vlan; u32 out_pport; @@ -2194,7 +2075,7 @@ static int ofdpa_port_fwding(struct ofdpa_port *ofdpa_port, continue; vlan_id = htons(vid); pop_vlan = ofdpa_vlan_id_is_internal(vlan_id); - err = ofdpa_group_l2_interface(ofdpa_port, trans, flags, + err = ofdpa_group_l2_interface(ofdpa_port, flags, vlan_id, out_pport, pop_vlan); if (err) { netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for pport %d\n", @@ -2207,7 +2088,6 @@ static int ofdpa_port_fwding(struct ofdpa_port *ofdpa_port, } static int ofdpa_port_stp_update(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, int flags, u8 state) { bool want[OFDPA_CTRL_MAX] = { 0, }; @@ -2216,11 +2096,12 @@ static int ofdpa_port_stp_update(struct ofdpa_port *ofdpa_port, int err; int i; + memcpy(prev_ctrls, ofdpa_port->ctrls, sizeof(prev_ctrls)); prev_state = ofdpa_port->stp_state; - if (prev_state == state) + + if (ofdpa_port->stp_state == state) return 0; - memcpy(prev_ctrls, ofdpa_port->ctrls, sizeof(prev_ctrls)); ofdpa_port->stp_state = state; switch (state) { @@ -2250,26 +2131,29 @@ static int ofdpa_port_stp_update(struct ofdpa_port *ofdpa_port, if (want[i] != ofdpa_port->ctrls[i]) { int ctrl_flags = flags | (want[i] ? 0 : OFDPA_OP_FLAG_REMOVE); - err = ofdpa_port_ctrl(ofdpa_port, trans, ctrl_flags, + err = ofdpa_port_ctrl(ofdpa_port, ctrl_flags, &ofdpa_ctrls[i]); if (err) - goto err_out; + goto err_port_ctrl; ofdpa_port->ctrls[i] = want[i]; } } - err = ofdpa_port_fdb_flush(ofdpa_port, trans, flags); + err = ofdpa_port_fdb_flush(ofdpa_port, flags); if (err) - goto err_out; + goto err_fdb_flush; - err = ofdpa_port_fwding(ofdpa_port, trans, flags); + err = ofdpa_port_fwding(ofdpa_port, flags); + if (err) + goto err_port_fwding; -err_out: - if (switchdev_trans_ph_prepare(trans)) { - memcpy(ofdpa_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); - ofdpa_port->stp_state = prev_state; - } + return 0; +err_port_ctrl: +err_fdb_flush: +err_port_fwding: + memcpy(ofdpa_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); + ofdpa_port->stp_state = prev_state; return err; } @@ -2280,7 +2164,7 @@ static int ofdpa_port_fwd_enable(struct ofdpa_port *ofdpa_port, int flags) return 0; /* port is not bridged, so simulate going to FORWARDING state */ - return ofdpa_port_stp_update(ofdpa_port, NULL, flags, + return ofdpa_port_stp_update(ofdpa_port, flags, BR_STATE_FORWARDING); } @@ -2291,25 +2175,24 @@ static int ofdpa_port_fwd_disable(struct ofdpa_port *ofdpa_port, int flags) return 0; /* port is not bridged, so simulate going to DISABLED state */ - return ofdpa_port_stp_update(ofdpa_port, NULL, flags, + return ofdpa_port_stp_update(ofdpa_port, flags, BR_STATE_DISABLED); } static int ofdpa_port_vlan_add(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, u16 vid, u16 flags) { int err; /* XXX deal with flags for PVID and untagged */ - err = ofdpa_port_vlan(ofdpa_port, trans, 0, vid); + err = ofdpa_port_vlan(ofdpa_port, 0, vid); if (err) return err; - err = ofdpa_port_router_mac(ofdpa_port, trans, 0, htons(vid)); + err = ofdpa_port_router_mac(ofdpa_port, 0, htons(vid)); if (err) - ofdpa_port_vlan(ofdpa_port, trans, + ofdpa_port_vlan(ofdpa_port, OFDPA_OP_FLAG_REMOVE, vid); return err; @@ -2320,13 +2203,13 @@ static int ofdpa_port_vlan_del(struct ofdpa_port *ofdpa_port, { int err; - err = ofdpa_port_router_mac(ofdpa_port, NULL, - OFDPA_OP_FLAG_REMOVE, htons(vid)); + err = ofdpa_port_router_mac(ofdpa_port, OFDPA_OP_FLAG_REMOVE, + htons(vid)); if (err) return err; - return ofdpa_port_vlan(ofdpa_port, NULL, - OFDPA_OP_FLAG_REMOVE, vid); + return ofdpa_port_vlan(ofdpa_port, OFDPA_OP_FLAG_REMOVE, + vid); } static struct ofdpa_internal_vlan_tbl_entry * @@ -2385,10 +2268,9 @@ static __be16 ofdpa_port_internal_vlan_id_get(struct ofdpa_port *ofdpa_port, return found->vlan_id; } -static int ofdpa_port_fib_ipv4(struct ofdpa_port *ofdpa_port, - struct switchdev_trans *trans, __be32 dst, - int dst_len, struct fib_info *fi, - u32 tb_id, int flags) +static int ofdpa_port_fib_ipv4(struct ofdpa_port *ofdpa_port, __be32 dst, + int dst_len, struct fib_info *fi, u32 tb_id, + int flags) { const struct fib_nh *nh; __be16 eth_type = htons(ETH_P_IP); @@ -2410,7 +2292,7 @@ static int ofdpa_port_fib_ipv4(struct ofdpa_port *ofdpa_port, has_gw = !!nh->nh_gw; if (has_gw && nh_on_port) { - err = ofdpa_port_ipv4_nh(ofdpa_port, trans, flags, + err = ofdpa_port_ipv4_nh(ofdpa_port, flags, nh->nh_gw, &index); if (err) return err; @@ -2421,7 +2303,7 @@ static int ofdpa_port_fib_ipv4(struct ofdpa_port *ofdpa_port, group_id = ROCKER_GROUP_L2_INTERFACE(internal_vlan_id, 0); } - err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, trans, eth_type, dst, + err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, eth_type, dst, dst_mask, priority, goto_tbl, group_id, fi, flags); if (err) @@ -2559,7 +2441,7 @@ static int ofdpa_port_init(struct rocker_port *rocker_port) rocker_port_set_learning(rocker_port, !!(ofdpa_port->brport_flags & BR_LEARNING)); - err = ofdpa_port_ig_tbl(ofdpa_port, NULL, 0); + err = ofdpa_port_ig_tbl(ofdpa_port, 0); if (err) { netdev_err(ofdpa_port->dev, "install ig port table failed\n"); return err; @@ -2569,7 +2451,7 @@ static int ofdpa_port_init(struct rocker_port *rocker_port) ofdpa_port_internal_vlan_id_get(ofdpa_port, ofdpa_port->dev->ifindex); - err = ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0); + err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0); if (err) { netdev_err(ofdpa_port->dev, "install untagged VLAN failed\n"); goto err_untagged_vlan; @@ -2577,7 +2459,7 @@ static int ofdpa_port_init(struct rocker_port *rocker_port) return 0; err_untagged_vlan: - ofdpa_port_ig_tbl(ofdpa_port, NULL, OFDPA_OP_FLAG_REMOVE); + ofdpa_port_ig_tbl(ofdpa_port, OFDPA_OP_FLAG_REMOVE); return err; } @@ -2585,7 +2467,7 @@ static void ofdpa_port_fini(struct rocker_port *rocker_port) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - ofdpa_port_ig_tbl(ofdpa_port, NULL, OFDPA_OP_FLAG_REMOVE); + ofdpa_port_ig_tbl(ofdpa_port, OFDPA_OP_FLAG_REMOVE); } static int ofdpa_port_open(struct rocker_port *rocker_port) @@ -2603,12 +2485,11 @@ static void ofdpa_port_stop(struct rocker_port *rocker_port) } static int ofdpa_port_attr_stp_state_set(struct rocker_port *rocker_port, - u8 state, - struct switchdev_trans *trans) + u8 state) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - return ofdpa_port_stp_update(ofdpa_port, trans, 0, state); + return ofdpa_port_stp_update(ofdpa_port, 0, state); } static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port, @@ -2671,15 +2552,14 @@ ofdpa_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, } static int ofdpa_port_obj_vlan_add(struct rocker_port *rocker_port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) + const struct switchdev_obj_port_vlan *vlan) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; u16 vid; int err; for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ofdpa_port_vlan_add(ofdpa_port, trans, vid, vlan->flags); + err = ofdpa_port_vlan_add(ofdpa_port, vid, vlan->flags); if (err) return err; } @@ -2727,29 +2607,28 @@ static int ofdpa_port_obj_vlan_dump(const struct rocker_port *rocker_port, } static int ofdpa_port_obj_fdb_add(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + u16 vid, const unsigned char *addr) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - __be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, fdb->vid, NULL); + __be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, NULL); if (!ofdpa_port_is_bridged(ofdpa_port)) return -EINVAL; - return ofdpa_port_fdb(ofdpa_port, trans, fdb->addr, vlan_id, 0); + return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, 0); } static int ofdpa_port_obj_fdb_del(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb) + u16 vid, const unsigned char *addr) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - __be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, fdb->vid, NULL); + __be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, NULL); int flags = OFDPA_OP_FLAG_REMOVE; if (!ofdpa_port_is_bridged(ofdpa_port)) return -EINVAL; - return ofdpa_port_fdb(ofdpa_port, NULL, fdb->addr, vlan_id, flags); + return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags); } static int ofdpa_port_obj_fdb_dump(const struct rocker_port *rocker_port, @@ -2803,7 +2682,7 @@ static int ofdpa_port_bridge_join(struct ofdpa_port *ofdpa_port, ofdpa_port->bridge_dev = bridge; - return ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0); + return ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0); } static int ofdpa_port_bridge_leave(struct ofdpa_port *ofdpa_port) @@ -2822,7 +2701,7 @@ static int ofdpa_port_bridge_leave(struct ofdpa_port *ofdpa_port) ofdpa_port->bridge_dev = NULL; - err = ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0); + err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0); if (err) return err; @@ -2881,7 +2760,7 @@ static int ofdpa_port_neigh_update(struct rocker_port *rocker_port, OFDPA_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *) n->primary_key; - return ofdpa_port_ipv4_neigh(ofdpa_port, NULL, flags, ip_addr, n->ha); + return ofdpa_port_ipv4_neigh(ofdpa_port, flags, ip_addr, n->ha); } static int ofdpa_port_neigh_destroy(struct rocker_port *rocker_port, @@ -2891,7 +2770,7 @@ static int ofdpa_port_neigh_destroy(struct rocker_port *rocker_port, int flags = OFDPA_OP_FLAG_REMOVE | OFDPA_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *) n->primary_key; - return ofdpa_port_ipv4_neigh(ofdpa_port, NULL, flags, ip_addr, n->ha); + return ofdpa_port_ipv4_neigh(ofdpa_port, flags, ip_addr, n->ha); } static int ofdpa_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, @@ -2905,7 +2784,7 @@ static int ofdpa_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, ofdpa_port->stp_state != BR_STATE_FORWARDING) return 0; - return ofdpa_port_fdb(ofdpa_port, NULL, addr, vlan_id, flags); + return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags); } static struct ofdpa_port *ofdpa_port_dev_lower_find(struct net_device *dev, @@ -2929,7 +2808,7 @@ static int ofdpa_fib4_add(struct rocker *rocker, ofdpa_port = ofdpa_port_dev_lower_find(fen_info->fi->fib_dev, rocker); if (!ofdpa_port) return 0; - err = ofdpa_port_fib_ipv4(ofdpa_port, NULL, htonl(fen_info->dst), + err = ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst), fen_info->dst_len, fen_info->fi, fen_info->tb_id, 0); if (err) @@ -2950,7 +2829,7 @@ static int ofdpa_fib4_del(struct rocker *rocker, if (!ofdpa_port) return 0; fib_info_offload_dec(fen_info->fi); - return ofdpa_port_fib_ipv4(ofdpa_port, NULL, htonl(fen_info->dst), + return ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst), fen_info->dst_len, fen_info->fi, fen_info->tb_id, OFDPA_OP_FLAG_REMOVE); } @@ -2977,7 +2856,7 @@ static void ofdpa_fib4_abort(struct rocker *rocker) if (!ofdpa_port) continue; fib_info_offload_dec(flow_entry->fi); - ofdpa_flow_tbl_del(ofdpa_port, NULL, OFDPA_OP_FLAG_REMOVE, + ofdpa_flow_tbl_del(ofdpa_port, OFDPA_OP_FLAG_REMOVE, flow_entry); } spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, flags); From 726fd42fc1e3fe478cf08169f639caf92daeef0d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:26 +0200 Subject: [PATCH 17/19] rocker: Add support for learning FDB through notification Add support for learning FDB through notification. The driver defers the hardware update via ordered work queue. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 140 +++++++++++++++++++++- 1 file changed, 135 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 9f0154dc2cf7..72dab7cad24a 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1690,6 +1690,29 @@ rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port, return wops->port_obj_fdb_del(rocker_port, fdb->vid, fdb->addr); } +static int +rocker_world_port_fdb_add(struct rocker_port *rocker_port, + struct switchdev_notifier_fdb_info *info) +{ + struct rocker_world_ops *wops = rocker_port->rocker->wops; + + if (!wops->port_obj_fdb_add) + return -EOPNOTSUPP; + + return wops->port_obj_fdb_add(rocker_port, info->vid, info->addr); +} + +static int +rocker_world_port_fdb_del(struct rocker_port *rocker_port, + struct switchdev_notifier_fdb_info *info) +{ + struct rocker_world_ops *wops = rocker_port->rocker->wops; + + if (!wops->port_obj_fdb_del) + return -EOPNOTSUPP; + return wops->port_obj_fdb_del(rocker_port, info->vid, info->addr); +} + static int rocker_world_port_obj_fdb_dump(const struct rocker_port *rocker_port, struct switchdev_obj_port_fdb *fdb, @@ -2767,6 +2790,109 @@ static void rocker_msix_fini(const struct rocker *rocker) kfree(rocker->msix_entries); } +static bool rocker_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &rocker_port_netdev_ops; +} + +struct rocker_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct rocker_port *rocker_port; + unsigned long event; +}; + +static void +rocker_fdb_offload_notify(struct rocker_port *rocker_port, + struct switchdev_notifier_fdb_info *recv_info) +{ + struct switchdev_notifier_fdb_info info; + + info.addr = recv_info->addr; + info.vid = recv_info->vid; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, + rocker_port->dev, &info.info); +} + +static void rocker_switchdev_event_work(struct work_struct *work) +{ + struct rocker_switchdev_event_work *switchdev_work = + container_of(work, struct rocker_switchdev_event_work, work); + struct rocker_port *rocker_port = switchdev_work->rocker_port; + struct switchdev_notifier_fdb_info *fdb_info; + int err; + + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = rocker_world_port_fdb_add(rocker_port, fdb_info); + if (err) { + netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err); + break; + } + rocker_fdb_offload_notify(rocker_port, fdb_info); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = rocker_world_port_fdb_del(rocker_port, fdb_info); + if (err) + netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err); + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(rocker_port->dev); +} + +/* called under rcu_read_lock() */ +static int rocker_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + struct rocker_switchdev_event_work *switchdev_work; + struct switchdev_notifier_fdb_info *fdb_info = ptr; + struct rocker_port *rocker_port; + + if (!rocker_port_dev_check(dev)) + return NOTIFY_DONE; + + rocker_port = netdev_priv(dev); + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work); + switchdev_work->rocker_port = rocker_port; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + /* Take a reference on the rocker device */ + dev_hold(dev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(rocker_port->rocker->rocker_owq, + &switchdev_work->work); + return NOTIFY_DONE; +} + +static struct notifier_block rocker_switchdev_notifier = { + .notifier_call = rocker_switchdev_event, +}; + static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct rocker *rocker; @@ -2872,6 +2998,12 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_register_fib_notifier; + err = register_switchdev_notifier(&rocker_switchdev_notifier); + if (err) { + dev_err(&pdev->dev, "Failed to register switchdev notifier\n"); + goto err_register_switchdev_notifier; + } + rocker->hw.id = rocker_read64(rocker, SWITCH_ID); err = rocker_probe_ports(rocker); @@ -2886,6 +3018,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_probe_ports: + unregister_switchdev_notifier(&rocker_switchdev_notifier); +err_register_switchdev_notifier: unregister_fib_notifier(&rocker->fib_nb); err_register_fib_notifier: destroy_workqueue(rocker->rocker_owq); @@ -2916,6 +3050,7 @@ static void rocker_remove(struct pci_dev *pdev) struct rocker *rocker = pci_get_drvdata(pdev); rocker_remove_ports(rocker); + unregister_switchdev_notifier(&rocker_switchdev_notifier); unregister_fib_notifier(&rocker->fib_nb); rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); destroy_workqueue(rocker->rocker_owq); @@ -2940,11 +3075,6 @@ static struct pci_driver rocker_pci_driver = { * Net device notifier event handler ************************************/ -static bool rocker_port_dev_check(const struct net_device *dev) -{ - return dev->netdev_ops == &rocker_port_netdev_ops; -} - static bool rocker_port_dev_check_under(const struct net_device *dev, struct rocker *rocker) { From 403caa7afc99bdd24a9382a2948b99ce5e6165e2 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:27 +0200 Subject: [PATCH 18/19] rocker: Remove support for bypass bridge port attributes/vlan set The bridge port attributes/vlan for mlxsw devices should be set only from bridge code. The vlans are synced totally with the bridge so there is no need to special dump support. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker.h | 3 --- drivers/net/ethernet/rocker/rocker_main.c | 20 ------------------ drivers/net/ethernet/rocker/rocker_ofdpa.c | 24 ---------------------- 3 files changed, 47 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index a0f7782ddfdd..a0fa3f8240ba 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -122,9 +122,6 @@ struct rocker_world_ops { const struct switchdev_obj_port_vlan *vlan); int (*port_obj_vlan_del)(struct rocker_port *rocker_port, const struct switchdev_obj_port_vlan *vlan); - int (*port_obj_vlan_dump)(const struct rocker_port *rocker_port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb); int (*port_obj_fdb_add)(struct rocker_port *rocker_port, u16 vid, const unsigned char *addr); int (*port_obj_fdb_del)(struct rocker_port *rocker_port, diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 72dab7cad24a..3e78129a81fe 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1651,18 +1651,6 @@ rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, return wops->port_obj_vlan_del(rocker_port, vlan); } -static int -rocker_world_port_obj_vlan_dump(const struct rocker_port *rocker_port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - - if (!wops->port_obj_vlan_dump) - return -EOPNOTSUPP; - return wops->port_obj_vlan_dump(rocker_port, vlan, cb); -} - static int rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port, const struct switchdev_obj_port_fdb *fdb, @@ -2079,9 +2067,6 @@ static const struct net_device_ops rocker_port_netdev_ops = { .ndo_start_xmit = rocker_port_xmit, .ndo_set_mac_address = rocker_port_set_mac_address, .ndo_change_mtu = rocker_port_change_mtu, - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_fdb_add = switchdev_port_fdb_add, .ndo_fdb_del = switchdev_port_fdb_del, .ndo_fdb_dump = switchdev_port_fdb_dump, @@ -2214,11 +2199,6 @@ static int rocker_port_obj_dump(struct net_device *dev, SWITCHDEV_OBJ_PORT_FDB(obj), cb); break; - case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = rocker_world_port_obj_vlan_dump(rocker_port, - SWITCHDEV_OBJ_PORT_VLAN(obj), - cb); - break; default: err = -EOPNOTSUPP; break; diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 184a478fadab..5510ad0fcc3c 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -2583,29 +2583,6 @@ static int ofdpa_port_obj_vlan_del(struct rocker_port *rocker_port, return 0; } -static int ofdpa_port_obj_vlan_dump(const struct rocker_port *rocker_port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - const struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - u16 vid; - int err = 0; - - for (vid = 1; vid < VLAN_N_VID; vid++) { - if (!test_bit(vid, ofdpa_port->vlan_bitmap)) - continue; - vlan->flags = 0; - if (ofdpa_vlan_id_is_internal(htons(vid))) - vlan->flags |= BRIDGE_VLAN_INFO_PVID; - vlan->vid_begin = vlan->vid_end = vid; - err = cb(&vlan->obj); - if (err) - break; - } - - return err; -} - static int ofdpa_port_obj_fdb_add(struct rocker_port *rocker_port, u16 vid, const unsigned char *addr) { @@ -2882,7 +2859,6 @@ struct rocker_world_ops rocker_ofdpa_ops = { .port_attr_bridge_ageing_time_set = ofdpa_port_attr_bridge_ageing_time_set, .port_obj_vlan_add = ofdpa_port_obj_vlan_add, .port_obj_vlan_del = ofdpa_port_obj_vlan_del, - .port_obj_vlan_dump = ofdpa_port_obj_vlan_dump, .port_obj_fdb_add = ofdpa_port_obj_fdb_add, .port_obj_fdb_del = ofdpa_port_obj_fdb_del, .port_obj_fdb_dump = ofdpa_port_obj_fdb_dump, From abfbf8a0b25e779d0e3533a7d99843982755d61e Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 8 Jun 2017 08:44:28 +0200 Subject: [PATCH 19/19] rocker: Remove support bridge bypass FDB The FDB add/delete are now done through the notification chain. The FDBs are synced with the bridge and there is no need for extra dumping. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker.h | 3 - drivers/net/ethernet/rocker/rocker_main.c | 73 ---------------------- drivers/net/ethernet/rocker/rocker_ofdpa.c | 30 --------- 3 files changed, 106 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index a0fa3f8240ba..748fb12260a6 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -126,9 +126,6 @@ struct rocker_world_ops { u16 vid, const unsigned char *addr); int (*port_obj_fdb_del)(struct rocker_port *rocker_port, u16 vid, const unsigned char *addr); - int (*port_obj_fdb_dump)(const struct rocker_port *rocker_port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); int (*port_master_linked)(struct rocker_port *rocker_port, struct net_device *master); int (*port_master_unlinked)(struct rocker_port *rocker_port, diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 3e78129a81fe..b1e5c07099fa 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1651,33 +1651,6 @@ rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, return wops->port_obj_vlan_del(rocker_port, vlan); } -static int -rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - - if (!wops->port_obj_fdb_add) - return -EOPNOTSUPP; - - if (switchdev_trans_ph_prepare(trans)) - return 0; - - return wops->port_obj_fdb_add(rocker_port, fdb->vid, fdb->addr); -} - -static int -rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - - if (!wops->port_obj_fdb_del) - return -EOPNOTSUPP; - return wops->port_obj_fdb_del(rocker_port, fdb->vid, fdb->addr); -} - static int rocker_world_port_fdb_add(struct rocker_port *rocker_port, struct switchdev_notifier_fdb_info *info) @@ -1701,18 +1674,6 @@ rocker_world_port_fdb_del(struct rocker_port *rocker_port, return wops->port_obj_fdb_del(rocker_port, info->vid, info->addr); } -static int -rocker_world_port_obj_fdb_dump(const struct rocker_port *rocker_port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - - if (!wops->port_obj_fdb_dump) - return -EOPNOTSUPP; - return wops->port_obj_fdb_dump(rocker_port, fdb, cb); -} - static int rocker_world_port_master_linked(struct rocker_port *rocker_port, struct net_device *master) { @@ -2067,9 +2028,6 @@ static const struct net_device_ops rocker_port_netdev_ops = { .ndo_start_xmit = rocker_port_xmit, .ndo_set_mac_address = rocker_port_set_mac_address, .ndo_change_mtu = rocker_port_change_mtu, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_get_phys_port_name = rocker_port_get_phys_port_name, .ndo_change_proto_down = rocker_port_change_proto_down, .ndo_neigh_destroy = rocker_port_neigh_destroy, @@ -2150,11 +2108,6 @@ static int rocker_port_obj_add(struct net_device *dev, SWITCHDEV_OBJ_PORT_VLAN(obj), trans); break; - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = rocker_world_port_obj_fdb_add(rocker_port, - SWITCHDEV_OBJ_PORT_FDB(obj), - trans); - break; default: err = -EOPNOTSUPP; break; @@ -2174,31 +2127,6 @@ static int rocker_port_obj_del(struct net_device *dev, err = rocker_world_port_obj_vlan_del(rocker_port, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = rocker_world_port_obj_fdb_del(rocker_port, - SWITCHDEV_OBJ_PORT_FDB(obj)); - break; - default: - err = -EOPNOTSUPP; - break; - } - - return err; -} - -static int rocker_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - const struct rocker_port *rocker_port = netdev_priv(dev); - int err = 0; - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = rocker_world_port_obj_fdb_dump(rocker_port, - SWITCHDEV_OBJ_PORT_FDB(obj), - cb); - break; default: err = -EOPNOTSUPP; break; @@ -2212,7 +2140,6 @@ static const struct switchdev_ops rocker_port_switchdev_ops = { .switchdev_port_attr_set = rocker_port_attr_set, .switchdev_port_obj_add = rocker_port_obj_add, .switchdev_port_obj_del = rocker_port_obj_del, - .switchdev_port_obj_dump = rocker_port_obj_dump, }; struct rocker_fib_event_work { diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 5510ad0fcc3c..bd0e3f157e9e 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -2608,35 +2608,6 @@ static int ofdpa_port_obj_fdb_del(struct rocker_port *rocker_port, return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags); } -static int ofdpa_port_obj_fdb_dump(const struct rocker_port *rocker_port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) -{ - const struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - struct ofdpa *ofdpa = ofdpa_port->ofdpa; - struct ofdpa_fdb_tbl_entry *found; - struct hlist_node *tmp; - unsigned long lock_flags; - int bkt; - int err = 0; - - spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags); - hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, found, entry) { - if (found->key.ofdpa_port != ofdpa_port) - continue; - ether_addr_copy(fdb->addr, found->key.addr); - fdb->ndm_state = NUD_REACHABLE; - fdb->vid = ofdpa_port_vlan_to_vid(ofdpa_port, - found->key.vlan_id); - err = cb(&fdb->obj); - if (err) - break; - } - spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags); - - return err; -} - static int ofdpa_port_bridge_join(struct ofdpa_port *ofdpa_port, struct net_device *bridge) { @@ -2861,7 +2832,6 @@ struct rocker_world_ops rocker_ofdpa_ops = { .port_obj_vlan_del = ofdpa_port_obj_vlan_del, .port_obj_fdb_add = ofdpa_port_obj_fdb_add, .port_obj_fdb_del = ofdpa_port_obj_fdb_del, - .port_obj_fdb_dump = ofdpa_port_obj_fdb_dump, .port_master_linked = ofdpa_port_master_linked, .port_master_unlinked = ofdpa_port_master_unlinked, .port_neigh_update = ofdpa_port_neigh_update,