1f6e8fb55SWojciech Drewek // SPDX-License-Identifier: GPL-2.0
2f6e8fb55SWojciech Drewek /* Copyright (C) 2023, Intel Corporation. */
3f6e8fb55SWojciech Drewek
4f6e8fb55SWojciech Drewek #include "ice.h"
5f6e8fb55SWojciech Drewek #include "ice_eswitch_br.h"
6f6e8fb55SWojciech Drewek #include "ice_repr.h"
77c945a1aSWojciech Drewek #include "ice_switch.h"
82946204bSMichal Swiatkowski #include "ice_vlan.h"
92946204bSMichal Swiatkowski #include "ice_vf_vsi_vlan_ops.h"
10d129c2a2SPawel Chmielewski #include "ice_trace.h"
117c945a1aSWojciech Drewek
12e42c6e0cSMichal Swiatkowski #define ICE_ESW_BRIDGE_UPDATE_INTERVAL msecs_to_jiffies(1000)
13e42c6e0cSMichal Swiatkowski
147c945a1aSWojciech Drewek static const struct rhashtable_params ice_fdb_ht_params = {
157c945a1aSWojciech Drewek .key_offset = offsetof(struct ice_esw_br_fdb_entry, data),
167c945a1aSWojciech Drewek .key_len = sizeof(struct ice_esw_br_fdb_data),
177c945a1aSWojciech Drewek .head_offset = offsetof(struct ice_esw_br_fdb_entry, ht_node),
187c945a1aSWojciech Drewek .automatic_shrinking = true,
197c945a1aSWojciech Drewek };
20f6e8fb55SWojciech Drewek
ice_eswitch_br_is_dev_valid(const struct net_device * dev)21f6e8fb55SWojciech Drewek static bool ice_eswitch_br_is_dev_valid(const struct net_device *dev)
22f6e8fb55SWojciech Drewek {
23505a1fdaSWojciech Drewek /* Accept only PF netdev, PRs and LAG */
24505a1fdaSWojciech Drewek return ice_is_port_repr_netdev(dev) || netif_is_ice(dev) ||
25505a1fdaSWojciech Drewek netif_is_lag_master(dev);
26505a1fdaSWojciech Drewek }
27505a1fdaSWojciech Drewek
28505a1fdaSWojciech Drewek static struct net_device *
ice_eswitch_br_get_uplink_from_lag(struct net_device * lag_dev)29505a1fdaSWojciech Drewek ice_eswitch_br_get_uplink_from_lag(struct net_device *lag_dev)
30505a1fdaSWojciech Drewek {
31505a1fdaSWojciech Drewek struct net_device *lower;
32505a1fdaSWojciech Drewek struct list_head *iter;
33505a1fdaSWojciech Drewek
34505a1fdaSWojciech Drewek netdev_for_each_lower_dev(lag_dev, lower, iter) {
35505a1fdaSWojciech Drewek if (netif_is_ice(lower))
36505a1fdaSWojciech Drewek return lower;
37505a1fdaSWojciech Drewek }
38505a1fdaSWojciech Drewek
39505a1fdaSWojciech Drewek return NULL;
40f6e8fb55SWojciech Drewek }
41f6e8fb55SWojciech Drewek
42f6e8fb55SWojciech Drewek static struct ice_esw_br_port *
ice_eswitch_br_netdev_to_port(struct net_device * dev)43f6e8fb55SWojciech Drewek ice_eswitch_br_netdev_to_port(struct net_device *dev)
44f6e8fb55SWojciech Drewek {
45f6e8fb55SWojciech Drewek if (ice_is_port_repr_netdev(dev)) {
46f6e8fb55SWojciech Drewek struct ice_repr *repr = ice_netdev_to_repr(dev);
47f6e8fb55SWojciech Drewek
48f6e8fb55SWojciech Drewek return repr->br_port;
49505a1fdaSWojciech Drewek } else if (netif_is_ice(dev) || netif_is_lag_master(dev)) {
50505a1fdaSWojciech Drewek struct net_device *ice_dev;
51505a1fdaSWojciech Drewek struct ice_pf *pf;
52505a1fdaSWojciech Drewek
53505a1fdaSWojciech Drewek if (netif_is_lag_master(dev))
54505a1fdaSWojciech Drewek ice_dev = ice_eswitch_br_get_uplink_from_lag(dev);
55505a1fdaSWojciech Drewek else
56505a1fdaSWojciech Drewek ice_dev = dev;
57505a1fdaSWojciech Drewek
58505a1fdaSWojciech Drewek if (!ice_dev)
59505a1fdaSWojciech Drewek return NULL;
60505a1fdaSWojciech Drewek
61505a1fdaSWojciech Drewek pf = ice_netdev_to_pf(ice_dev);
62f6e8fb55SWojciech Drewek
63f6e8fb55SWojciech Drewek return pf->br_port;
64f6e8fb55SWojciech Drewek }
65f6e8fb55SWojciech Drewek
66f6e8fb55SWojciech Drewek return NULL;
67f6e8fb55SWojciech Drewek }
68f6e8fb55SWojciech Drewek
69f6e8fb55SWojciech Drewek static void
ice_eswitch_br_ingress_rule_setup(struct ice_adv_rule_info * rule_info,u8 pf_id,u16 vf_vsi_idx)707c945a1aSWojciech Drewek ice_eswitch_br_ingress_rule_setup(struct ice_adv_rule_info *rule_info,
717c945a1aSWojciech Drewek u8 pf_id, u16 vf_vsi_idx)
727c945a1aSWojciech Drewek {
737c945a1aSWojciech Drewek rule_info->sw_act.vsi_handle = vf_vsi_idx;
747c945a1aSWojciech Drewek rule_info->sw_act.flag |= ICE_FLTR_RX;
757c945a1aSWojciech Drewek rule_info->sw_act.src = pf_id;
767c945a1aSWojciech Drewek rule_info->priority = 5;
777c945a1aSWojciech Drewek }
787c945a1aSWojciech Drewek
797c945a1aSWojciech Drewek static void
ice_eswitch_br_egress_rule_setup(struct ice_adv_rule_info * rule_info,u16 pf_vsi_idx)807c945a1aSWojciech Drewek ice_eswitch_br_egress_rule_setup(struct ice_adv_rule_info *rule_info,
817c945a1aSWojciech Drewek u16 pf_vsi_idx)
827c945a1aSWojciech Drewek {
837c945a1aSWojciech Drewek rule_info->sw_act.vsi_handle = pf_vsi_idx;
847c945a1aSWojciech Drewek rule_info->sw_act.flag |= ICE_FLTR_TX;
857c945a1aSWojciech Drewek rule_info->flags_info.act = ICE_SINGLE_ACT_LAN_ENABLE;
867c945a1aSWojciech Drewek rule_info->flags_info.act_valid = true;
877c945a1aSWojciech Drewek rule_info->priority = 5;
887c945a1aSWojciech Drewek }
897c945a1aSWojciech Drewek
907c945a1aSWojciech Drewek static int
ice_eswitch_br_rule_delete(struct ice_hw * hw,struct ice_rule_query_data * rule)917c945a1aSWojciech Drewek ice_eswitch_br_rule_delete(struct ice_hw *hw, struct ice_rule_query_data *rule)
927c945a1aSWojciech Drewek {
937c945a1aSWojciech Drewek int err;
947c945a1aSWojciech Drewek
957c945a1aSWojciech Drewek if (!rule)
967c945a1aSWojciech Drewek return -EINVAL;
977c945a1aSWojciech Drewek
987c945a1aSWojciech Drewek err = ice_rem_adv_rule_by_id(hw, rule);
997c945a1aSWojciech Drewek kfree(rule);
1007c945a1aSWojciech Drewek
1017c945a1aSWojciech Drewek return err;
1027c945a1aSWojciech Drewek }
1037c945a1aSWojciech Drewek
104e9dda2cfSMarcin Szycik static u16
ice_eswitch_br_get_lkups_cnt(u16 vid)105e9dda2cfSMarcin Szycik ice_eswitch_br_get_lkups_cnt(u16 vid)
106e9dda2cfSMarcin Szycik {
107e9dda2cfSMarcin Szycik return ice_eswitch_br_is_vid_valid(vid) ? 2 : 1;
108e9dda2cfSMarcin Szycik }
109e9dda2cfSMarcin Szycik
110e9dda2cfSMarcin Szycik static void
ice_eswitch_br_add_vlan_lkup(struct ice_adv_lkup_elem * list,u16 vid)111e9dda2cfSMarcin Szycik ice_eswitch_br_add_vlan_lkup(struct ice_adv_lkup_elem *list, u16 vid)
112e9dda2cfSMarcin Szycik {
113e9dda2cfSMarcin Szycik if (ice_eswitch_br_is_vid_valid(vid)) {
114e9dda2cfSMarcin Szycik list[1].type = ICE_VLAN_OFOS;
115e9dda2cfSMarcin Szycik list[1].h_u.vlan_hdr.vlan = cpu_to_be16(vid & VLAN_VID_MASK);
116e9dda2cfSMarcin Szycik list[1].m_u.vlan_hdr.vlan = cpu_to_be16(0xFFFF);
117e9dda2cfSMarcin Szycik }
118e9dda2cfSMarcin Szycik }
119e9dda2cfSMarcin Szycik
1207c945a1aSWojciech Drewek static struct ice_rule_query_data *
ice_eswitch_br_fwd_rule_create(struct ice_hw * hw,int vsi_idx,int port_type,const unsigned char * mac,u16 vid)1217c945a1aSWojciech Drewek ice_eswitch_br_fwd_rule_create(struct ice_hw *hw, int vsi_idx, int port_type,
122e9dda2cfSMarcin Szycik const unsigned char *mac, u16 vid)
1237c945a1aSWojciech Drewek {
1247c945a1aSWojciech Drewek struct ice_adv_rule_info rule_info = { 0 };
1257c945a1aSWojciech Drewek struct ice_rule_query_data *rule;
1267c945a1aSWojciech Drewek struct ice_adv_lkup_elem *list;
127e9dda2cfSMarcin Szycik u16 lkups_cnt;
1287c945a1aSWojciech Drewek int err;
1297c945a1aSWojciech Drewek
130e9dda2cfSMarcin Szycik lkups_cnt = ice_eswitch_br_get_lkups_cnt(vid);
131e9dda2cfSMarcin Szycik
1327c945a1aSWojciech Drewek rule = kzalloc(sizeof(*rule), GFP_KERNEL);
1337c945a1aSWojciech Drewek if (!rule)
1347c945a1aSWojciech Drewek return ERR_PTR(-ENOMEM);
1357c945a1aSWojciech Drewek
1367c945a1aSWojciech Drewek list = kcalloc(lkups_cnt, sizeof(*list), GFP_ATOMIC);
1377c945a1aSWojciech Drewek if (!list) {
1387c945a1aSWojciech Drewek err = -ENOMEM;
1397c945a1aSWojciech Drewek goto err_list_alloc;
1407c945a1aSWojciech Drewek }
1417c945a1aSWojciech Drewek
1427c945a1aSWojciech Drewek switch (port_type) {
1437c945a1aSWojciech Drewek case ICE_ESWITCH_BR_UPLINK_PORT:
1447c945a1aSWojciech Drewek ice_eswitch_br_egress_rule_setup(&rule_info, vsi_idx);
1457c945a1aSWojciech Drewek break;
1467c945a1aSWojciech Drewek case ICE_ESWITCH_BR_VF_REPR_PORT:
1477c945a1aSWojciech Drewek ice_eswitch_br_ingress_rule_setup(&rule_info, hw->pf_id,
1487c945a1aSWojciech Drewek vsi_idx);
1497c945a1aSWojciech Drewek break;
1507c945a1aSWojciech Drewek default:
1517c945a1aSWojciech Drewek err = -EINVAL;
1527c945a1aSWojciech Drewek goto err_add_rule;
1537c945a1aSWojciech Drewek }
1547c945a1aSWojciech Drewek
1557c945a1aSWojciech Drewek list[0].type = ICE_MAC_OFOS;
1567c945a1aSWojciech Drewek ether_addr_copy(list[0].h_u.eth_hdr.dst_addr, mac);
1577c945a1aSWojciech Drewek eth_broadcast_addr(list[0].m_u.eth_hdr.dst_addr);
1587c945a1aSWojciech Drewek
159e9dda2cfSMarcin Szycik ice_eswitch_br_add_vlan_lkup(list, vid);
160e9dda2cfSMarcin Szycik
161bccd9bceSMarcin Szycik rule_info.need_pass_l2 = true;
162bccd9bceSMarcin Szycik
1637c945a1aSWojciech Drewek rule_info.sw_act.fltr_act = ICE_FWD_TO_VSI;
1647c945a1aSWojciech Drewek
1657c945a1aSWojciech Drewek err = ice_add_adv_rule(hw, list, lkups_cnt, &rule_info, rule);
1667c945a1aSWojciech Drewek if (err)
1677c945a1aSWojciech Drewek goto err_add_rule;
1687c945a1aSWojciech Drewek
1697c945a1aSWojciech Drewek kfree(list);
1707c945a1aSWojciech Drewek
1717c945a1aSWojciech Drewek return rule;
1727c945a1aSWojciech Drewek
1737c945a1aSWojciech Drewek err_add_rule:
1747c945a1aSWojciech Drewek kfree(list);
1757c945a1aSWojciech Drewek err_list_alloc:
1767c945a1aSWojciech Drewek kfree(rule);
1777c945a1aSWojciech Drewek
1787c945a1aSWojciech Drewek return ERR_PTR(err);
1797c945a1aSWojciech Drewek }
1807c945a1aSWojciech Drewek
181bccd9bceSMarcin Szycik static struct ice_rule_query_data *
ice_eswitch_br_guard_rule_create(struct ice_hw * hw,u16 vsi_idx,const unsigned char * mac,u16 vid)182bccd9bceSMarcin Szycik ice_eswitch_br_guard_rule_create(struct ice_hw *hw, u16 vsi_idx,
183e9dda2cfSMarcin Szycik const unsigned char *mac, u16 vid)
184bccd9bceSMarcin Szycik {
185bccd9bceSMarcin Szycik struct ice_adv_rule_info rule_info = { 0 };
186bccd9bceSMarcin Szycik struct ice_rule_query_data *rule;
187bccd9bceSMarcin Szycik struct ice_adv_lkup_elem *list;
188bccd9bceSMarcin Szycik int err = -ENOMEM;
189e9dda2cfSMarcin Szycik u16 lkups_cnt;
190e9dda2cfSMarcin Szycik
191e9dda2cfSMarcin Szycik lkups_cnt = ice_eswitch_br_get_lkups_cnt(vid);
192bccd9bceSMarcin Szycik
193bccd9bceSMarcin Szycik rule = kzalloc(sizeof(*rule), GFP_KERNEL);
194bccd9bceSMarcin Szycik if (!rule)
195bccd9bceSMarcin Szycik goto err_exit;
196bccd9bceSMarcin Szycik
197bccd9bceSMarcin Szycik list = kcalloc(lkups_cnt, sizeof(*list), GFP_ATOMIC);
198bccd9bceSMarcin Szycik if (!list)
199bccd9bceSMarcin Szycik goto err_list_alloc;
200bccd9bceSMarcin Szycik
201bccd9bceSMarcin Szycik list[0].type = ICE_MAC_OFOS;
202bccd9bceSMarcin Szycik ether_addr_copy(list[0].h_u.eth_hdr.src_addr, mac);
203bccd9bceSMarcin Szycik eth_broadcast_addr(list[0].m_u.eth_hdr.src_addr);
204bccd9bceSMarcin Szycik
205e9dda2cfSMarcin Szycik ice_eswitch_br_add_vlan_lkup(list, vid);
206e9dda2cfSMarcin Szycik
207bccd9bceSMarcin Szycik rule_info.allow_pass_l2 = true;
208bccd9bceSMarcin Szycik rule_info.sw_act.vsi_handle = vsi_idx;
209bccd9bceSMarcin Szycik rule_info.sw_act.fltr_act = ICE_NOP;
210bccd9bceSMarcin Szycik rule_info.priority = 5;
211bccd9bceSMarcin Szycik
212bccd9bceSMarcin Szycik err = ice_add_adv_rule(hw, list, lkups_cnt, &rule_info, rule);
213bccd9bceSMarcin Szycik if (err)
214bccd9bceSMarcin Szycik goto err_add_rule;
215bccd9bceSMarcin Szycik
216bccd9bceSMarcin Szycik kfree(list);
217bccd9bceSMarcin Szycik
218bccd9bceSMarcin Szycik return rule;
219bccd9bceSMarcin Szycik
220bccd9bceSMarcin Szycik err_add_rule:
221bccd9bceSMarcin Szycik kfree(list);
222bccd9bceSMarcin Szycik err_list_alloc:
223bccd9bceSMarcin Szycik kfree(rule);
224bccd9bceSMarcin Szycik err_exit:
225bccd9bceSMarcin Szycik return ERR_PTR(err);
226bccd9bceSMarcin Szycik }
227bccd9bceSMarcin Szycik
2287c945a1aSWojciech Drewek static struct ice_esw_br_flow *
ice_eswitch_br_flow_create(struct device * dev,struct ice_hw * hw,int vsi_idx,int port_type,const unsigned char * mac,u16 vid)2297c945a1aSWojciech Drewek ice_eswitch_br_flow_create(struct device *dev, struct ice_hw *hw, int vsi_idx,
230e9dda2cfSMarcin Szycik int port_type, const unsigned char *mac, u16 vid)
2317c945a1aSWojciech Drewek {
232bccd9bceSMarcin Szycik struct ice_rule_query_data *fwd_rule, *guard_rule;
2337c945a1aSWojciech Drewek struct ice_esw_br_flow *flow;
2347c945a1aSWojciech Drewek int err;
2357c945a1aSWojciech Drewek
2367c945a1aSWojciech Drewek flow = kzalloc(sizeof(*flow), GFP_KERNEL);
2377c945a1aSWojciech Drewek if (!flow)
2387c945a1aSWojciech Drewek return ERR_PTR(-ENOMEM);
2397c945a1aSWojciech Drewek
240e9dda2cfSMarcin Szycik fwd_rule = ice_eswitch_br_fwd_rule_create(hw, vsi_idx, port_type, mac,
241e9dda2cfSMarcin Szycik vid);
2427c945a1aSWojciech Drewek err = PTR_ERR_OR_ZERO(fwd_rule);
2437c945a1aSWojciech Drewek if (err) {
2447c945a1aSWojciech Drewek dev_err(dev, "Failed to create eswitch bridge %sgress forward rule, err: %d\n",
2457c945a1aSWojciech Drewek port_type == ICE_ESWITCH_BR_UPLINK_PORT ? "e" : "in",
2467c945a1aSWojciech Drewek err);
2477c945a1aSWojciech Drewek goto err_fwd_rule;
2487c945a1aSWojciech Drewek }
2497c945a1aSWojciech Drewek
250e9dda2cfSMarcin Szycik guard_rule = ice_eswitch_br_guard_rule_create(hw, vsi_idx, mac, vid);
251bccd9bceSMarcin Szycik err = PTR_ERR_OR_ZERO(guard_rule);
252bccd9bceSMarcin Szycik if (err) {
253bccd9bceSMarcin Szycik dev_err(dev, "Failed to create eswitch bridge %sgress guard rule, err: %d\n",
254bccd9bceSMarcin Szycik port_type == ICE_ESWITCH_BR_UPLINK_PORT ? "e" : "in",
255bccd9bceSMarcin Szycik err);
256bccd9bceSMarcin Szycik goto err_guard_rule;
257bccd9bceSMarcin Szycik }
258bccd9bceSMarcin Szycik
2597c945a1aSWojciech Drewek flow->fwd_rule = fwd_rule;
260bccd9bceSMarcin Szycik flow->guard_rule = guard_rule;
2617c945a1aSWojciech Drewek
2627c945a1aSWojciech Drewek return flow;
2637c945a1aSWojciech Drewek
264bccd9bceSMarcin Szycik err_guard_rule:
265bccd9bceSMarcin Szycik ice_eswitch_br_rule_delete(hw, fwd_rule);
2667c945a1aSWojciech Drewek err_fwd_rule:
2677c945a1aSWojciech Drewek kfree(flow);
2687c945a1aSWojciech Drewek
2697c945a1aSWojciech Drewek return ERR_PTR(err);
2707c945a1aSWojciech Drewek }
2717c945a1aSWojciech Drewek
2727c945a1aSWojciech Drewek static struct ice_esw_br_fdb_entry *
ice_eswitch_br_fdb_find(struct ice_esw_br * bridge,const unsigned char * mac,u16 vid)2737c945a1aSWojciech Drewek ice_eswitch_br_fdb_find(struct ice_esw_br *bridge, const unsigned char *mac,
2747c945a1aSWojciech Drewek u16 vid)
2757c945a1aSWojciech Drewek {
2767c945a1aSWojciech Drewek struct ice_esw_br_fdb_data data = {
2777c945a1aSWojciech Drewek .vid = vid,
2787c945a1aSWojciech Drewek };
2797c945a1aSWojciech Drewek
2807c945a1aSWojciech Drewek ether_addr_copy(data.addr, mac);
2817c945a1aSWojciech Drewek return rhashtable_lookup_fast(&bridge->fdb_ht, &data,
2827c945a1aSWojciech Drewek ice_fdb_ht_params);
2837c945a1aSWojciech Drewek }
2847c945a1aSWojciech Drewek
2857c945a1aSWojciech Drewek static void
ice_eswitch_br_flow_delete(struct ice_pf * pf,struct ice_esw_br_flow * flow)2867c945a1aSWojciech Drewek ice_eswitch_br_flow_delete(struct ice_pf *pf, struct ice_esw_br_flow *flow)
2877c945a1aSWojciech Drewek {
2887c945a1aSWojciech Drewek struct device *dev = ice_pf_to_dev(pf);
2897c945a1aSWojciech Drewek int err;
2907c945a1aSWojciech Drewek
2917c945a1aSWojciech Drewek err = ice_eswitch_br_rule_delete(&pf->hw, flow->fwd_rule);
2927c945a1aSWojciech Drewek if (err)
2937c945a1aSWojciech Drewek dev_err(dev, "Failed to delete FDB forward rule, err: %d\n",
2947c945a1aSWojciech Drewek err);
2957c945a1aSWojciech Drewek
296bccd9bceSMarcin Szycik err = ice_eswitch_br_rule_delete(&pf->hw, flow->guard_rule);
297bccd9bceSMarcin Szycik if (err)
298bccd9bceSMarcin Szycik dev_err(dev, "Failed to delete FDB guard rule, err: %d\n",
299bccd9bceSMarcin Szycik err);
300bccd9bceSMarcin Szycik
3017c945a1aSWojciech Drewek kfree(flow);
3027c945a1aSWojciech Drewek }
3037c945a1aSWojciech Drewek
304e9dda2cfSMarcin Szycik static struct ice_esw_br_vlan *
ice_esw_br_port_vlan_lookup(struct ice_esw_br * bridge,u16 vsi_idx,u16 vid)305e9dda2cfSMarcin Szycik ice_esw_br_port_vlan_lookup(struct ice_esw_br *bridge, u16 vsi_idx, u16 vid)
306e9dda2cfSMarcin Szycik {
307e9dda2cfSMarcin Szycik struct ice_pf *pf = bridge->br_offloads->pf;
308e9dda2cfSMarcin Szycik struct device *dev = ice_pf_to_dev(pf);
309e9dda2cfSMarcin Szycik struct ice_esw_br_port *port;
310e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan;
311e9dda2cfSMarcin Szycik
312e9dda2cfSMarcin Szycik port = xa_load(&bridge->ports, vsi_idx);
313e9dda2cfSMarcin Szycik if (!port) {
314e9dda2cfSMarcin Szycik dev_info(dev, "Bridge port lookup failed (vsi=%u)\n", vsi_idx);
315e9dda2cfSMarcin Szycik return ERR_PTR(-EINVAL);
316e9dda2cfSMarcin Szycik }
317e9dda2cfSMarcin Szycik
318e9dda2cfSMarcin Szycik vlan = xa_load(&port->vlans, vid);
319e9dda2cfSMarcin Szycik if (!vlan) {
320e9dda2cfSMarcin Szycik dev_info(dev, "Bridge port vlan metadata lookup failed (vsi=%u)\n",
321e9dda2cfSMarcin Szycik vsi_idx);
322e9dda2cfSMarcin Szycik return ERR_PTR(-EINVAL);
323e9dda2cfSMarcin Szycik }
324e9dda2cfSMarcin Szycik
325e9dda2cfSMarcin Szycik return vlan;
326e9dda2cfSMarcin Szycik }
327e9dda2cfSMarcin Szycik
3287c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_entry_delete(struct ice_esw_br * bridge,struct ice_esw_br_fdb_entry * fdb_entry)3297c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_delete(struct ice_esw_br *bridge,
3307c945a1aSWojciech Drewek struct ice_esw_br_fdb_entry *fdb_entry)
3317c945a1aSWojciech Drewek {
3327c945a1aSWojciech Drewek struct ice_pf *pf = bridge->br_offloads->pf;
3337c945a1aSWojciech Drewek
3347c945a1aSWojciech Drewek rhashtable_remove_fast(&bridge->fdb_ht, &fdb_entry->ht_node,
3357c945a1aSWojciech Drewek ice_fdb_ht_params);
3367c945a1aSWojciech Drewek list_del(&fdb_entry->list);
3377c945a1aSWojciech Drewek
3387c945a1aSWojciech Drewek ice_eswitch_br_flow_delete(pf, fdb_entry->flow);
3397c945a1aSWojciech Drewek
3407c945a1aSWojciech Drewek kfree(fdb_entry);
3417c945a1aSWojciech Drewek }
3427c945a1aSWojciech Drewek
3437c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_offload_notify(struct net_device * dev,const unsigned char * mac,u16 vid,unsigned long val)3447c945a1aSWojciech Drewek ice_eswitch_br_fdb_offload_notify(struct net_device *dev,
3457c945a1aSWojciech Drewek const unsigned char *mac, u16 vid,
3467c945a1aSWojciech Drewek unsigned long val)
3477c945a1aSWojciech Drewek {
3487c945a1aSWojciech Drewek struct switchdev_notifier_fdb_info fdb_info = {
3497c945a1aSWojciech Drewek .addr = mac,
3507c945a1aSWojciech Drewek .vid = vid,
3517c945a1aSWojciech Drewek .offloaded = true,
3527c945a1aSWojciech Drewek };
3537c945a1aSWojciech Drewek
3547c945a1aSWojciech Drewek call_switchdev_notifiers(val, dev, &fdb_info.info, NULL);
3557c945a1aSWojciech Drewek }
3567c945a1aSWojciech Drewek
3577c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_entry_notify_and_cleanup(struct ice_esw_br * bridge,struct ice_esw_br_fdb_entry * entry)3587c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_notify_and_cleanup(struct ice_esw_br *bridge,
3597c945a1aSWojciech Drewek struct ice_esw_br_fdb_entry *entry)
3607c945a1aSWojciech Drewek {
3617c945a1aSWojciech Drewek if (!(entry->flags & ICE_ESWITCH_BR_FDB_ADDED_BY_USER))
3627c945a1aSWojciech Drewek ice_eswitch_br_fdb_offload_notify(entry->dev, entry->data.addr,
3637c945a1aSWojciech Drewek entry->data.vid,
3647c945a1aSWojciech Drewek SWITCHDEV_FDB_DEL_TO_BRIDGE);
3657c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_delete(bridge, entry);
3667c945a1aSWojciech Drewek }
3677c945a1aSWojciech Drewek
3687c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_entry_find_and_delete(struct ice_esw_br * bridge,const unsigned char * mac,u16 vid)3697c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_find_and_delete(struct ice_esw_br *bridge,
3707c945a1aSWojciech Drewek const unsigned char *mac, u16 vid)
3717c945a1aSWojciech Drewek {
3727c945a1aSWojciech Drewek struct ice_pf *pf = bridge->br_offloads->pf;
3737c945a1aSWojciech Drewek struct ice_esw_br_fdb_entry *fdb_entry;
3747c945a1aSWojciech Drewek struct device *dev = ice_pf_to_dev(pf);
3757c945a1aSWojciech Drewek
3767c945a1aSWojciech Drewek fdb_entry = ice_eswitch_br_fdb_find(bridge, mac, vid);
3777c945a1aSWojciech Drewek if (!fdb_entry) {
3787c945a1aSWojciech Drewek dev_err(dev, "FDB entry with mac: %pM and vid: %u not found\n",
3797c945a1aSWojciech Drewek mac, vid);
3807c945a1aSWojciech Drewek return;
3817c945a1aSWojciech Drewek }
3827c945a1aSWojciech Drewek
383d129c2a2SPawel Chmielewski trace_ice_eswitch_br_fdb_entry_find_and_delete(fdb_entry);
3847c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_notify_and_cleanup(bridge, fdb_entry);
3857c945a1aSWojciech Drewek }
3867c945a1aSWojciech Drewek
3877c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_entry_create(struct net_device * netdev,struct ice_esw_br_port * br_port,bool added_by_user,const unsigned char * mac,u16 vid)3887c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_create(struct net_device *netdev,
3897c945a1aSWojciech Drewek struct ice_esw_br_port *br_port,
3907c945a1aSWojciech Drewek bool added_by_user,
3917c945a1aSWojciech Drewek const unsigned char *mac, u16 vid)
3927c945a1aSWojciech Drewek {
3937c945a1aSWojciech Drewek struct ice_esw_br *bridge = br_port->bridge;
3947c945a1aSWojciech Drewek struct ice_pf *pf = bridge->br_offloads->pf;
3957c945a1aSWojciech Drewek struct device *dev = ice_pf_to_dev(pf);
3967c945a1aSWojciech Drewek struct ice_esw_br_fdb_entry *fdb_entry;
3977c945a1aSWojciech Drewek struct ice_esw_br_flow *flow;
398e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan;
3997c945a1aSWojciech Drewek struct ice_hw *hw = &pf->hw;
4007c945a1aSWojciech Drewek unsigned long event;
4017c945a1aSWojciech Drewek int err;
4027c945a1aSWojciech Drewek
403e9dda2cfSMarcin Szycik /* untagged filtering is not yet supported */
404e9dda2cfSMarcin Szycik if (!(bridge->flags & ICE_ESWITCH_BR_VLAN_FILTERING) && vid)
405e9dda2cfSMarcin Szycik return;
406e9dda2cfSMarcin Szycik
407e9dda2cfSMarcin Szycik if ((bridge->flags & ICE_ESWITCH_BR_VLAN_FILTERING)) {
408e9dda2cfSMarcin Szycik vlan = ice_esw_br_port_vlan_lookup(bridge, br_port->vsi_idx,
409e9dda2cfSMarcin Szycik vid);
410e9dda2cfSMarcin Szycik if (IS_ERR(vlan)) {
411e9dda2cfSMarcin Szycik dev_err(dev, "Failed to find vlan lookup, err: %ld\n",
412e9dda2cfSMarcin Szycik PTR_ERR(vlan));
413e9dda2cfSMarcin Szycik return;
414e9dda2cfSMarcin Szycik }
415e9dda2cfSMarcin Szycik }
416e9dda2cfSMarcin Szycik
4177c945a1aSWojciech Drewek fdb_entry = ice_eswitch_br_fdb_find(bridge, mac, vid);
4187c945a1aSWojciech Drewek if (fdb_entry)
4197c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_notify_and_cleanup(bridge, fdb_entry);
4207c945a1aSWojciech Drewek
4217c945a1aSWojciech Drewek fdb_entry = kzalloc(sizeof(*fdb_entry), GFP_KERNEL);
4227c945a1aSWojciech Drewek if (!fdb_entry) {
4237c945a1aSWojciech Drewek err = -ENOMEM;
4247c945a1aSWojciech Drewek goto err_exit;
4257c945a1aSWojciech Drewek }
4267c945a1aSWojciech Drewek
4277c945a1aSWojciech Drewek flow = ice_eswitch_br_flow_create(dev, hw, br_port->vsi_idx,
428e9dda2cfSMarcin Szycik br_port->type, mac, vid);
4297c945a1aSWojciech Drewek if (IS_ERR(flow)) {
4307c945a1aSWojciech Drewek err = PTR_ERR(flow);
4317c945a1aSWojciech Drewek goto err_add_flow;
4327c945a1aSWojciech Drewek }
4337c945a1aSWojciech Drewek
4347c945a1aSWojciech Drewek ether_addr_copy(fdb_entry->data.addr, mac);
4357c945a1aSWojciech Drewek fdb_entry->data.vid = vid;
4367c945a1aSWojciech Drewek fdb_entry->br_port = br_port;
4377c945a1aSWojciech Drewek fdb_entry->flow = flow;
4387c945a1aSWojciech Drewek fdb_entry->dev = netdev;
439e42c6e0cSMichal Swiatkowski fdb_entry->last_use = jiffies;
4407c945a1aSWojciech Drewek event = SWITCHDEV_FDB_ADD_TO_BRIDGE;
4417c945a1aSWojciech Drewek
4427c945a1aSWojciech Drewek if (added_by_user) {
4437c945a1aSWojciech Drewek fdb_entry->flags |= ICE_ESWITCH_BR_FDB_ADDED_BY_USER;
4447c945a1aSWojciech Drewek event = SWITCHDEV_FDB_OFFLOADED;
4457c945a1aSWojciech Drewek }
4467c945a1aSWojciech Drewek
4477c945a1aSWojciech Drewek err = rhashtable_insert_fast(&bridge->fdb_ht, &fdb_entry->ht_node,
4487c945a1aSWojciech Drewek ice_fdb_ht_params);
4497c945a1aSWojciech Drewek if (err)
4507c945a1aSWojciech Drewek goto err_fdb_insert;
4517c945a1aSWojciech Drewek
4527c945a1aSWojciech Drewek list_add(&fdb_entry->list, &bridge->fdb_list);
453d129c2a2SPawel Chmielewski trace_ice_eswitch_br_fdb_entry_create(fdb_entry);
4547c945a1aSWojciech Drewek
4557c945a1aSWojciech Drewek ice_eswitch_br_fdb_offload_notify(netdev, mac, vid, event);
4567c945a1aSWojciech Drewek
4577c945a1aSWojciech Drewek return;
4587c945a1aSWojciech Drewek
4597c945a1aSWojciech Drewek err_fdb_insert:
4607c945a1aSWojciech Drewek ice_eswitch_br_flow_delete(pf, flow);
4617c945a1aSWojciech Drewek err_add_flow:
4627c945a1aSWojciech Drewek kfree(fdb_entry);
4637c945a1aSWojciech Drewek err_exit:
4647c945a1aSWojciech Drewek dev_err(dev, "Failed to create fdb entry, err: %d\n", err);
4657c945a1aSWojciech Drewek }
4667c945a1aSWojciech Drewek
4677c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_work_dealloc(struct ice_esw_br_fdb_work * fdb_work)4687c945a1aSWojciech Drewek ice_eswitch_br_fdb_work_dealloc(struct ice_esw_br_fdb_work *fdb_work)
4697c945a1aSWojciech Drewek {
4707c945a1aSWojciech Drewek kfree(fdb_work->fdb_info.addr);
4717c945a1aSWojciech Drewek kfree(fdb_work);
4727c945a1aSWojciech Drewek }
4737c945a1aSWojciech Drewek
4747c945a1aSWojciech Drewek static void
ice_eswitch_br_fdb_event_work(struct work_struct * work)4757c945a1aSWojciech Drewek ice_eswitch_br_fdb_event_work(struct work_struct *work)
4767c945a1aSWojciech Drewek {
4777c945a1aSWojciech Drewek struct ice_esw_br_fdb_work *fdb_work = ice_work_to_fdb_work(work);
4787c945a1aSWojciech Drewek bool added_by_user = fdb_work->fdb_info.added_by_user;
4797c945a1aSWojciech Drewek const unsigned char *mac = fdb_work->fdb_info.addr;
4807c945a1aSWojciech Drewek u16 vid = fdb_work->fdb_info.vid;
4817c945a1aSWojciech Drewek struct ice_esw_br_port *br_port;
4827c945a1aSWojciech Drewek
4837c945a1aSWojciech Drewek rtnl_lock();
4847c945a1aSWojciech Drewek
4857c945a1aSWojciech Drewek br_port = ice_eswitch_br_netdev_to_port(fdb_work->dev);
4867c945a1aSWojciech Drewek if (!br_port)
4877c945a1aSWojciech Drewek goto err_exit;
4887c945a1aSWojciech Drewek
4897c945a1aSWojciech Drewek switch (fdb_work->event) {
4907c945a1aSWojciech Drewek case SWITCHDEV_FDB_ADD_TO_DEVICE:
4917c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_create(fdb_work->dev, br_port,
4927c945a1aSWojciech Drewek added_by_user, mac, vid);
4937c945a1aSWojciech Drewek break;
4947c945a1aSWojciech Drewek case SWITCHDEV_FDB_DEL_TO_DEVICE:
4957c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_find_and_delete(br_port->bridge,
4967c945a1aSWojciech Drewek mac, vid);
4977c945a1aSWojciech Drewek break;
4987c945a1aSWojciech Drewek default:
4997c945a1aSWojciech Drewek goto err_exit;
5007c945a1aSWojciech Drewek }
5017c945a1aSWojciech Drewek
5027c945a1aSWojciech Drewek err_exit:
5037c945a1aSWojciech Drewek rtnl_unlock();
5047c945a1aSWojciech Drewek dev_put(fdb_work->dev);
5057c945a1aSWojciech Drewek ice_eswitch_br_fdb_work_dealloc(fdb_work);
5067c945a1aSWojciech Drewek }
5077c945a1aSWojciech Drewek
5087c945a1aSWojciech Drewek static struct ice_esw_br_fdb_work *
ice_eswitch_br_fdb_work_alloc(struct switchdev_notifier_fdb_info * fdb_info,struct net_device * dev,unsigned long event)5097c945a1aSWojciech Drewek ice_eswitch_br_fdb_work_alloc(struct switchdev_notifier_fdb_info *fdb_info,
5107c945a1aSWojciech Drewek struct net_device *dev,
5117c945a1aSWojciech Drewek unsigned long event)
5127c945a1aSWojciech Drewek {
5137c945a1aSWojciech Drewek struct ice_esw_br_fdb_work *work;
5147c945a1aSWojciech Drewek unsigned char *mac;
5157c945a1aSWojciech Drewek
5167c945a1aSWojciech Drewek work = kzalloc(sizeof(*work), GFP_ATOMIC);
5177c945a1aSWojciech Drewek if (!work)
5187c945a1aSWojciech Drewek return ERR_PTR(-ENOMEM);
5197c945a1aSWojciech Drewek
5207c945a1aSWojciech Drewek INIT_WORK(&work->work, ice_eswitch_br_fdb_event_work);
5217c945a1aSWojciech Drewek memcpy(&work->fdb_info, fdb_info, sizeof(work->fdb_info));
5227c945a1aSWojciech Drewek
5237c945a1aSWojciech Drewek mac = kzalloc(ETH_ALEN, GFP_ATOMIC);
5247c945a1aSWojciech Drewek if (!mac) {
5257c945a1aSWojciech Drewek kfree(work);
5267c945a1aSWojciech Drewek return ERR_PTR(-ENOMEM);
5277c945a1aSWojciech Drewek }
5287c945a1aSWojciech Drewek
5297c945a1aSWojciech Drewek ether_addr_copy(mac, fdb_info->addr);
5307c945a1aSWojciech Drewek work->fdb_info.addr = mac;
5317c945a1aSWojciech Drewek work->event = event;
5327c945a1aSWojciech Drewek work->dev = dev;
5337c945a1aSWojciech Drewek
5347c945a1aSWojciech Drewek return work;
5357c945a1aSWojciech Drewek }
5367c945a1aSWojciech Drewek
5377c945a1aSWojciech Drewek static int
ice_eswitch_br_switchdev_event(struct notifier_block * nb,unsigned long event,void * ptr)5387c945a1aSWojciech Drewek ice_eswitch_br_switchdev_event(struct notifier_block *nb,
5397c945a1aSWojciech Drewek unsigned long event, void *ptr)
5407c945a1aSWojciech Drewek {
5417c945a1aSWojciech Drewek struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
5427c945a1aSWojciech Drewek struct switchdev_notifier_fdb_info *fdb_info;
5437c945a1aSWojciech Drewek struct switchdev_notifier_info *info = ptr;
5447c945a1aSWojciech Drewek struct ice_esw_br_offloads *br_offloads;
5457c945a1aSWojciech Drewek struct ice_esw_br_fdb_work *work;
5467c945a1aSWojciech Drewek struct netlink_ext_ack *extack;
5477c945a1aSWojciech Drewek struct net_device *upper;
5487c945a1aSWojciech Drewek
5497c945a1aSWojciech Drewek br_offloads = ice_nb_to_br_offloads(nb, switchdev_nb);
5507c945a1aSWojciech Drewek extack = switchdev_notifier_info_to_extack(ptr);
5517c945a1aSWojciech Drewek
5527c945a1aSWojciech Drewek upper = netdev_master_upper_dev_get_rcu(dev);
5537c945a1aSWojciech Drewek if (!upper)
5547c945a1aSWojciech Drewek return NOTIFY_DONE;
5557c945a1aSWojciech Drewek
5567c945a1aSWojciech Drewek if (!netif_is_bridge_master(upper))
5577c945a1aSWojciech Drewek return NOTIFY_DONE;
5587c945a1aSWojciech Drewek
5597c945a1aSWojciech Drewek if (!ice_eswitch_br_is_dev_valid(dev))
5607c945a1aSWojciech Drewek return NOTIFY_DONE;
5617c945a1aSWojciech Drewek
5627c945a1aSWojciech Drewek if (!ice_eswitch_br_netdev_to_port(dev))
5637c945a1aSWojciech Drewek return NOTIFY_DONE;
5647c945a1aSWojciech Drewek
5657c945a1aSWojciech Drewek switch (event) {
5667c945a1aSWojciech Drewek case SWITCHDEV_FDB_ADD_TO_DEVICE:
5677c945a1aSWojciech Drewek case SWITCHDEV_FDB_DEL_TO_DEVICE:
5687c945a1aSWojciech Drewek fdb_info = container_of(info, typeof(*fdb_info), info);
5697c945a1aSWojciech Drewek
5707c945a1aSWojciech Drewek work = ice_eswitch_br_fdb_work_alloc(fdb_info, dev, event);
5717c945a1aSWojciech Drewek if (IS_ERR(work)) {
5727c945a1aSWojciech Drewek NL_SET_ERR_MSG_MOD(extack, "Failed to init switchdev fdb work");
5737c945a1aSWojciech Drewek return notifier_from_errno(PTR_ERR(work));
5747c945a1aSWojciech Drewek }
5757c945a1aSWojciech Drewek dev_hold(dev);
5767c945a1aSWojciech Drewek
5777c945a1aSWojciech Drewek queue_work(br_offloads->wq, &work->work);
5787c945a1aSWojciech Drewek break;
5797c945a1aSWojciech Drewek default:
5807c945a1aSWojciech Drewek break;
5817c945a1aSWojciech Drewek }
5827c945a1aSWojciech Drewek return NOTIFY_DONE;
5837c945a1aSWojciech Drewek }
5847c945a1aSWojciech Drewek
ice_eswitch_br_fdb_flush(struct ice_esw_br * bridge)585*29d7aa18SWojciech Drewek void ice_eswitch_br_fdb_flush(struct ice_esw_br *bridge)
586e9dda2cfSMarcin Szycik {
587e9dda2cfSMarcin Szycik struct ice_esw_br_fdb_entry *entry, *tmp;
588e9dda2cfSMarcin Szycik
589*29d7aa18SWojciech Drewek if (!bridge)
590*29d7aa18SWojciech Drewek return;
591*29d7aa18SWojciech Drewek
592e9dda2cfSMarcin Szycik list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list)
593e9dda2cfSMarcin Szycik ice_eswitch_br_fdb_entry_notify_and_cleanup(bridge, entry);
594e9dda2cfSMarcin Szycik }
595e9dda2cfSMarcin Szycik
596e9dda2cfSMarcin Szycik static void
ice_eswitch_br_vlan_filtering_set(struct ice_esw_br * bridge,bool enable)597e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_filtering_set(struct ice_esw_br *bridge, bool enable)
598e9dda2cfSMarcin Szycik {
599e9dda2cfSMarcin Szycik if (enable == !!(bridge->flags & ICE_ESWITCH_BR_VLAN_FILTERING))
600e9dda2cfSMarcin Szycik return;
601e9dda2cfSMarcin Szycik
602e9dda2cfSMarcin Szycik ice_eswitch_br_fdb_flush(bridge);
603e9dda2cfSMarcin Szycik if (enable)
604e9dda2cfSMarcin Szycik bridge->flags |= ICE_ESWITCH_BR_VLAN_FILTERING;
605e9dda2cfSMarcin Szycik else
606e9dda2cfSMarcin Szycik bridge->flags &= ~ICE_ESWITCH_BR_VLAN_FILTERING;
607e9dda2cfSMarcin Szycik }
608e9dda2cfSMarcin Szycik
609e9dda2cfSMarcin Szycik static void
ice_eswitch_br_clear_pvid(struct ice_esw_br_port * port)6102946204bSMichal Swiatkowski ice_eswitch_br_clear_pvid(struct ice_esw_br_port *port)
6112946204bSMichal Swiatkowski {
6122946204bSMichal Swiatkowski struct ice_vlan port_vlan = ICE_VLAN(ETH_P_8021Q, port->pvid, 0);
6132946204bSMichal Swiatkowski struct ice_vsi_vlan_ops *vlan_ops;
6142946204bSMichal Swiatkowski
6152946204bSMichal Swiatkowski vlan_ops = ice_get_compat_vsi_vlan_ops(port->vsi);
6162946204bSMichal Swiatkowski
6172946204bSMichal Swiatkowski vlan_ops->del_vlan(port->vsi, &port_vlan);
6182946204bSMichal Swiatkowski vlan_ops->clear_port_vlan(port->vsi);
6192946204bSMichal Swiatkowski
6202946204bSMichal Swiatkowski ice_vf_vsi_disable_port_vlan(port->vsi);
6212946204bSMichal Swiatkowski
6222946204bSMichal Swiatkowski port->pvid = 0;
6232946204bSMichal Swiatkowski }
6242946204bSMichal Swiatkowski
6252946204bSMichal Swiatkowski static void
ice_eswitch_br_vlan_cleanup(struct ice_esw_br_port * port,struct ice_esw_br_vlan * vlan)626e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_cleanup(struct ice_esw_br_port *port,
627e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan)
628e9dda2cfSMarcin Szycik {
629e9dda2cfSMarcin Szycik struct ice_esw_br_fdb_entry *fdb_entry, *tmp;
630e9dda2cfSMarcin Szycik struct ice_esw_br *bridge = port->bridge;
631e9dda2cfSMarcin Szycik
632d129c2a2SPawel Chmielewski trace_ice_eswitch_br_vlan_cleanup(vlan);
633d129c2a2SPawel Chmielewski
634e9dda2cfSMarcin Szycik list_for_each_entry_safe(fdb_entry, tmp, &bridge->fdb_list, list) {
635e9dda2cfSMarcin Szycik if (vlan->vid == fdb_entry->data.vid)
636e9dda2cfSMarcin Szycik ice_eswitch_br_fdb_entry_delete(bridge, fdb_entry);
637e9dda2cfSMarcin Szycik }
638e9dda2cfSMarcin Szycik
639e9dda2cfSMarcin Szycik xa_erase(&port->vlans, vlan->vid);
6402946204bSMichal Swiatkowski if (port->pvid == vlan->vid)
6412946204bSMichal Swiatkowski ice_eswitch_br_clear_pvid(port);
642e9dda2cfSMarcin Szycik kfree(vlan);
643e9dda2cfSMarcin Szycik }
644e9dda2cfSMarcin Szycik
ice_eswitch_br_port_vlans_flush(struct ice_esw_br_port * port)645e9dda2cfSMarcin Szycik static void ice_eswitch_br_port_vlans_flush(struct ice_esw_br_port *port)
646e9dda2cfSMarcin Szycik {
647e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan;
648e9dda2cfSMarcin Szycik unsigned long index;
649e9dda2cfSMarcin Szycik
650e9dda2cfSMarcin Szycik xa_for_each(&port->vlans, index, vlan)
651e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_cleanup(port, vlan);
652e9dda2cfSMarcin Szycik }
653e9dda2cfSMarcin Szycik
6542946204bSMichal Swiatkowski static int
ice_eswitch_br_set_pvid(struct ice_esw_br_port * port,struct ice_esw_br_vlan * vlan)6552946204bSMichal Swiatkowski ice_eswitch_br_set_pvid(struct ice_esw_br_port *port,
6562946204bSMichal Swiatkowski struct ice_esw_br_vlan *vlan)
6572946204bSMichal Swiatkowski {
6582946204bSMichal Swiatkowski struct ice_vlan port_vlan = ICE_VLAN(ETH_P_8021Q, vlan->vid, 0);
6592946204bSMichal Swiatkowski struct device *dev = ice_pf_to_dev(port->vsi->back);
6602946204bSMichal Swiatkowski struct ice_vsi_vlan_ops *vlan_ops;
6612946204bSMichal Swiatkowski int err;
6622946204bSMichal Swiatkowski
6632946204bSMichal Swiatkowski if (port->pvid == vlan->vid || vlan->vid == 1)
6642946204bSMichal Swiatkowski return 0;
6652946204bSMichal Swiatkowski
6662946204bSMichal Swiatkowski /* Setting port vlan on uplink isn't supported by hw */
6672946204bSMichal Swiatkowski if (port->type == ICE_ESWITCH_BR_UPLINK_PORT)
6682946204bSMichal Swiatkowski return -EOPNOTSUPP;
6692946204bSMichal Swiatkowski
6702946204bSMichal Swiatkowski if (port->pvid) {
6712946204bSMichal Swiatkowski dev_info(dev,
6722946204bSMichal Swiatkowski "Port VLAN (vsi=%u, vid=%u) already exists on the port, remove it before adding new one\n",
6732946204bSMichal Swiatkowski port->vsi_idx, port->pvid);
6742946204bSMichal Swiatkowski return -EEXIST;
6752946204bSMichal Swiatkowski }
6762946204bSMichal Swiatkowski
6772946204bSMichal Swiatkowski ice_vf_vsi_enable_port_vlan(port->vsi);
6782946204bSMichal Swiatkowski
6792946204bSMichal Swiatkowski vlan_ops = ice_get_compat_vsi_vlan_ops(port->vsi);
6802946204bSMichal Swiatkowski err = vlan_ops->set_port_vlan(port->vsi, &port_vlan);
6812946204bSMichal Swiatkowski if (err)
6822946204bSMichal Swiatkowski return err;
6832946204bSMichal Swiatkowski
6842946204bSMichal Swiatkowski err = vlan_ops->add_vlan(port->vsi, &port_vlan);
6852946204bSMichal Swiatkowski if (err)
6862946204bSMichal Swiatkowski return err;
6872946204bSMichal Swiatkowski
6882946204bSMichal Swiatkowski ice_eswitch_br_port_vlans_flush(port);
6892946204bSMichal Swiatkowski port->pvid = vlan->vid;
6902946204bSMichal Swiatkowski
6912946204bSMichal Swiatkowski return 0;
6922946204bSMichal Swiatkowski }
6932946204bSMichal Swiatkowski
694e9dda2cfSMarcin Szycik static struct ice_esw_br_vlan *
ice_eswitch_br_vlan_create(u16 vid,u16 flags,struct ice_esw_br_port * port)695e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_create(u16 vid, u16 flags, struct ice_esw_br_port *port)
696e9dda2cfSMarcin Szycik {
6972946204bSMichal Swiatkowski struct device *dev = ice_pf_to_dev(port->vsi->back);
698e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan;
699e9dda2cfSMarcin Szycik int err;
700e9dda2cfSMarcin Szycik
701e9dda2cfSMarcin Szycik vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
702e9dda2cfSMarcin Szycik if (!vlan)
703e9dda2cfSMarcin Szycik return ERR_PTR(-ENOMEM);
704e9dda2cfSMarcin Szycik
705e9dda2cfSMarcin Szycik vlan->vid = vid;
706e9dda2cfSMarcin Szycik vlan->flags = flags;
7072946204bSMichal Swiatkowski if ((flags & BRIDGE_VLAN_INFO_PVID) &&
7082946204bSMichal Swiatkowski (flags & BRIDGE_VLAN_INFO_UNTAGGED)) {
7092946204bSMichal Swiatkowski err = ice_eswitch_br_set_pvid(port, vlan);
7102946204bSMichal Swiatkowski if (err)
7112946204bSMichal Swiatkowski goto err_set_pvid;
7122946204bSMichal Swiatkowski } else if ((flags & BRIDGE_VLAN_INFO_PVID) ||
7132946204bSMichal Swiatkowski (flags & BRIDGE_VLAN_INFO_UNTAGGED)) {
7142946204bSMichal Swiatkowski dev_info(dev, "VLAN push and pop are supported only simultaneously\n");
7152946204bSMichal Swiatkowski err = -EOPNOTSUPP;
7162946204bSMichal Swiatkowski goto err_set_pvid;
717e9dda2cfSMarcin Szycik }
718e9dda2cfSMarcin Szycik
7192946204bSMichal Swiatkowski err = xa_insert(&port->vlans, vlan->vid, vlan, GFP_KERNEL);
7202946204bSMichal Swiatkowski if (err)
7212946204bSMichal Swiatkowski goto err_insert;
7222946204bSMichal Swiatkowski
723d129c2a2SPawel Chmielewski trace_ice_eswitch_br_vlan_create(vlan);
724d129c2a2SPawel Chmielewski
725e9dda2cfSMarcin Szycik return vlan;
7262946204bSMichal Swiatkowski
7272946204bSMichal Swiatkowski err_insert:
7282946204bSMichal Swiatkowski if (port->pvid)
7292946204bSMichal Swiatkowski ice_eswitch_br_clear_pvid(port);
7302946204bSMichal Swiatkowski err_set_pvid:
7312946204bSMichal Swiatkowski kfree(vlan);
7322946204bSMichal Swiatkowski return ERR_PTR(err);
733e9dda2cfSMarcin Szycik }
734e9dda2cfSMarcin Szycik
735e9dda2cfSMarcin Szycik static int
ice_eswitch_br_port_vlan_add(struct ice_esw_br * bridge,u16 vsi_idx,u16 vid,u16 flags,struct netlink_ext_ack * extack)736e9dda2cfSMarcin Szycik ice_eswitch_br_port_vlan_add(struct ice_esw_br *bridge, u16 vsi_idx, u16 vid,
737e9dda2cfSMarcin Szycik u16 flags, struct netlink_ext_ack *extack)
738e9dda2cfSMarcin Szycik {
739e9dda2cfSMarcin Szycik struct ice_esw_br_port *port;
740e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan;
741e9dda2cfSMarcin Szycik
742e9dda2cfSMarcin Szycik port = xa_load(&bridge->ports, vsi_idx);
743e9dda2cfSMarcin Szycik if (!port)
744e9dda2cfSMarcin Szycik return -EINVAL;
745e9dda2cfSMarcin Szycik
7462946204bSMichal Swiatkowski if (port->pvid) {
7472946204bSMichal Swiatkowski dev_info(ice_pf_to_dev(port->vsi->back),
7482946204bSMichal Swiatkowski "Port VLAN (vsi=%u, vid=%d) exists on the port, remove it to add trunk VLANs\n",
7492946204bSMichal Swiatkowski port->vsi_idx, port->pvid);
7502946204bSMichal Swiatkowski return -EEXIST;
7512946204bSMichal Swiatkowski }
7522946204bSMichal Swiatkowski
753e9dda2cfSMarcin Szycik vlan = xa_load(&port->vlans, vid);
754e9dda2cfSMarcin Szycik if (vlan) {
755e9dda2cfSMarcin Szycik if (vlan->flags == flags)
756e9dda2cfSMarcin Szycik return 0;
757e9dda2cfSMarcin Szycik
758e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_cleanup(port, vlan);
759e9dda2cfSMarcin Szycik }
760e9dda2cfSMarcin Szycik
761e9dda2cfSMarcin Szycik vlan = ice_eswitch_br_vlan_create(vid, flags, port);
762e9dda2cfSMarcin Szycik if (IS_ERR(vlan)) {
763e9dda2cfSMarcin Szycik NL_SET_ERR_MSG_FMT_MOD(extack, "Failed to create VLAN entry, vid: %u, vsi: %u",
764e9dda2cfSMarcin Szycik vid, vsi_idx);
765e9dda2cfSMarcin Szycik return PTR_ERR(vlan);
766e9dda2cfSMarcin Szycik }
767e9dda2cfSMarcin Szycik
768e9dda2cfSMarcin Szycik return 0;
769e9dda2cfSMarcin Szycik }
770e9dda2cfSMarcin Szycik
771e9dda2cfSMarcin Szycik static void
ice_eswitch_br_port_vlan_del(struct ice_esw_br * bridge,u16 vsi_idx,u16 vid)772e9dda2cfSMarcin Szycik ice_eswitch_br_port_vlan_del(struct ice_esw_br *bridge, u16 vsi_idx, u16 vid)
773e9dda2cfSMarcin Szycik {
774e9dda2cfSMarcin Szycik struct ice_esw_br_port *port;
775e9dda2cfSMarcin Szycik struct ice_esw_br_vlan *vlan;
776e9dda2cfSMarcin Szycik
777e9dda2cfSMarcin Szycik port = xa_load(&bridge->ports, vsi_idx);
778e9dda2cfSMarcin Szycik if (!port)
779e9dda2cfSMarcin Szycik return;
780e9dda2cfSMarcin Szycik
781e9dda2cfSMarcin Szycik vlan = xa_load(&port->vlans, vid);
782e9dda2cfSMarcin Szycik if (!vlan)
783e9dda2cfSMarcin Szycik return;
784e9dda2cfSMarcin Szycik
785e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_cleanup(port, vlan);
786e9dda2cfSMarcin Szycik }
787e9dda2cfSMarcin Szycik
788e9dda2cfSMarcin Szycik static int
ice_eswitch_br_port_obj_add(struct net_device * netdev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)789e9dda2cfSMarcin Szycik ice_eswitch_br_port_obj_add(struct net_device *netdev, const void *ctx,
790e9dda2cfSMarcin Szycik const struct switchdev_obj *obj,
791e9dda2cfSMarcin Szycik struct netlink_ext_ack *extack)
792e9dda2cfSMarcin Szycik {
793e9dda2cfSMarcin Szycik struct ice_esw_br_port *br_port = ice_eswitch_br_netdev_to_port(netdev);
794e9dda2cfSMarcin Szycik struct switchdev_obj_port_vlan *vlan;
795e9dda2cfSMarcin Szycik int err;
796e9dda2cfSMarcin Szycik
797e9dda2cfSMarcin Szycik if (!br_port)
798e9dda2cfSMarcin Szycik return -EINVAL;
799e9dda2cfSMarcin Szycik
800e9dda2cfSMarcin Szycik switch (obj->id) {
801e9dda2cfSMarcin Szycik case SWITCHDEV_OBJ_ID_PORT_VLAN:
802e9dda2cfSMarcin Szycik vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
803e9dda2cfSMarcin Szycik err = ice_eswitch_br_port_vlan_add(br_port->bridge,
804e9dda2cfSMarcin Szycik br_port->vsi_idx, vlan->vid,
805e9dda2cfSMarcin Szycik vlan->flags, extack);
806e9dda2cfSMarcin Szycik return err;
807e9dda2cfSMarcin Szycik default:
808e9dda2cfSMarcin Szycik return -EOPNOTSUPP;
809e9dda2cfSMarcin Szycik }
810e9dda2cfSMarcin Szycik }
811e9dda2cfSMarcin Szycik
812e9dda2cfSMarcin Szycik static int
ice_eswitch_br_port_obj_del(struct net_device * netdev,const void * ctx,const struct switchdev_obj * obj)813e9dda2cfSMarcin Szycik ice_eswitch_br_port_obj_del(struct net_device *netdev, const void *ctx,
814e9dda2cfSMarcin Szycik const struct switchdev_obj *obj)
815e9dda2cfSMarcin Szycik {
816e9dda2cfSMarcin Szycik struct ice_esw_br_port *br_port = ice_eswitch_br_netdev_to_port(netdev);
817e9dda2cfSMarcin Szycik struct switchdev_obj_port_vlan *vlan;
818e9dda2cfSMarcin Szycik
819e9dda2cfSMarcin Szycik if (!br_port)
820e9dda2cfSMarcin Szycik return -EINVAL;
821e9dda2cfSMarcin Szycik
822e9dda2cfSMarcin Szycik switch (obj->id) {
823e9dda2cfSMarcin Szycik case SWITCHDEV_OBJ_ID_PORT_VLAN:
824e9dda2cfSMarcin Szycik vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
825e9dda2cfSMarcin Szycik ice_eswitch_br_port_vlan_del(br_port->bridge, br_port->vsi_idx,
826e9dda2cfSMarcin Szycik vlan->vid);
827e9dda2cfSMarcin Szycik return 0;
828e9dda2cfSMarcin Szycik default:
829e9dda2cfSMarcin Szycik return -EOPNOTSUPP;
830e9dda2cfSMarcin Szycik }
831e9dda2cfSMarcin Szycik }
832e9dda2cfSMarcin Szycik
833e9dda2cfSMarcin Szycik static int
ice_eswitch_br_port_obj_attr_set(struct net_device * netdev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)834e9dda2cfSMarcin Szycik ice_eswitch_br_port_obj_attr_set(struct net_device *netdev, const void *ctx,
835e9dda2cfSMarcin Szycik const struct switchdev_attr *attr,
836e9dda2cfSMarcin Szycik struct netlink_ext_ack *extack)
837e9dda2cfSMarcin Szycik {
838e9dda2cfSMarcin Szycik struct ice_esw_br_port *br_port = ice_eswitch_br_netdev_to_port(netdev);
839e9dda2cfSMarcin Szycik
840e9dda2cfSMarcin Szycik if (!br_port)
841e9dda2cfSMarcin Szycik return -EINVAL;
842e9dda2cfSMarcin Szycik
843e9dda2cfSMarcin Szycik switch (attr->id) {
844e9dda2cfSMarcin Szycik case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
845e9dda2cfSMarcin Szycik ice_eswitch_br_vlan_filtering_set(br_port->bridge,
846e9dda2cfSMarcin Szycik attr->u.vlan_filtering);
847e9dda2cfSMarcin Szycik return 0;
848e42c6e0cSMichal Swiatkowski case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
849e42c6e0cSMichal Swiatkowski br_port->bridge->ageing_time =
850e42c6e0cSMichal Swiatkowski clock_t_to_jiffies(attr->u.ageing_time);
851e42c6e0cSMichal Swiatkowski return 0;
852e9dda2cfSMarcin Szycik default:
853e9dda2cfSMarcin Szycik return -EOPNOTSUPP;
854e9dda2cfSMarcin Szycik }
855e9dda2cfSMarcin Szycik }
856e9dda2cfSMarcin Szycik
857e9dda2cfSMarcin Szycik static int
ice_eswitch_br_event_blocking(struct notifier_block * nb,unsigned long event,void * ptr)858e9dda2cfSMarcin Szycik ice_eswitch_br_event_blocking(struct notifier_block *nb, unsigned long event,
859e9dda2cfSMarcin Szycik void *ptr)
860e9dda2cfSMarcin Szycik {
861e9dda2cfSMarcin Szycik struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
862e9dda2cfSMarcin Szycik int err;
863e9dda2cfSMarcin Szycik
864e9dda2cfSMarcin Szycik switch (event) {
865e9dda2cfSMarcin Szycik case SWITCHDEV_PORT_OBJ_ADD:
866e9dda2cfSMarcin Szycik err = switchdev_handle_port_obj_add(dev, ptr,
867e9dda2cfSMarcin Szycik ice_eswitch_br_is_dev_valid,
868e9dda2cfSMarcin Szycik ice_eswitch_br_port_obj_add);
869e9dda2cfSMarcin Szycik break;
870e9dda2cfSMarcin Szycik case SWITCHDEV_PORT_OBJ_DEL:
871e9dda2cfSMarcin Szycik err = switchdev_handle_port_obj_del(dev, ptr,
872e9dda2cfSMarcin Szycik ice_eswitch_br_is_dev_valid,
873e9dda2cfSMarcin Szycik ice_eswitch_br_port_obj_del);
874e9dda2cfSMarcin Szycik break;
875e9dda2cfSMarcin Szycik case SWITCHDEV_PORT_ATTR_SET:
876e9dda2cfSMarcin Szycik err = switchdev_handle_port_attr_set(dev, ptr,
877e9dda2cfSMarcin Szycik ice_eswitch_br_is_dev_valid,
878e9dda2cfSMarcin Szycik ice_eswitch_br_port_obj_attr_set);
879e9dda2cfSMarcin Szycik break;
880e9dda2cfSMarcin Szycik default:
881e9dda2cfSMarcin Szycik err = 0;
882e9dda2cfSMarcin Szycik }
883e9dda2cfSMarcin Szycik
884e9dda2cfSMarcin Szycik return notifier_from_errno(err);
885e9dda2cfSMarcin Szycik }
886e9dda2cfSMarcin Szycik
8877c945a1aSWojciech Drewek static void
ice_eswitch_br_port_deinit(struct ice_esw_br * bridge,struct ice_esw_br_port * br_port)888f6e8fb55SWojciech Drewek ice_eswitch_br_port_deinit(struct ice_esw_br *bridge,
889f6e8fb55SWojciech Drewek struct ice_esw_br_port *br_port)
890f6e8fb55SWojciech Drewek {
8917c945a1aSWojciech Drewek struct ice_esw_br_fdb_entry *fdb_entry, *tmp;
892f6e8fb55SWojciech Drewek struct ice_vsi *vsi = br_port->vsi;
893f6e8fb55SWojciech Drewek
8947c945a1aSWojciech Drewek list_for_each_entry_safe(fdb_entry, tmp, &bridge->fdb_list, list) {
8957c945a1aSWojciech Drewek if (br_port == fdb_entry->br_port)
8967c945a1aSWojciech Drewek ice_eswitch_br_fdb_entry_delete(bridge, fdb_entry);
8977c945a1aSWojciech Drewek }
8987c945a1aSWojciech Drewek
899f6e8fb55SWojciech Drewek if (br_port->type == ICE_ESWITCH_BR_UPLINK_PORT && vsi->back)
900f6e8fb55SWojciech Drewek vsi->back->br_port = NULL;
901f6e8fb55SWojciech Drewek else if (vsi->vf && vsi->vf->repr)
902f6e8fb55SWojciech Drewek vsi->vf->repr->br_port = NULL;
903f6e8fb55SWojciech Drewek
904f6e8fb55SWojciech Drewek xa_erase(&bridge->ports, br_port->vsi_idx);
905e9dda2cfSMarcin Szycik ice_eswitch_br_port_vlans_flush(br_port);
906f6e8fb55SWojciech Drewek kfree(br_port);
907f6e8fb55SWojciech Drewek }
908f6e8fb55SWojciech Drewek
909f6e8fb55SWojciech Drewek static struct ice_esw_br_port *
ice_eswitch_br_port_init(struct ice_esw_br * bridge)910f6e8fb55SWojciech Drewek ice_eswitch_br_port_init(struct ice_esw_br *bridge)
911f6e8fb55SWojciech Drewek {
912f6e8fb55SWojciech Drewek struct ice_esw_br_port *br_port;
913f6e8fb55SWojciech Drewek
914f6e8fb55SWojciech Drewek br_port = kzalloc(sizeof(*br_port), GFP_KERNEL);
915f6e8fb55SWojciech Drewek if (!br_port)
916f6e8fb55SWojciech Drewek return ERR_PTR(-ENOMEM);
917f6e8fb55SWojciech Drewek
918e9dda2cfSMarcin Szycik xa_init(&br_port->vlans);
919e9dda2cfSMarcin Szycik
920f6e8fb55SWojciech Drewek br_port->bridge = bridge;
921f6e8fb55SWojciech Drewek
922f6e8fb55SWojciech Drewek return br_port;
923f6e8fb55SWojciech Drewek }
924f6e8fb55SWojciech Drewek
925f6e8fb55SWojciech Drewek static int
ice_eswitch_br_vf_repr_port_init(struct ice_esw_br * bridge,struct ice_repr * repr)926f6e8fb55SWojciech Drewek ice_eswitch_br_vf_repr_port_init(struct ice_esw_br *bridge,
927f6e8fb55SWojciech Drewek struct ice_repr *repr)
928f6e8fb55SWojciech Drewek {
929f6e8fb55SWojciech Drewek struct ice_esw_br_port *br_port;
930f6e8fb55SWojciech Drewek int err;
931f6e8fb55SWojciech Drewek
932f6e8fb55SWojciech Drewek br_port = ice_eswitch_br_port_init(bridge);
933f6e8fb55SWojciech Drewek if (IS_ERR(br_port))
934f6e8fb55SWojciech Drewek return PTR_ERR(br_port);
935f6e8fb55SWojciech Drewek
936f6e8fb55SWojciech Drewek br_port->vsi = repr->src_vsi;
937f6e8fb55SWojciech Drewek br_port->vsi_idx = br_port->vsi->idx;
938f6e8fb55SWojciech Drewek br_port->type = ICE_ESWITCH_BR_VF_REPR_PORT;
939f6e8fb55SWojciech Drewek repr->br_port = br_port;
940f6e8fb55SWojciech Drewek
941f6e8fb55SWojciech Drewek err = xa_insert(&bridge->ports, br_port->vsi_idx, br_port, GFP_KERNEL);
942f6e8fb55SWojciech Drewek if (err) {
943f6e8fb55SWojciech Drewek ice_eswitch_br_port_deinit(bridge, br_port);
944f6e8fb55SWojciech Drewek return err;
945f6e8fb55SWojciech Drewek }
946f6e8fb55SWojciech Drewek
947f6e8fb55SWojciech Drewek return 0;
948f6e8fb55SWojciech Drewek }
949f6e8fb55SWojciech Drewek
950f6e8fb55SWojciech Drewek static int
ice_eswitch_br_uplink_port_init(struct ice_esw_br * bridge,struct ice_pf * pf)951f6e8fb55SWojciech Drewek ice_eswitch_br_uplink_port_init(struct ice_esw_br *bridge, struct ice_pf *pf)
952f6e8fb55SWojciech Drewek {
9533b66266aSMichal Swiatkowski struct ice_vsi *vsi = pf->eswitch.uplink_vsi;
954f6e8fb55SWojciech Drewek struct ice_esw_br_port *br_port;
955f6e8fb55SWojciech Drewek int err;
956f6e8fb55SWojciech Drewek
957f6e8fb55SWojciech Drewek br_port = ice_eswitch_br_port_init(bridge);
958f6e8fb55SWojciech Drewek if (IS_ERR(br_port))
959f6e8fb55SWojciech Drewek return PTR_ERR(br_port);
960f6e8fb55SWojciech Drewek
961f6e8fb55SWojciech Drewek br_port->vsi = vsi;
962f6e8fb55SWojciech Drewek br_port->vsi_idx = br_port->vsi->idx;
963f6e8fb55SWojciech Drewek br_port->type = ICE_ESWITCH_BR_UPLINK_PORT;
964f6e8fb55SWojciech Drewek pf->br_port = br_port;
965f6e8fb55SWojciech Drewek
966f6e8fb55SWojciech Drewek err = xa_insert(&bridge->ports, br_port->vsi_idx, br_port, GFP_KERNEL);
967f6e8fb55SWojciech Drewek if (err) {
968f6e8fb55SWojciech Drewek ice_eswitch_br_port_deinit(bridge, br_port);
969f6e8fb55SWojciech Drewek return err;
970f6e8fb55SWojciech Drewek }
971f6e8fb55SWojciech Drewek
972f6e8fb55SWojciech Drewek return 0;
973f6e8fb55SWojciech Drewek }
974f6e8fb55SWojciech Drewek
975f6e8fb55SWojciech Drewek static void
ice_eswitch_br_ports_flush(struct ice_esw_br * bridge)976f6e8fb55SWojciech Drewek ice_eswitch_br_ports_flush(struct ice_esw_br *bridge)
977f6e8fb55SWojciech Drewek {
978f6e8fb55SWojciech Drewek struct ice_esw_br_port *port;
979f6e8fb55SWojciech Drewek unsigned long i;
980f6e8fb55SWojciech Drewek
981f6e8fb55SWojciech Drewek xa_for_each(&bridge->ports, i, port)
982f6e8fb55SWojciech Drewek ice_eswitch_br_port_deinit(bridge, port);
983f6e8fb55SWojciech Drewek }
984f6e8fb55SWojciech Drewek
985f6e8fb55SWojciech Drewek static void
ice_eswitch_br_deinit(struct ice_esw_br_offloads * br_offloads,struct ice_esw_br * bridge)986f6e8fb55SWojciech Drewek ice_eswitch_br_deinit(struct ice_esw_br_offloads *br_offloads,
987f6e8fb55SWojciech Drewek struct ice_esw_br *bridge)
988f6e8fb55SWojciech Drewek {
989f6e8fb55SWojciech Drewek if (!bridge)
990f6e8fb55SWojciech Drewek return;
991f6e8fb55SWojciech Drewek
992f6e8fb55SWojciech Drewek /* Cleanup all the ports that were added asynchronously
993f6e8fb55SWojciech Drewek * through NETDEV_CHANGEUPPER event.
994f6e8fb55SWojciech Drewek */
995f6e8fb55SWojciech Drewek ice_eswitch_br_ports_flush(bridge);
996f6e8fb55SWojciech Drewek WARN_ON(!xa_empty(&bridge->ports));
997f6e8fb55SWojciech Drewek xa_destroy(&bridge->ports);
9987c945a1aSWojciech Drewek rhashtable_destroy(&bridge->fdb_ht);
9997c945a1aSWojciech Drewek
1000f6e8fb55SWojciech Drewek br_offloads->bridge = NULL;
1001f6e8fb55SWojciech Drewek kfree(bridge);
1002f6e8fb55SWojciech Drewek }
1003f6e8fb55SWojciech Drewek
1004f6e8fb55SWojciech Drewek static struct ice_esw_br *
ice_eswitch_br_init(struct ice_esw_br_offloads * br_offloads,int ifindex)1005f6e8fb55SWojciech Drewek ice_eswitch_br_init(struct ice_esw_br_offloads *br_offloads, int ifindex)
1006f6e8fb55SWojciech Drewek {
1007f6e8fb55SWojciech Drewek struct ice_esw_br *bridge;
10087c945a1aSWojciech Drewek int err;
1009f6e8fb55SWojciech Drewek
1010f6e8fb55SWojciech Drewek bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
1011f6e8fb55SWojciech Drewek if (!bridge)
1012f6e8fb55SWojciech Drewek return ERR_PTR(-ENOMEM);
1013f6e8fb55SWojciech Drewek
10147c945a1aSWojciech Drewek err = rhashtable_init(&bridge->fdb_ht, &ice_fdb_ht_params);
10157c945a1aSWojciech Drewek if (err) {
10167c945a1aSWojciech Drewek kfree(bridge);
10177c945a1aSWojciech Drewek return ERR_PTR(err);
10187c945a1aSWojciech Drewek }
10197c945a1aSWojciech Drewek
10207c945a1aSWojciech Drewek INIT_LIST_HEAD(&bridge->fdb_list);
1021f6e8fb55SWojciech Drewek bridge->br_offloads = br_offloads;
1022f6e8fb55SWojciech Drewek bridge->ifindex = ifindex;
1023e42c6e0cSMichal Swiatkowski bridge->ageing_time = clock_t_to_jiffies(BR_DEFAULT_AGEING_TIME);
1024f6e8fb55SWojciech Drewek xa_init(&bridge->ports);
1025f6e8fb55SWojciech Drewek br_offloads->bridge = bridge;
1026f6e8fb55SWojciech Drewek
1027f6e8fb55SWojciech Drewek return bridge;
1028f6e8fb55SWojciech Drewek }
1029f6e8fb55SWojciech Drewek
1030f6e8fb55SWojciech Drewek static struct ice_esw_br *
ice_eswitch_br_get(struct ice_esw_br_offloads * br_offloads,int ifindex,struct netlink_ext_ack * extack)1031f6e8fb55SWojciech Drewek ice_eswitch_br_get(struct ice_esw_br_offloads *br_offloads, int ifindex,
1032f6e8fb55SWojciech Drewek struct netlink_ext_ack *extack)
1033f6e8fb55SWojciech Drewek {
1034f6e8fb55SWojciech Drewek struct ice_esw_br *bridge = br_offloads->bridge;
1035f6e8fb55SWojciech Drewek
1036f6e8fb55SWojciech Drewek if (bridge) {
1037f6e8fb55SWojciech Drewek if (bridge->ifindex != ifindex) {
1038f6e8fb55SWojciech Drewek NL_SET_ERR_MSG_MOD(extack,
1039f6e8fb55SWojciech Drewek "Only one bridge is supported per eswitch");
1040f6e8fb55SWojciech Drewek return ERR_PTR(-EOPNOTSUPP);
1041f6e8fb55SWojciech Drewek }
1042f6e8fb55SWojciech Drewek return bridge;
1043f6e8fb55SWojciech Drewek }
1044f6e8fb55SWojciech Drewek
1045f6e8fb55SWojciech Drewek /* Create the bridge if it doesn't exist yet */
1046f6e8fb55SWojciech Drewek bridge = ice_eswitch_br_init(br_offloads, ifindex);
1047f6e8fb55SWojciech Drewek if (IS_ERR(bridge))
1048f6e8fb55SWojciech Drewek NL_SET_ERR_MSG_MOD(extack, "Failed to init the bridge");
1049f6e8fb55SWojciech Drewek
1050f6e8fb55SWojciech Drewek return bridge;
1051f6e8fb55SWojciech Drewek }
1052f6e8fb55SWojciech Drewek
1053f6e8fb55SWojciech Drewek static void
ice_eswitch_br_verify_deinit(struct ice_esw_br_offloads * br_offloads,struct ice_esw_br * bridge)1054f6e8fb55SWojciech Drewek ice_eswitch_br_verify_deinit(struct ice_esw_br_offloads *br_offloads,
1055f6e8fb55SWojciech Drewek struct ice_esw_br *bridge)
1056f6e8fb55SWojciech Drewek {
1057f6e8fb55SWojciech Drewek /* Remove the bridge if it exists and there are no ports left */
1058f6e8fb55SWojciech Drewek if (!bridge || !xa_empty(&bridge->ports))
1059f6e8fb55SWojciech Drewek return;
1060f6e8fb55SWojciech Drewek
1061f6e8fb55SWojciech Drewek ice_eswitch_br_deinit(br_offloads, bridge);
1062f6e8fb55SWojciech Drewek }
1063f6e8fb55SWojciech Drewek
1064f6e8fb55SWojciech Drewek static int
ice_eswitch_br_port_unlink(struct ice_esw_br_offloads * br_offloads,struct net_device * dev,int ifindex,struct netlink_ext_ack * extack)1065f6e8fb55SWojciech Drewek ice_eswitch_br_port_unlink(struct ice_esw_br_offloads *br_offloads,
1066f6e8fb55SWojciech Drewek struct net_device *dev, int ifindex,
1067f6e8fb55SWojciech Drewek struct netlink_ext_ack *extack)
1068f6e8fb55SWojciech Drewek {
1069f6e8fb55SWojciech Drewek struct ice_esw_br_port *br_port = ice_eswitch_br_netdev_to_port(dev);
1070f6e8fb55SWojciech Drewek struct ice_esw_br *bridge;
1071f6e8fb55SWojciech Drewek
1072f6e8fb55SWojciech Drewek if (!br_port) {
1073f6e8fb55SWojciech Drewek NL_SET_ERR_MSG_MOD(extack,
1074f6e8fb55SWojciech Drewek "Port representor is not attached to any bridge");
1075f6e8fb55SWojciech Drewek return -EINVAL;
1076f6e8fb55SWojciech Drewek }
1077f6e8fb55SWojciech Drewek
1078f6e8fb55SWojciech Drewek if (br_port->bridge->ifindex != ifindex) {
1079f6e8fb55SWojciech Drewek NL_SET_ERR_MSG_MOD(extack,
1080f6e8fb55SWojciech Drewek "Port representor is attached to another bridge");
1081f6e8fb55SWojciech Drewek return -EINVAL;
1082f6e8fb55SWojciech Drewek }
1083f6e8fb55SWojciech Drewek
1084f6e8fb55SWojciech Drewek bridge = br_port->bridge;
1085f6e8fb55SWojciech Drewek
1086d129c2a2SPawel Chmielewski trace_ice_eswitch_br_port_unlink(br_port);
1087f6e8fb55SWojciech Drewek ice_eswitch_br_port_deinit(br_port->bridge, br_port);
1088f6e8fb55SWojciech Drewek ice_eswitch_br_verify_deinit(br_offloads, bridge);
1089f6e8fb55SWojciech Drewek
1090f6e8fb55SWojciech Drewek return 0;
1091f6e8fb55SWojciech Drewek }
1092f6e8fb55SWojciech Drewek
1093f6e8fb55SWojciech Drewek static int
ice_eswitch_br_port_link(struct ice_esw_br_offloads * br_offloads,struct net_device * dev,int ifindex,struct netlink_ext_ack * extack)1094f6e8fb55SWojciech Drewek ice_eswitch_br_port_link(struct ice_esw_br_offloads *br_offloads,
1095f6e8fb55SWojciech Drewek struct net_device *dev, int ifindex,
1096f6e8fb55SWojciech Drewek struct netlink_ext_ack *extack)
1097f6e8fb55SWojciech Drewek {
1098f6e8fb55SWojciech Drewek struct ice_esw_br *bridge;
1099f6e8fb55SWojciech Drewek int err;
1100f6e8fb55SWojciech Drewek
1101f6e8fb55SWojciech Drewek if (ice_eswitch_br_netdev_to_port(dev)) {
1102f6e8fb55SWojciech Drewek NL_SET_ERR_MSG_MOD(extack,
1103f6e8fb55SWojciech Drewek "Port is already attached to the bridge");
1104f6e8fb55SWojciech Drewek return -EINVAL;
1105f6e8fb55SWojciech Drewek }
1106f6e8fb55SWojciech Drewek
1107f6e8fb55SWojciech Drewek bridge = ice_eswitch_br_get(br_offloads, ifindex, extack);
1108f6e8fb55SWojciech Drewek if (IS_ERR(bridge))
1109f6e8fb55SWojciech Drewek return PTR_ERR(bridge);
1110f6e8fb55SWojciech Drewek
1111f6e8fb55SWojciech Drewek if (ice_is_port_repr_netdev(dev)) {
1112f6e8fb55SWojciech Drewek struct ice_repr *repr = ice_netdev_to_repr(dev);
1113f6e8fb55SWojciech Drewek
1114f6e8fb55SWojciech Drewek err = ice_eswitch_br_vf_repr_port_init(bridge, repr);
1115d129c2a2SPawel Chmielewski trace_ice_eswitch_br_port_link(repr->br_port);
1116f6e8fb55SWojciech Drewek } else {
1117505a1fdaSWojciech Drewek struct net_device *ice_dev;
1118505a1fdaSWojciech Drewek struct ice_pf *pf;
1119505a1fdaSWojciech Drewek
1120505a1fdaSWojciech Drewek if (netif_is_lag_master(dev))
1121505a1fdaSWojciech Drewek ice_dev = ice_eswitch_br_get_uplink_from_lag(dev);
1122505a1fdaSWojciech Drewek else
1123505a1fdaSWojciech Drewek ice_dev = dev;
1124505a1fdaSWojciech Drewek
1125505a1fdaSWojciech Drewek if (!ice_dev)
1126505a1fdaSWojciech Drewek return 0;
1127505a1fdaSWojciech Drewek
1128505a1fdaSWojciech Drewek pf = ice_netdev_to_pf(ice_dev);
1129f6e8fb55SWojciech Drewek
1130f6e8fb55SWojciech Drewek err = ice_eswitch_br_uplink_port_init(bridge, pf);
1131d129c2a2SPawel Chmielewski trace_ice_eswitch_br_port_link(pf->br_port);
1132f6e8fb55SWojciech Drewek }
1133f6e8fb55SWojciech Drewek if (err) {
1134f6e8fb55SWojciech Drewek NL_SET_ERR_MSG_MOD(extack, "Failed to init bridge port");
1135f6e8fb55SWojciech Drewek goto err_port_init;
1136f6e8fb55SWojciech Drewek }
1137f6e8fb55SWojciech Drewek
1138f6e8fb55SWojciech Drewek return 0;
1139f6e8fb55SWojciech Drewek
1140f6e8fb55SWojciech Drewek err_port_init:
1141f6e8fb55SWojciech Drewek ice_eswitch_br_verify_deinit(br_offloads, bridge);
1142f6e8fb55SWojciech Drewek return err;
1143f6e8fb55SWojciech Drewek }
1144f6e8fb55SWojciech Drewek
1145f6e8fb55SWojciech Drewek static int
ice_eswitch_br_port_changeupper(struct notifier_block * nb,void * ptr)1146f6e8fb55SWojciech Drewek ice_eswitch_br_port_changeupper(struct notifier_block *nb, void *ptr)
1147f6e8fb55SWojciech Drewek {
1148f6e8fb55SWojciech Drewek struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1149f6e8fb55SWojciech Drewek struct netdev_notifier_changeupper_info *info = ptr;
1150f6e8fb55SWojciech Drewek struct ice_esw_br_offloads *br_offloads;
1151f6e8fb55SWojciech Drewek struct netlink_ext_ack *extack;
1152f6e8fb55SWojciech Drewek struct net_device *upper;
1153f6e8fb55SWojciech Drewek
1154f6e8fb55SWojciech Drewek br_offloads = ice_nb_to_br_offloads(nb, netdev_nb);
1155f6e8fb55SWojciech Drewek
1156f6e8fb55SWojciech Drewek if (!ice_eswitch_br_is_dev_valid(dev))
1157f6e8fb55SWojciech Drewek return 0;
1158f6e8fb55SWojciech Drewek
1159f6e8fb55SWojciech Drewek upper = info->upper_dev;
1160f6e8fb55SWojciech Drewek if (!netif_is_bridge_master(upper))
1161f6e8fb55SWojciech Drewek return 0;
1162f6e8fb55SWojciech Drewek
1163f6e8fb55SWojciech Drewek extack = netdev_notifier_info_to_extack(&info->info);
1164f6e8fb55SWojciech Drewek
1165f6e8fb55SWojciech Drewek if (info->linking)
1166f6e8fb55SWojciech Drewek return ice_eswitch_br_port_link(br_offloads, dev,
1167f6e8fb55SWojciech Drewek upper->ifindex, extack);
1168f6e8fb55SWojciech Drewek else
1169f6e8fb55SWojciech Drewek return ice_eswitch_br_port_unlink(br_offloads, dev,
1170f6e8fb55SWojciech Drewek upper->ifindex, extack);
1171f6e8fb55SWojciech Drewek }
1172f6e8fb55SWojciech Drewek
1173f6e8fb55SWojciech Drewek static int
ice_eswitch_br_port_event(struct notifier_block * nb,unsigned long event,void * ptr)1174f6e8fb55SWojciech Drewek ice_eswitch_br_port_event(struct notifier_block *nb,
1175f6e8fb55SWojciech Drewek unsigned long event, void *ptr)
1176f6e8fb55SWojciech Drewek {
1177f6e8fb55SWojciech Drewek int err = 0;
1178f6e8fb55SWojciech Drewek
1179f6e8fb55SWojciech Drewek switch (event) {
1180f6e8fb55SWojciech Drewek case NETDEV_CHANGEUPPER:
1181f6e8fb55SWojciech Drewek err = ice_eswitch_br_port_changeupper(nb, ptr);
1182f6e8fb55SWojciech Drewek break;
1183f6e8fb55SWojciech Drewek }
1184f6e8fb55SWojciech Drewek
1185f6e8fb55SWojciech Drewek return notifier_from_errno(err);
1186f6e8fb55SWojciech Drewek }
1187f6e8fb55SWojciech Drewek
1188f6e8fb55SWojciech Drewek static void
ice_eswitch_br_offloads_dealloc(struct ice_pf * pf)1189f6e8fb55SWojciech Drewek ice_eswitch_br_offloads_dealloc(struct ice_pf *pf)
1190f6e8fb55SWojciech Drewek {
11913b66266aSMichal Swiatkowski struct ice_esw_br_offloads *br_offloads = pf->eswitch.br_offloads;
1192f6e8fb55SWojciech Drewek
1193f6e8fb55SWojciech Drewek ASSERT_RTNL();
1194f6e8fb55SWojciech Drewek
1195f6e8fb55SWojciech Drewek if (!br_offloads)
1196f6e8fb55SWojciech Drewek return;
1197f6e8fb55SWojciech Drewek
1198f6e8fb55SWojciech Drewek ice_eswitch_br_deinit(br_offloads, br_offloads->bridge);
1199f6e8fb55SWojciech Drewek
12003b66266aSMichal Swiatkowski pf->eswitch.br_offloads = NULL;
1201f6e8fb55SWojciech Drewek kfree(br_offloads);
1202f6e8fb55SWojciech Drewek }
1203f6e8fb55SWojciech Drewek
1204f6e8fb55SWojciech Drewek static struct ice_esw_br_offloads *
ice_eswitch_br_offloads_alloc(struct ice_pf * pf)1205f6e8fb55SWojciech Drewek ice_eswitch_br_offloads_alloc(struct ice_pf *pf)
1206f6e8fb55SWojciech Drewek {
1207f6e8fb55SWojciech Drewek struct ice_esw_br_offloads *br_offloads;
1208f6e8fb55SWojciech Drewek
1209f6e8fb55SWojciech Drewek ASSERT_RTNL();
1210f6e8fb55SWojciech Drewek
12113b66266aSMichal Swiatkowski if (pf->eswitch.br_offloads)
1212f6e8fb55SWojciech Drewek return ERR_PTR(-EEXIST);
1213f6e8fb55SWojciech Drewek
1214f6e8fb55SWojciech Drewek br_offloads = kzalloc(sizeof(*br_offloads), GFP_KERNEL);
1215f6e8fb55SWojciech Drewek if (!br_offloads)
1216f6e8fb55SWojciech Drewek return ERR_PTR(-ENOMEM);
1217f6e8fb55SWojciech Drewek
12183b66266aSMichal Swiatkowski pf->eswitch.br_offloads = br_offloads;
1219f6e8fb55SWojciech Drewek br_offloads->pf = pf;
1220f6e8fb55SWojciech Drewek
1221f6e8fb55SWojciech Drewek return br_offloads;
1222f6e8fb55SWojciech Drewek }
1223f6e8fb55SWojciech Drewek
1224f6e8fb55SWojciech Drewek void
ice_eswitch_br_offloads_deinit(struct ice_pf * pf)1225f6e8fb55SWojciech Drewek ice_eswitch_br_offloads_deinit(struct ice_pf *pf)
1226f6e8fb55SWojciech Drewek {
1227f6e8fb55SWojciech Drewek struct ice_esw_br_offloads *br_offloads;
1228f6e8fb55SWojciech Drewek
12293b66266aSMichal Swiatkowski br_offloads = pf->eswitch.br_offloads;
1230f6e8fb55SWojciech Drewek if (!br_offloads)
1231f6e8fb55SWojciech Drewek return;
1232f6e8fb55SWojciech Drewek
1233e42c6e0cSMichal Swiatkowski cancel_delayed_work_sync(&br_offloads->update_work);
1234f6e8fb55SWojciech Drewek unregister_netdevice_notifier(&br_offloads->netdev_nb);
1235e9dda2cfSMarcin Szycik unregister_switchdev_blocking_notifier(&br_offloads->switchdev_blk);
12367c945a1aSWojciech Drewek unregister_switchdev_notifier(&br_offloads->switchdev_nb);
12377c945a1aSWojciech Drewek destroy_workqueue(br_offloads->wq);
1238f6e8fb55SWojciech Drewek /* Although notifier block is unregistered just before,
1239f6e8fb55SWojciech Drewek * so we don't get any new events, some events might be
1240f6e8fb55SWojciech Drewek * already in progress. Hold the rtnl lock and wait for
1241f6e8fb55SWojciech Drewek * them to finished.
1242f6e8fb55SWojciech Drewek */
1243f6e8fb55SWojciech Drewek rtnl_lock();
1244f6e8fb55SWojciech Drewek ice_eswitch_br_offloads_dealloc(pf);
1245f6e8fb55SWojciech Drewek rtnl_unlock();
1246f6e8fb55SWojciech Drewek }
1247f6e8fb55SWojciech Drewek
ice_eswitch_br_update(struct ice_esw_br_offloads * br_offloads)1248e42c6e0cSMichal Swiatkowski static void ice_eswitch_br_update(struct ice_esw_br_offloads *br_offloads)
1249e42c6e0cSMichal Swiatkowski {
1250e42c6e0cSMichal Swiatkowski struct ice_esw_br *bridge = br_offloads->bridge;
1251e42c6e0cSMichal Swiatkowski struct ice_esw_br_fdb_entry *entry, *tmp;
1252e42c6e0cSMichal Swiatkowski
1253e42c6e0cSMichal Swiatkowski if (!bridge)
1254e42c6e0cSMichal Swiatkowski return;
1255e42c6e0cSMichal Swiatkowski
1256e42c6e0cSMichal Swiatkowski rtnl_lock();
1257e42c6e0cSMichal Swiatkowski list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list) {
1258e42c6e0cSMichal Swiatkowski if (entry->flags & ICE_ESWITCH_BR_FDB_ADDED_BY_USER)
1259e42c6e0cSMichal Swiatkowski continue;
1260e42c6e0cSMichal Swiatkowski
1261e42c6e0cSMichal Swiatkowski if (time_is_after_eq_jiffies(entry->last_use +
1262e42c6e0cSMichal Swiatkowski bridge->ageing_time))
1263e42c6e0cSMichal Swiatkowski continue;
1264e42c6e0cSMichal Swiatkowski
1265e42c6e0cSMichal Swiatkowski ice_eswitch_br_fdb_entry_notify_and_cleanup(bridge, entry);
1266e42c6e0cSMichal Swiatkowski }
1267e42c6e0cSMichal Swiatkowski rtnl_unlock();
1268e42c6e0cSMichal Swiatkowski }
1269e42c6e0cSMichal Swiatkowski
ice_eswitch_br_update_work(struct work_struct * work)1270e42c6e0cSMichal Swiatkowski static void ice_eswitch_br_update_work(struct work_struct *work)
1271e42c6e0cSMichal Swiatkowski {
1272e42c6e0cSMichal Swiatkowski struct ice_esw_br_offloads *br_offloads;
1273e42c6e0cSMichal Swiatkowski
1274e42c6e0cSMichal Swiatkowski br_offloads = ice_work_to_br_offloads(work);
1275e42c6e0cSMichal Swiatkowski
1276e42c6e0cSMichal Swiatkowski ice_eswitch_br_update(br_offloads);
1277e42c6e0cSMichal Swiatkowski
1278e42c6e0cSMichal Swiatkowski queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
1279e42c6e0cSMichal Swiatkowski ICE_ESW_BRIDGE_UPDATE_INTERVAL);
1280e42c6e0cSMichal Swiatkowski }
1281e42c6e0cSMichal Swiatkowski
1282f6e8fb55SWojciech Drewek int
ice_eswitch_br_offloads_init(struct ice_pf * pf)1283f6e8fb55SWojciech Drewek ice_eswitch_br_offloads_init(struct ice_pf *pf)
1284f6e8fb55SWojciech Drewek {
1285f6e8fb55SWojciech Drewek struct ice_esw_br_offloads *br_offloads;
1286f6e8fb55SWojciech Drewek struct device *dev = ice_pf_to_dev(pf);
1287f6e8fb55SWojciech Drewek int err;
1288f6e8fb55SWojciech Drewek
1289f6e8fb55SWojciech Drewek rtnl_lock();
1290f6e8fb55SWojciech Drewek br_offloads = ice_eswitch_br_offloads_alloc(pf);
1291f6e8fb55SWojciech Drewek rtnl_unlock();
1292f6e8fb55SWojciech Drewek if (IS_ERR(br_offloads)) {
1293f6e8fb55SWojciech Drewek dev_err(dev, "Failed to init eswitch bridge\n");
1294f6e8fb55SWojciech Drewek return PTR_ERR(br_offloads);
1295f6e8fb55SWojciech Drewek }
1296f6e8fb55SWojciech Drewek
12977c945a1aSWojciech Drewek br_offloads->wq = alloc_ordered_workqueue("ice_bridge_wq", 0);
12987c945a1aSWojciech Drewek if (!br_offloads->wq) {
12997c945a1aSWojciech Drewek err = -ENOMEM;
13007c945a1aSWojciech Drewek dev_err(dev, "Failed to allocate bridge workqueue\n");
13017c945a1aSWojciech Drewek goto err_alloc_wq;
13027c945a1aSWojciech Drewek }
13037c945a1aSWojciech Drewek
13047c945a1aSWojciech Drewek br_offloads->switchdev_nb.notifier_call =
13057c945a1aSWojciech Drewek ice_eswitch_br_switchdev_event;
13067c945a1aSWojciech Drewek err = register_switchdev_notifier(&br_offloads->switchdev_nb);
13077c945a1aSWojciech Drewek if (err) {
13087c945a1aSWojciech Drewek dev_err(dev,
13097c945a1aSWojciech Drewek "Failed to register switchdev notifier\n");
13107c945a1aSWojciech Drewek goto err_reg_switchdev_nb;
13117c945a1aSWojciech Drewek }
13127c945a1aSWojciech Drewek
1313e9dda2cfSMarcin Szycik br_offloads->switchdev_blk.notifier_call =
1314e9dda2cfSMarcin Szycik ice_eswitch_br_event_blocking;
1315e9dda2cfSMarcin Szycik err = register_switchdev_blocking_notifier(&br_offloads->switchdev_blk);
1316e9dda2cfSMarcin Szycik if (err) {
1317e9dda2cfSMarcin Szycik dev_err(dev,
1318e9dda2cfSMarcin Szycik "Failed to register bridge blocking switchdev notifier\n");
1319e9dda2cfSMarcin Szycik goto err_reg_switchdev_blk;
1320e9dda2cfSMarcin Szycik }
1321e9dda2cfSMarcin Szycik
1322f6e8fb55SWojciech Drewek br_offloads->netdev_nb.notifier_call = ice_eswitch_br_port_event;
1323f6e8fb55SWojciech Drewek err = register_netdevice_notifier(&br_offloads->netdev_nb);
1324f6e8fb55SWojciech Drewek if (err) {
1325f6e8fb55SWojciech Drewek dev_err(dev,
1326f6e8fb55SWojciech Drewek "Failed to register bridge port event notifier\n");
1327f6e8fb55SWojciech Drewek goto err_reg_netdev_nb;
1328f6e8fb55SWojciech Drewek }
1329f6e8fb55SWojciech Drewek
1330e42c6e0cSMichal Swiatkowski INIT_DELAYED_WORK(&br_offloads->update_work,
1331e42c6e0cSMichal Swiatkowski ice_eswitch_br_update_work);
1332e42c6e0cSMichal Swiatkowski queue_delayed_work(br_offloads->wq, &br_offloads->update_work,
1333e42c6e0cSMichal Swiatkowski ICE_ESW_BRIDGE_UPDATE_INTERVAL);
1334e42c6e0cSMichal Swiatkowski
1335f6e8fb55SWojciech Drewek return 0;
1336f6e8fb55SWojciech Drewek
1337f6e8fb55SWojciech Drewek err_reg_netdev_nb:
1338e9dda2cfSMarcin Szycik unregister_switchdev_blocking_notifier(&br_offloads->switchdev_blk);
1339e9dda2cfSMarcin Szycik err_reg_switchdev_blk:
13407c945a1aSWojciech Drewek unregister_switchdev_notifier(&br_offloads->switchdev_nb);
13417c945a1aSWojciech Drewek err_reg_switchdev_nb:
13427c945a1aSWojciech Drewek destroy_workqueue(br_offloads->wq);
13437c945a1aSWojciech Drewek err_alloc_wq:
1344f6e8fb55SWojciech Drewek rtnl_lock();
1345f6e8fb55SWojciech Drewek ice_eswitch_br_offloads_dealloc(pf);
1346f6e8fb55SWojciech Drewek rtnl_unlock();
1347f6e8fb55SWojciech Drewek
1348f6e8fb55SWojciech Drewek return err;
1349f6e8fb55SWojciech Drewek }
1350