// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2019-2021, Intel Corporation. */ #include "ice.h" #include "ice_lib.h" #include "ice_eswitch.h" #include "ice_eswitch_br.h" #include "ice_fltr.h" #include "ice_repr.h" #include "devlink/devlink.h" #include "ice_tc_lib.h" /** * ice_eswitch_setup_env - configure eswitch HW filters * @pf: pointer to PF struct * * This function adds HW filters configuration specific for switchdev * mode. */ static int ice_eswitch_setup_env(struct ice_pf *pf) { struct ice_vsi *uplink_vsi = pf->eswitch.uplink_vsi; struct net_device *netdev = uplink_vsi->netdev; bool if_running = netif_running(netdev); struct ice_vsi_vlan_ops *vlan_ops; if (if_running && !test_and_set_bit(ICE_VSI_DOWN, uplink_vsi->state)) if (ice_down(uplink_vsi)) return -ENODEV; ice_remove_vsi_fltr(&pf->hw, uplink_vsi->idx); netif_addr_lock_bh(netdev); __dev_uc_unsync(netdev, NULL); __dev_mc_unsync(netdev, NULL); netif_addr_unlock_bh(netdev); if (ice_vsi_add_vlan_zero(uplink_vsi)) goto err_vlan_zero; if (ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, true, ICE_FLTR_RX)) goto err_def_rx; if (ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, true, ICE_FLTR_TX)) goto err_def_tx; vlan_ops = ice_get_compat_vsi_vlan_ops(uplink_vsi); if (vlan_ops->dis_rx_filtering(uplink_vsi)) goto err_vlan_filtering; if (ice_vsi_update_security(uplink_vsi, ice_vsi_ctx_set_allow_override)) goto err_override_uplink; if (ice_vsi_update_local_lb(uplink_vsi, true)) goto err_override_local_lb; if (if_running && ice_up(uplink_vsi)) goto err_up; return 0; err_up: ice_vsi_update_local_lb(uplink_vsi, false); err_override_local_lb: ice_vsi_update_security(uplink_vsi, ice_vsi_ctx_clear_allow_override); err_override_uplink: vlan_ops->ena_rx_filtering(uplink_vsi); err_vlan_filtering: ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false, ICE_FLTR_TX); err_def_tx: ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false, ICE_FLTR_RX); err_def_rx: ice_vsi_del_vlan_zero(uplink_vsi); err_vlan_zero: ice_fltr_add_mac_and_broadcast(uplink_vsi, uplink_vsi->port_info->mac.perm_addr, ICE_FWD_TO_VSI); if (if_running) ice_up(uplink_vsi); return -ENODEV; } /** * ice_eswitch_release_repr - clear PR VSI configuration * @pf: poiner to PF struct * @repr: pointer to PR */ static void ice_eswitch_release_repr(struct ice_pf *pf, struct ice_repr *repr) { struct ice_vsi *vsi = repr->src_vsi; /* Skip representors that aren't configured */ if (!repr->dst) return; ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); metadata_dst_free(repr->dst); repr->dst = NULL; ice_fltr_add_mac_and_broadcast(vsi, repr->parent_mac, ICE_FWD_TO_VSI); } /** * ice_eswitch_setup_repr - configure PR to run in switchdev mode * @pf: pointer to PF struct * @repr: pointer to PR struct */ static int ice_eswitch_setup_repr(struct ice_pf *pf, struct ice_repr *repr) { struct ice_vsi *uplink_vsi = pf->eswitch.uplink_vsi; struct ice_vsi *vsi = repr->src_vsi; struct metadata_dst *dst; repr->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, GFP_KERNEL); if (!repr->dst) return -ENOMEM; netif_keep_dst(uplink_vsi->netdev); dst = repr->dst; dst->u.port_info.port_id = vsi->vsi_num; dst->u.port_info.lower_dev = uplink_vsi->netdev; return 0; } /** * ice_eswitch_cfg_vsi - configure VSI to work in slow-path * @vsi: VSI structure of representee * @mac: representee MAC * * Return: 0 on success, non-zero on error. */ int ice_eswitch_cfg_vsi(struct ice_vsi *vsi, const u8 *mac) { int err; ice_remove_vsi_fltr(&vsi->back->hw, vsi->idx); err = ice_vsi_update_security(vsi, ice_vsi_ctx_clear_antispoof); if (err) goto err_update_security; err = ice_vsi_add_vlan_zero(vsi); if (err) goto err_vlan_zero; return 0; err_vlan_zero: ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); err_update_security: ice_fltr_add_mac_and_broadcast(vsi, mac, ICE_FWD_TO_VSI); return err; } /** * ice_eswitch_decfg_vsi - unroll changes done to VSI for switchdev * @vsi: VSI structure of representee * @mac: representee MAC */ void ice_eswitch_decfg_vsi(struct ice_vsi *vsi, const u8 *mac) { ice_vsi_update_security(vsi, ice_vsi_ctx_set_antispoof); ice_fltr_add_mac_and_broadcast(vsi, mac, ICE_FWD_TO_VSI); } /** * ice_eswitch_update_repr - reconfigure port representor * @repr_id: representor ID * @vsi: VSI for which port representor is configured */ void ice_eswitch_update_repr(unsigned long *repr_id, struct ice_vsi *vsi) { struct ice_pf *pf = vsi->back; struct ice_repr *repr; int err; if (!ice_is_switchdev_running(pf)) return; repr = xa_load(&pf->eswitch.reprs, *repr_id); if (!repr) return; repr->src_vsi = vsi; repr->dst->u.port_info.port_id = vsi->vsi_num; if (repr->br_port) repr->br_port->vsi = vsi; err = ice_eswitch_cfg_vsi(vsi, repr->parent_mac); if (err) dev_err(ice_pf_to_dev(pf), "Failed to update VSI of port representor %d", repr->id); /* The VSI number is different, reload the PR with new id */ if (repr->id != vsi->vsi_num) { xa_erase(&pf->eswitch.reprs, repr->id); repr->id = vsi->vsi_num; if (xa_insert(&pf->eswitch.reprs, repr->id, repr, GFP_KERNEL)) dev_err(ice_pf_to_dev(pf), "Failed to reload port representor %d", repr->id); *repr_id = repr->id; } } /** * ice_eswitch_port_start_xmit - callback for packets transmit * @skb: send buffer * @netdev: network interface device structure * * Returns NETDEV_TX_OK if sent, else an error code */ netdev_tx_t ice_eswitch_port_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct ice_repr *repr = ice_netdev_to_repr(netdev); unsigned int len = skb->len; int ret; skb_dst_drop(skb); dst_hold((struct dst_entry *)repr->dst); skb_dst_set(skb, (struct dst_entry *)repr->dst); skb->dev = repr->dst->u.port_info.lower_dev; ret = dev_queue_xmit(skb); ice_repr_inc_tx_stats(repr, len, ret); return ret; } /** * ice_eswitch_set_target_vsi - set eswitch context in Tx context descriptor * @skb: pointer to send buffer * @off: pointer to offload struct */ void ice_eswitch_set_target_vsi(struct sk_buff *skb, struct ice_tx_offload_params *off) { struct metadata_dst *dst = skb_metadata_dst(skb); u64 cd_cmd, dst_vsi; if (!dst) { cd_cmd = ICE_TX_CTX_DESC_SWTCH_UPLINK << ICE_TXD_CTX_QW1_CMD_S; off->cd_qw1 |= (cd_cmd | ICE_TX_DESC_DTYPE_CTX); } else { cd_cmd = ICE_TX_CTX_DESC_SWTCH_VSI << ICE_TXD_CTX_QW1_CMD_S; dst_vsi = FIELD_PREP(ICE_TXD_CTX_QW1_VSI_M, dst->u.port_info.port_id); off->cd_qw1 = cd_cmd | dst_vsi | ICE_TX_DESC_DTYPE_CTX; } } /** * ice_eswitch_release_env - clear eswitch HW filters * @pf: pointer to PF struct * * This function removes HW filters configuration specific for switchdev * mode and restores default legacy mode settings. */ static void ice_eswitch_release_env(struct ice_pf *pf) { struct ice_vsi *uplink_vsi = pf->eswitch.uplink_vsi; struct ice_vsi_vlan_ops *vlan_ops; vlan_ops = ice_get_compat_vsi_vlan_ops(uplink_vsi); ice_vsi_update_local_lb(uplink_vsi, false); ice_vsi_update_security(uplink_vsi, ice_vsi_ctx_clear_allow_override); vlan_ops->ena_rx_filtering(uplink_vsi); ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false, ICE_FLTR_TX); ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false, ICE_FLTR_RX); ice_fltr_add_mac_and_broadcast(uplink_vsi, uplink_vsi->port_info->mac.perm_addr, ICE_FWD_TO_VSI); } /** * ice_eswitch_enable_switchdev - configure eswitch in switchdev mode * @pf: pointer to PF structure */ static int ice_eswitch_enable_switchdev(struct ice_pf *pf) { struct ice_vsi *uplink_vsi; uplink_vsi = ice_get_main_vsi(pf); if (!uplink_vsi) return -ENODEV; if (netif_is_any_bridge_port(uplink_vsi->netdev)) { dev_err(ice_pf_to_dev(pf), "Uplink port cannot be a bridge port\n"); return -EINVAL; } pf->eswitch.uplink_vsi = uplink_vsi; if (ice_eswitch_setup_env(pf)) return -ENODEV; if (ice_eswitch_br_offloads_init(pf)) goto err_br_offloads; pf->eswitch.is_running = true; return 0; err_br_offloads: ice_eswitch_release_env(pf); return -ENODEV; } /** * ice_eswitch_disable_switchdev - disable eswitch resources * @pf: pointer to PF structure */ static void ice_eswitch_disable_switchdev(struct ice_pf *pf) { ice_eswitch_br_offloads_deinit(pf); ice_eswitch_release_env(pf); pf->eswitch.is_running = false; } /** * ice_eswitch_mode_set - set new eswitch mode * @devlink: pointer to devlink structure * @mode: eswitch mode to switch to * @extack: pointer to extack structure */ int ice_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); if (pf->eswitch_mode == mode) return 0; if (ice_has_vfs(pf)) { dev_info(ice_pf_to_dev(pf), "Changing eswitch mode is allowed only if there is no VFs created"); NL_SET_ERR_MSG_MOD(extack, "Changing eswitch mode is allowed only if there is no VFs created"); return -EOPNOTSUPP; } switch (mode) { case DEVLINK_ESWITCH_MODE_LEGACY: dev_info(ice_pf_to_dev(pf), "PF %d changed eswitch mode to legacy", pf->hw.pf_id); xa_destroy(&pf->eswitch.reprs); NL_SET_ERR_MSG_MOD(extack, "Changed eswitch mode to legacy"); break; case DEVLINK_ESWITCH_MODE_SWITCHDEV: { if (ice_is_adq_active(pf)) { dev_err(ice_pf_to_dev(pf), "Couldn't change eswitch mode to switchdev - ADQ is active. Delete ADQ configs and try again, e.g. tc qdisc del dev $PF root"); NL_SET_ERR_MSG_MOD(extack, "Couldn't change eswitch mode to switchdev - ADQ is active. Delete ADQ configs and try again, e.g. tc qdisc del dev $PF root"); return -EOPNOTSUPP; } dev_info(ice_pf_to_dev(pf), "PF %d changed eswitch mode to switchdev", pf->hw.pf_id); xa_init(&pf->eswitch.reprs); NL_SET_ERR_MSG_MOD(extack, "Changed eswitch mode to switchdev"); break; } default: NL_SET_ERR_MSG_MOD(extack, "Unknown eswitch mode"); return -EINVAL; } pf->eswitch_mode = mode; return 0; } /** * ice_eswitch_mode_get - get current eswitch mode * @devlink: pointer to devlink structure * @mode: output parameter for current eswitch mode */ int ice_eswitch_mode_get(struct devlink *devlink, u16 *mode) { struct ice_pf *pf = devlink_priv(devlink); *mode = pf->eswitch_mode; return 0; } /** * ice_is_eswitch_mode_switchdev - check if eswitch mode is set to switchdev * @pf: pointer to PF structure * * Returns true if eswitch mode is set to DEVLINK_ESWITCH_MODE_SWITCHDEV, * false otherwise. */ bool ice_is_eswitch_mode_switchdev(struct ice_pf *pf) { return pf->eswitch_mode == DEVLINK_ESWITCH_MODE_SWITCHDEV; } /** * ice_eswitch_start_all_tx_queues - start Tx queues of all port representors * @pf: pointer to PF structure */ static void ice_eswitch_start_all_tx_queues(struct ice_pf *pf) { struct ice_repr *repr; unsigned long id; if (test_bit(ICE_DOWN, pf->state)) return; xa_for_each(&pf->eswitch.reprs, id, repr) ice_repr_start_tx_queues(repr); } /** * ice_eswitch_stop_all_tx_queues - stop Tx queues of all port representors * @pf: pointer to PF structure */ void ice_eswitch_stop_all_tx_queues(struct ice_pf *pf) { struct ice_repr *repr; unsigned long id; if (test_bit(ICE_DOWN, pf->state)) return; xa_for_each(&pf->eswitch.reprs, id, repr) ice_repr_stop_tx_queues(repr); } static void ice_eswitch_stop_reprs(struct ice_pf *pf) { ice_eswitch_stop_all_tx_queues(pf); } static void ice_eswitch_start_reprs(struct ice_pf *pf) { ice_eswitch_start_all_tx_queues(pf); } static int ice_eswitch_attach(struct ice_pf *pf, struct ice_repr *repr, unsigned long *id) { int err; if (pf->eswitch_mode == DEVLINK_ESWITCH_MODE_LEGACY) return 0; if (xa_empty(&pf->eswitch.reprs)) { err = ice_eswitch_enable_switchdev(pf); if (err) return err; } ice_eswitch_stop_reprs(pf); err = repr->ops.add(repr); if (err) goto err_create_repr; err = ice_eswitch_setup_repr(pf, repr); if (err) goto err_setup_repr; err = xa_insert(&pf->eswitch.reprs, repr->id, repr, GFP_KERNEL); if (err) goto err_xa_alloc; *id = repr->id; ice_eswitch_start_reprs(pf); return 0; err_xa_alloc: ice_eswitch_release_repr(pf, repr); err_setup_repr: repr->ops.rem(repr); err_create_repr: if (xa_empty(&pf->eswitch.reprs)) ice_eswitch_disable_switchdev(pf); ice_eswitch_start_reprs(pf); return err; } /** * ice_eswitch_attach_vf - attach VF to a eswitch * @pf: pointer to PF structure * @vf: pointer to VF structure to be attached * * During attaching port representor for VF is created. * * Return: zero on success or an error code on failure. */ int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf) { struct ice_repr *repr = ice_repr_create_vf(vf); struct devlink *devlink = priv_to_devlink(pf); int err; if (IS_ERR(repr)) return PTR_ERR(repr); devl_lock(devlink); err = ice_eswitch_attach(pf, repr, &vf->repr_id); if (err) ice_repr_destroy(repr); devl_unlock(devlink); return err; } /** * ice_eswitch_attach_sf - attach SF to a eswitch * @pf: pointer to PF structure * @sf: pointer to SF structure to be attached * * During attaching port representor for SF is created. * * Return: zero on success or an error code on failure. */ int ice_eswitch_attach_sf(struct ice_pf *pf, struct ice_dynamic_port *sf) { struct ice_repr *repr = ice_repr_create_sf(sf); int err; if (IS_ERR(repr)) return PTR_ERR(repr); err = ice_eswitch_attach(pf, repr, &sf->repr_id); if (err) ice_repr_destroy(repr); return err; } static void ice_eswitch_detach(struct ice_pf *pf, struct ice_repr *repr) { ice_eswitch_stop_reprs(pf); xa_erase(&pf->eswitch.reprs, repr->id); if (xa_empty(&pf->eswitch.reprs)) ice_eswitch_disable_switchdev(pf); ice_eswitch_release_repr(pf, repr); repr->ops.rem(repr); ice_repr_destroy(repr); if (xa_empty(&pf->eswitch.reprs)) { struct devlink *devlink = priv_to_devlink(pf); /* since all port representors are destroyed, there is * no point in keeping the nodes */ ice_devlink_rate_clear_tx_topology(ice_get_main_vsi(pf)); devl_rate_nodes_destroy(devlink); } else { ice_eswitch_start_reprs(pf); } } /** * ice_eswitch_detach_vf - detach VF from a eswitch * @pf: pointer to PF structure * @vf: pointer to VF structure to be detached */ void ice_eswitch_detach_vf(struct ice_pf *pf, struct ice_vf *vf) { struct ice_repr *repr = xa_load(&pf->eswitch.reprs, vf->repr_id); struct devlink *devlink = priv_to_devlink(pf); if (!repr) return; devl_lock(devlink); ice_eswitch_detach(pf, repr); devl_unlock(devlink); } /** * ice_eswitch_detach_sf - detach SF from a eswitch * @pf: pointer to PF structure * @sf: pointer to SF structure to be detached */ void ice_eswitch_detach_sf(struct ice_pf *pf, struct ice_dynamic_port *sf) { struct ice_repr *repr = xa_load(&pf->eswitch.reprs, sf->repr_id); if (!repr) return; ice_eswitch_detach(pf, repr); } /** * ice_eswitch_get_target - get netdev based on src_vsi from descriptor * @rx_ring: ring used to receive the packet * @rx_desc: descriptor used to get src_vsi value * * Get src_vsi value from descriptor and load correct representor. If it isn't * found return rx_ring->netdev. */ struct net_device *ice_eswitch_get_target(struct ice_rx_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc) { struct ice_eswitch *eswitch = &rx_ring->vsi->back->eswitch; struct ice_32b_rx_flex_desc_nic_2 *desc; struct ice_repr *repr; desc = (struct ice_32b_rx_flex_desc_nic_2 *)rx_desc; repr = xa_load(&eswitch->reprs, le16_to_cpu(desc->src_vsi)); if (!repr) return rx_ring->netdev; return repr->netdev; }