Commit b57e0d48 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue

Tony Nguyen says:

====================
ice: switchdev bridge offload

Wojciech Drewek says:

Linux bridge provides ability to learn MAC addresses and vlans
detected on bridge's ports. As a result of this, FDB (forward data base)
entries are created and they can be offloaded to the HW. By adding
VF's port representors to the bridge together with the uplink netdev,
we can learn VF's and link partner's MAC addresses. This is achieved
by slow/exception-path, where packets that do not match any filters
(FDB entries in this case) are send to the bridge ports.

Driver keeps track of the netdevs added to the bridge
by listening for NETDEV_CHANGEUPPER event. We distinguish two types
of bridge ports: uplink port and VF's representor port. Linux
bridge always learns src MAC of the packet on rx path. With the
current slow-path implementation, it means that we will learn
VF's MAC on port repr (when the VF transmits the packet) and
link partner's MAC on uplink (when we receive it on uplink from LAN).

The driver is notified about learning of the MAC/VLAN by
SWITCHDEV_FDB_{ADD|DEL}_TO_DEVICE events. This is followed by creation
of the HW filter. The direction of the filter is based on port
type (uplink or VF repr). In case of the uplink, rule forwards
the packets to the LAN (matching on link partner's MAC). When the
notification is received on VF repr then the rule forwards the
packets to the associated VF (matching on VF's MAC).

This approach would not work on its own however. This is because if
one of the directions is offloaded, then the bridge would not be able
to learn the other one. If the egress rule is added (learned on uplink)
then the response from the VF will be sent directly to the LAN.
The packet will not got through slow-path, it would not be seen on
VF's port repr. Because of that, the bridge would not learn VF's MAC.

This is solved by introducing guard rule. It prevents forward rule from
working until the opposite direction is offloaded.

Aging is not fully supported yet, aging time is static for now. The
follow up submissions will introduce counters that will allow us to
keep track if the rule is actually being used or not.

A few fixes/changes are needed for this feature to work with ice driver.
These are introduced in first 5 patches.
Reviewed-by: default avatarVlad Buslov <vladbu@nvidia.com>

* '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  ice: add tracepoints for the switchdev bridge
  ice: implement static version of ageing
  ice: implement bridge port vlan
  ice: Add VLAN FDB support in switchdev mode
  ice: Add guard rule when creating FDB in switchdev
  ice: Switchdev FDB events support
  ice: Implement basic eswitch bridge setup
  ice: Unset src prune on uplink VSI
  ice: Disable vlan pruning for uplink VSI
  ice: Don't tx before switchdev is fully configured
  ice: Prohibit rx mode change in switchdev mode
  ice: Skip adv rules removal upon switchdev release
====================

Link: https://lore.kernel.org/r/20230724161152.2177196-1-anthony.l.nguyen@intel.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 20bf98c9 d129c2a2
......@@ -47,5 +47,5 @@ ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o
ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o
ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o
ice-$(CONFIG_ICE_SWITCHDEV) += ice_eswitch.o
ice-$(CONFIG_ICE_SWITCHDEV) += ice_eswitch.o ice_eswitch_br.o
ice-$(CONFIG_GNSS) += ice_gnss.o
......@@ -370,6 +370,7 @@ struct ice_vsi {
u16 rx_buf_len;
struct ice_aqc_vsi_props info; /* VSI properties */
struct ice_vsi_vlan_info vlan_info; /* vlan config to be restored */
/* VSI stats */
struct rtnl_link_stats64 net_stats;
......@@ -517,6 +518,7 @@ enum ice_misc_thread_tasks {
struct ice_switchdev_info {
struct ice_vsi *control_vsi;
struct ice_vsi *uplink_vsi;
struct ice_esw_br_offloads *br_offloads;
bool is_running;
};
......@@ -626,6 +628,7 @@ struct ice_pf {
struct ice_lag *lag; /* Link Aggregation information */
struct ice_switchdev_info switchdev;
struct ice_esw_br_port *br_port;
#define ICE_INVALID_AGG_NODE_ID 0
#define ICE_PF_AGG_NODE_ID_START 1
......@@ -853,7 +856,7 @@ static inline bool ice_is_adq_active(struct ice_pf *pf)
return false;
}
bool netif_is_ice(struct net_device *dev);
bool netif_is_ice(const struct net_device *dev);
int ice_vsi_setup_tx_rings(struct ice_vsi *vsi);
int ice_vsi_setup_rx_rings(struct ice_vsi *vsi);
int ice_vsi_open_ctrl(struct ice_vsi *vsi);
......
......@@ -4,6 +4,7 @@
#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 "ice_devlink.h"
......@@ -103,17 +104,28 @@ static int ice_eswitch_setup_env(struct ice_pf *pf)
rule_added = true;
}
vlan_ops = ice_get_compat_vsi_vlan_ops(uplink_vsi);
if (vlan_ops->dis_rx_filtering(uplink_vsi))
goto err_dis_rx;
if (ice_vsi_update_security(uplink_vsi, ice_vsi_ctx_set_allow_override))
goto err_override_uplink;
if (ice_vsi_update_security(ctrl_vsi, ice_vsi_ctx_set_allow_override))
goto err_override_control;
if (ice_vsi_update_local_lb(uplink_vsi, true))
goto err_override_local_lb;
return 0;
err_override_local_lb:
ice_vsi_update_security(ctrl_vsi, ice_vsi_ctx_clear_allow_override);
err_override_control:
ice_vsi_update_security(uplink_vsi, ice_vsi_ctx_clear_allow_override);
err_override_uplink:
vlan_ops->ena_rx_filtering(uplink_vsi);
err_dis_rx:
if (rule_added)
ice_clear_dflt_vsi(uplink_vsi);
err_def_rx:
......@@ -306,6 +318,9 @@ void ice_eswitch_update_repr(struct ice_vsi *vsi)
repr->src_vsi = vsi;
repr->dst->u.port_info.port_id = vsi->vsi_num;
if (repr->br_port)
repr->br_port->vsi = vsi;
ret = ice_vsi_update_security(vsi, ice_vsi_ctx_clear_antispoof);
if (ret) {
ice_fltr_add_mac_and_broadcast(vsi, vf->hw_lan_addr, ICE_FWD_TO_VSI);
......@@ -331,6 +346,9 @@ ice_eswitch_port_start_xmit(struct sk_buff *skb, struct net_device *netdev)
np = netdev_priv(netdev);
vsi = np->vsi;
if (!vsi || !ice_is_switchdev_running(vsi->back))
return NETDEV_TX_BUSY;
if (ice_is_reset_in_progress(vsi->back->state) ||
test_bit(ICE_VF_DIS, vsi->back->state))
return NETDEV_TX_BUSY;
......@@ -378,9 +396,14 @@ static void ice_eswitch_release_env(struct ice_pf *pf)
{
struct ice_vsi *uplink_vsi = pf->switchdev.uplink_vsi;
struct ice_vsi *ctrl_vsi = pf->switchdev.control_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(ctrl_vsi, ice_vsi_ctx_clear_allow_override);
ice_vsi_update_security(uplink_vsi, ice_vsi_ctx_clear_allow_override);
vlan_ops->ena_rx_filtering(uplink_vsi);
ice_clear_dflt_vsi(uplink_vsi);
ice_fltr_add_mac_and_broadcast(uplink_vsi,
uplink_vsi->port_info->mac.perm_addr,
......@@ -455,16 +478,24 @@ static void ice_eswitch_napi_disable(struct ice_pf *pf)
*/
static int ice_eswitch_enable_switchdev(struct ice_pf *pf)
{
struct ice_vsi *ctrl_vsi;
struct ice_vsi *ctrl_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->switchdev.control_vsi = ice_eswitch_vsi_setup(pf, pf->hw.port_info);
if (!pf->switchdev.control_vsi)
return -ENODEV;
ctrl_vsi = pf->switchdev.control_vsi;
pf->switchdev.uplink_vsi = ice_get_main_vsi(pf);
if (!pf->switchdev.uplink_vsi)
goto err_vsi;
pf->switchdev.uplink_vsi = uplink_vsi;
if (ice_eswitch_setup_env(pf))
goto err_vsi;
......@@ -480,10 +511,15 @@ static int ice_eswitch_enable_switchdev(struct ice_pf *pf)
if (ice_vsi_open(ctrl_vsi))
goto err_setup_reprs;
if (ice_eswitch_br_offloads_init(pf))
goto err_br_offloads;
ice_eswitch_napi_enable(pf);
return 0;
err_br_offloads:
ice_vsi_close(ctrl_vsi);
err_setup_reprs:
ice_repr_rem_from_all_vfs(pf);
err_repr_add:
......@@ -502,8 +538,8 @@ static void ice_eswitch_disable_switchdev(struct ice_pf *pf)
struct ice_vsi *ctrl_vsi = pf->switchdev.control_vsi;
ice_eswitch_napi_disable(pf);
ice_eswitch_br_offloads_deinit(pf);
ice_eswitch_release_env(pf);
ice_rem_adv_rule_for_vsi(&pf->hw, ctrl_vsi->idx);
ice_eswitch_release_reprs(pf, ctrl_vsi);
ice_vsi_release(ctrl_vsi);
ice_repr_rem_from_all_vfs(pf);
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2023, Intel Corporation. */
#ifndef _ICE_ESWITCH_BR_H_
#define _ICE_ESWITCH_BR_H_
#include <linux/rhashtable.h>
#include <linux/workqueue.h>
struct ice_esw_br_fdb_data {
unsigned char addr[ETH_ALEN];
u16 vid;
};
struct ice_esw_br_flow {
struct ice_rule_query_data *fwd_rule;
struct ice_rule_query_data *guard_rule;
};
enum {
ICE_ESWITCH_BR_FDB_ADDED_BY_USER = BIT(0),
};
struct ice_esw_br_fdb_entry {
struct ice_esw_br_fdb_data data;
struct rhash_head ht_node;
struct list_head list;
int flags;
struct net_device *dev;
struct ice_esw_br_port *br_port;
struct ice_esw_br_flow *flow;
unsigned long last_use;
};
enum ice_esw_br_port_type {
ICE_ESWITCH_BR_UPLINK_PORT = 0,
ICE_ESWITCH_BR_VF_REPR_PORT = 1,
};
struct ice_esw_br_port {
struct ice_esw_br *bridge;
struct ice_vsi *vsi;
enum ice_esw_br_port_type type;
u16 vsi_idx;
u16 pvid;
struct xarray vlans;
};
enum {
ICE_ESWITCH_BR_VLAN_FILTERING = BIT(0),
};
struct ice_esw_br {
struct ice_esw_br_offloads *br_offloads;
struct xarray ports;
struct rhashtable fdb_ht;
struct list_head fdb_list;
int ifindex;
u32 flags;
unsigned long ageing_time;
};
struct ice_esw_br_offloads {
struct ice_pf *pf;
struct ice_esw_br *bridge;
struct notifier_block netdev_nb;
struct notifier_block switchdev_blk;
struct notifier_block switchdev_nb;
struct workqueue_struct *wq;
struct delayed_work update_work;
};
struct ice_esw_br_fdb_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct net_device *dev;
unsigned long event;
};
struct ice_esw_br_vlan {
u16 vid;
u16 flags;
};
#define ice_nb_to_br_offloads(nb, nb_name) \
container_of(nb, \
struct ice_esw_br_offloads, \
nb_name)
#define ice_work_to_br_offloads(w) \
container_of(w, \
struct ice_esw_br_offloads, \
update_work.work)
#define ice_work_to_fdb_work(w) \
container_of(w, \
struct ice_esw_br_fdb_work, \
work)
static inline bool ice_eswitch_br_is_vid_valid(u16 vid)
{
/* In trunk VLAN mode, for untagged traffic the bridge sends requests
* to offload VLAN 1 with pvid and untagged flags set. Since these
* flags are not supported, add a MAC filter instead.
*/
return vid > 1;
}
void
ice_eswitch_br_offloads_deinit(struct ice_pf *pf);
int
ice_eswitch_br_offloads_init(struct ice_pf *pf);
#endif /* _ICE_ESWITCH_BR_H_ */
......@@ -4076,3 +4076,28 @@ void ice_vsi_ctx_clear_allow_override(struct ice_vsi_ctx *ctx)
{
ctx->info.sec_flags &= ~ICE_AQ_VSI_SEC_FLAG_ALLOW_DEST_OVRD;
}
/**
* ice_vsi_update_local_lb - update sw block in VSI with local loopback bit
* @vsi: pointer to VSI structure
* @set: set or unset the bit
*/
int
ice_vsi_update_local_lb(struct ice_vsi *vsi, bool set)
{
struct ice_vsi_ctx ctx = {
.info = vsi->info,
};
ctx.info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SW_VALID);
if (set)
ctx.info.sw_flags |= ICE_AQ_VSI_SW_FLAG_LOCAL_LB;
else
ctx.info.sw_flags &= ~ICE_AQ_VSI_SW_FLAG_LOCAL_LB;
if (ice_update_vsi(&vsi->back->hw, vsi->idx, &ctx, NULL))
return -ENODEV;
vsi->info = ctx.info;
return 0;
}
......@@ -157,6 +157,7 @@ void ice_vsi_ctx_clear_antispoof(struct ice_vsi_ctx *ctx);
void ice_vsi_ctx_set_allow_override(struct ice_vsi_ctx *ctx);
void ice_vsi_ctx_clear_allow_override(struct ice_vsi_ctx *ctx);
int ice_vsi_update_local_lb(struct ice_vsi *vsi, bool set);
int ice_vsi_add_vlan_zero(struct ice_vsi *vsi);
int ice_vsi_del_vlan_zero(struct ice_vsi *vsi);
bool ice_vsi_has_non_zero_vlans(struct ice_vsi *vsi);
......
......@@ -80,7 +80,7 @@ ice_indr_setup_tc_cb(struct net_device *netdev, struct Qdisc *sch,
void *data,
void (*cleanup)(struct flow_block_cb *block_cb));
bool netif_is_ice(struct net_device *dev)
bool netif_is_ice(const struct net_device *dev)
{
return dev && (dev->netdev_ops == &ice_netdev_ops);
}
......@@ -5704,7 +5704,7 @@ static void ice_set_rx_mode(struct net_device *netdev)
struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_vsi *vsi = np->vsi;
if (!vsi)
if (!vsi || ice_is_switchdev_running(vsi->back))
return;
/* Set the flags to synchronize filters
......
......@@ -254,7 +254,7 @@ static const struct net_device_ops ice_repr_netdev_ops = {
* ice_is_port_repr_netdev - Check if a given netdevice is a port representor netdev
* @netdev: pointer to netdev
*/
bool ice_is_port_repr_netdev(struct net_device *netdev)
bool ice_is_port_repr_netdev(const struct net_device *netdev)
{
return netdev && (netdev->netdev_ops == &ice_repr_netdev_ops);
}
......
......@@ -12,6 +12,7 @@ struct ice_repr {
struct ice_q_vector *q_vector;
struct net_device *netdev;
struct metadata_dst *dst;
struct ice_esw_br_port *br_port;
#ifdef CONFIG_ICE_SWITCHDEV
/* info about slow path rule */
struct ice_rule_query_data sp_rule;
......@@ -27,5 +28,5 @@ void ice_repr_stop_tx_queues(struct ice_repr *repr);
void ice_repr_set_traffic_vsi(struct ice_repr *repr, struct ice_vsi *vsi);
struct ice_repr *ice_netdev_to_repr(struct net_device *netdev);
bool ice_is_port_repr_netdev(struct net_device *netdev);
bool ice_is_port_repr_netdev(const struct net_device *netdev);
#endif
This diff is collapsed.
......@@ -191,6 +191,8 @@ struct ice_adv_rule_info {
u16 vlan_type;
u16 fltr_rule_id;
u32 priority;
u16 need_pass_l2:1;
u16 allow_pass_l2:1;
u16 src_vsi;
struct ice_sw_act_ctrl sw_act;
struct ice_adv_rule_flags_info flags_info;
......@@ -254,6 +256,9 @@ struct ice_sw_recipe {
*/
u8 priority;
u8 need_pass_l2:1;
u8 allow_pass_l2:1;
struct list_head rg_list;
/* AQ buffer associated with this recipe */
......@@ -379,7 +384,6 @@ int
ice_set_vlan_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask,
bool rm_vlan_promisc);
int ice_rem_adv_rule_for_vsi(struct ice_hw *hw, u16 vsi_handle);
int
ice_rem_adv_rule_by_id(struct ice_hw *hw,
struct ice_rule_query_data *remove_entry);
......
......@@ -21,6 +21,7 @@
#define _ICE_TRACE_H_
#include <linux/tracepoint.h>
#include "ice_eswitch_br.h"
/* ice_trace() macro enables shared code to refer to trace points
* like:
......@@ -240,6 +241,95 @@ DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_fw_req);
DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_fw_done);
DEFINE_TX_TSTAMP_OP_EVENT(ice_tx_tstamp_complete);
DECLARE_EVENT_CLASS(ice_esw_br_fdb_template,
TP_PROTO(struct ice_esw_br_fdb_entry *fdb),
TP_ARGS(fdb),
TP_STRUCT__entry(__array(char, dev_name, IFNAMSIZ)
__array(unsigned char, addr, ETH_ALEN)
__field(u16, vid)
__field(int, flags)),
TP_fast_assign(strscpy(__entry->dev_name,
netdev_name(fdb->dev),
IFNAMSIZ);
memcpy(__entry->addr, fdb->data.addr, ETH_ALEN);
__entry->vid = fdb->data.vid;
__entry->flags = fdb->flags;),
TP_printk("net_device=%s addr=%pM vid=%u flags=%x",
__entry->dev_name,
__entry->addr,
__entry->vid,
__entry->flags)
);
DEFINE_EVENT(ice_esw_br_fdb_template,
ice_eswitch_br_fdb_entry_create,
TP_PROTO(struct ice_esw_br_fdb_entry *fdb),
TP_ARGS(fdb)
);
DEFINE_EVENT(ice_esw_br_fdb_template,
ice_eswitch_br_fdb_entry_find_and_delete,
TP_PROTO(struct ice_esw_br_fdb_entry *fdb),
TP_ARGS(fdb)
);
DECLARE_EVENT_CLASS(ice_esw_br_vlan_template,
TP_PROTO(struct ice_esw_br_vlan *vlan),
TP_ARGS(vlan),
TP_STRUCT__entry(__field(u16, vid)
__field(u16, flags)),
TP_fast_assign(__entry->vid = vlan->vid;
__entry->flags = vlan->flags;),
TP_printk("vid=%u flags=%x",
__entry->vid,
__entry->flags)
);
DEFINE_EVENT(ice_esw_br_vlan_template,
ice_eswitch_br_vlan_create,
TP_PROTO(struct ice_esw_br_vlan *vlan),
TP_ARGS(vlan)
);
DEFINE_EVENT(ice_esw_br_vlan_template,
ice_eswitch_br_vlan_cleanup,
TP_PROTO(struct ice_esw_br_vlan *vlan),
TP_ARGS(vlan)
);
#define ICE_ESW_BR_PORT_NAME_L 16
DECLARE_EVENT_CLASS(ice_esw_br_port_template,
TP_PROTO(struct ice_esw_br_port *port),
TP_ARGS(port),
TP_STRUCT__entry(__field(u16, vport_num)
__array(char, port_type, ICE_ESW_BR_PORT_NAME_L)),
TP_fast_assign(__entry->vport_num = port->vsi_idx;
if (port->type == ICE_ESWITCH_BR_UPLINK_PORT)
strscpy(__entry->port_type,
"Uplink",
ICE_ESW_BR_PORT_NAME_L);
else
strscpy(__entry->port_type,
"VF Representor",
ICE_ESW_BR_PORT_NAME_L);),
TP_printk("vport_num=%u port type=%s",
__entry->vport_num,
__entry->port_type)
);
DEFINE_EVENT(ice_esw_br_port_template,
ice_eswitch_br_port_link,
TP_PROTO(struct ice_esw_br_port *port),
TP_ARGS(port)
);
DEFINE_EVENT(ice_esw_br_port_template,
ice_eswitch_br_port_unlink,
TP_PROTO(struct ice_esw_br_port *port),
TP_ARGS(port)
);
/* End tracepoints */
#endif /* _ICE_TRACE_H_ */
......
......@@ -1033,6 +1033,7 @@ enum ice_sw_fwd_act_type {
ICE_FWD_TO_Q,
ICE_FWD_TO_QGRP,
ICE_DROP_PACKET,
ICE_NOP,
ICE_INVAL_ACT
};
......
......@@ -21,6 +21,99 @@ noop_vlan(struct ice_vsi __always_unused *vsi)
return 0;
}
static void ice_port_vlan_on(struct ice_vsi *vsi)
{
struct ice_vsi_vlan_ops *vlan_ops;
struct ice_pf *pf = vsi->back;
if (ice_is_dvm_ena(&pf->hw)) {
vlan_ops = &vsi->outer_vlan_ops;
/* setup outer VLAN ops */
vlan_ops->set_port_vlan = ice_vsi_set_outer_port_vlan;
vlan_ops->clear_port_vlan = ice_vsi_clear_outer_port_vlan;
vlan_ops->clear_port_vlan = ice_vsi_clear_outer_port_vlan;
/* setup inner VLAN ops */
vlan_ops = &vsi->inner_vlan_ops;
vlan_ops->add_vlan = noop_vlan_arg;
vlan_ops->del_vlan = noop_vlan_arg;
vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion;
} else {
vlan_ops = &vsi->inner_vlan_ops;
vlan_ops->set_port_vlan = ice_vsi_set_inner_port_vlan;
vlan_ops->clear_port_vlan = ice_vsi_clear_inner_port_vlan;
vlan_ops->clear_port_vlan = ice_vsi_clear_inner_port_vlan;
}
vlan_ops->ena_rx_filtering = ice_vsi_ena_rx_vlan_filtering;
}
static void ice_port_vlan_off(struct ice_vsi *vsi)
{
struct ice_vsi_vlan_ops *vlan_ops;
struct ice_pf *pf = vsi->back;
/* setup inner VLAN ops */
vlan_ops = &vsi->inner_vlan_ops;
vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion;
if (ice_is_dvm_ena(&pf->hw)) {
vlan_ops = &vsi->outer_vlan_ops;
vlan_ops->del_vlan = ice_vsi_del_vlan;
vlan_ops->ena_stripping = ice_vsi_ena_outer_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_outer_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_outer_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_outer_insertion;
} else {
vlan_ops->del_vlan = ice_vsi_del_vlan;
}
if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags))
vlan_ops->ena_rx_filtering = noop_vlan;
else
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
}
/**
* ice_vf_vsi_enable_port_vlan - Set VSI VLAN ops to support port VLAN
* @vsi: VF's VSI being configured
*
* The function won't create port VLAN, it only allows to create port VLAN
* using VLAN ops on the VF VSI.
*/
void ice_vf_vsi_enable_port_vlan(struct ice_vsi *vsi)
{
if (WARN_ON_ONCE(!vsi->vf))
return;
ice_port_vlan_on(vsi);
}
/**
* ice_vf_vsi_disable_port_vlan - Clear VSI support for creating port VLAN
* @vsi: VF's VSI being configured
*
* The function should be called after removing port VLAN on VSI
* (using VLAN ops)
*/
void ice_vf_vsi_disable_port_vlan(struct ice_vsi *vsi)
{
if (WARN_ON_ONCE(!vsi->vf))
return;
ice_port_vlan_off(vsi);
}
/**
* ice_vf_vsi_init_vlan_ops - Initialize default VSI VLAN ops for VF VSI
* @vsi: VF's VSI being configured
......@@ -39,91 +132,18 @@ void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
if (WARN_ON(!vf))
return;
if (ice_is_dvm_ena(&pf->hw)) {
vlan_ops = &vsi->outer_vlan_ops;
if (ice_vf_is_port_vlan_ena(vf))
ice_port_vlan_on(vsi);
else
ice_port_vlan_off(vsi);
/* outer VLAN ops regardless of port VLAN config */
vlan_ops->add_vlan = ice_vsi_add_vlan;
vlan_ops->ena_tx_filtering = ice_vsi_ena_tx_vlan_filtering;
vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering;
if (ice_vf_is_port_vlan_ena(vf)) {
/* setup outer VLAN ops */
vlan_ops->set_port_vlan = ice_vsi_set_outer_port_vlan;
/* all Rx traffic should be in the domain of the
* assigned port VLAN, so prevent disabling Rx VLAN
* filtering
*/
vlan_ops->dis_rx_filtering = noop_vlan;
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
/* setup inner VLAN ops */
vlan_ops = &vsi->inner_vlan_ops;
vlan_ops->add_vlan = noop_vlan_arg;
vlan_ops->del_vlan = noop_vlan_arg;
vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion;
} else {
vlan_ops->dis_rx_filtering =
ice_vsi_dis_rx_vlan_filtering;
if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags))
vlan_ops->ena_rx_filtering = noop_vlan;
else
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
vlan_ops->del_vlan = ice_vsi_del_vlan;
vlan_ops->ena_stripping = ice_vsi_ena_outer_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_outer_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_outer_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_outer_insertion;
/* setup inner VLAN ops */
vlan_ops = &vsi->inner_vlan_ops;
vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion;
}
} else {
vlan_ops = &vsi->inner_vlan_ops;
vlan_ops = ice_is_dvm_ena(&pf->hw) ?
&vsi->outer_vlan_ops : &vsi->inner_vlan_ops;
/* inner VLAN ops regardless of port VLAN config */
vlan_ops->add_vlan = ice_vsi_add_vlan;
vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering;
vlan_ops->ena_tx_filtering = ice_vsi_ena_tx_vlan_filtering;
vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering;
if (ice_vf_is_port_vlan_ena(vf)) {
vlan_ops->set_port_vlan = ice_vsi_set_inner_port_vlan;
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
/* all Rx traffic should be in the domain of the
* assigned port VLAN, so prevent disabling Rx VLAN
* filtering
*/
vlan_ops->dis_rx_filtering = noop_vlan;
} else {
vlan_ops->dis_rx_filtering =
ice_vsi_dis_rx_vlan_filtering;
if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags))
vlan_ops->ena_rx_filtering = noop_vlan;
else
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
vlan_ops->del_vlan = ice_vsi_del_vlan;
vlan_ops->ena_stripping = ice_vsi_ena_inner_stripping;
vlan_ops->dis_stripping = ice_vsi_dis_inner_stripping;
vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion;
}
}
vlan_ops->add_vlan = ice_vsi_add_vlan;
vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering;
vlan_ops->ena_tx_filtering = ice_vsi_ena_tx_vlan_filtering;
vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering;
}
/**
......
......@@ -13,7 +13,11 @@ void ice_vf_vsi_cfg_svm_legacy_vlan_mode(struct ice_vsi *vsi);
#ifdef CONFIG_PCI_IOV
void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi);
void ice_vf_vsi_enable_port_vlan(struct ice_vsi *vsi);
void ice_vf_vsi_disable_port_vlan(struct ice_vsi *vsi);
#else
static inline void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi) { }
static inline void ice_vf_vsi_enable_port_vlan(struct ice_vsi *vsi) { }
static inline void ice_vf_vsi_disable_port_vlan(struct ice_vsi *vsi) { }
#endif /* CONFIG_PCI_IOV */
#endif /* _ICE_PF_VSI_VLAN_OPS_H_ */
......@@ -202,6 +202,24 @@ int ice_vsi_dis_inner_insertion(struct ice_vsi *vsi)
return ice_vsi_manage_vlan_insertion(vsi);
}
static void
ice_save_vlan_info(struct ice_aqc_vsi_props *info,
struct ice_vsi_vlan_info *vlan)
{
vlan->sw_flags2 = info->sw_flags2;
vlan->inner_vlan_flags = info->inner_vlan_flags;
vlan->outer_vlan_flags = info->outer_vlan_flags;
}
static void
ice_restore_vlan_info(struct ice_aqc_vsi_props *info,
struct ice_vsi_vlan_info *vlan)
{
info->sw_flags2 = vlan->sw_flags2;
info->inner_vlan_flags = vlan->inner_vlan_flags;
info->outer_vlan_flags = vlan->outer_vlan_flags;
}
/**
* __ice_vsi_set_inner_port_vlan - set port VLAN VSI context settings to enable a port VLAN
* @vsi: the VSI to update
......@@ -218,6 +236,7 @@ static int __ice_vsi_set_inner_port_vlan(struct ice_vsi *vsi, u16 pvid_info)
if (!ctxt)
return -ENOMEM;
ice_save_vlan_info(&vsi->info, &vsi->vlan_info);
ctxt->info = vsi->info;
info = &ctxt->info;
info->inner_vlan_flags = ICE_AQ_VSI_INNER_VLAN_TX_MODE_ACCEPTUNTAGGED |
......@@ -259,6 +278,33 @@ int ice_vsi_set_inner_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan)
return __ice_vsi_set_inner_port_vlan(vsi, port_vlan_info);
}
int ice_vsi_clear_inner_port_vlan(struct ice_vsi *vsi)
{
struct ice_hw *hw = &vsi->back->hw;
struct ice_aqc_vsi_props *info;
struct ice_vsi_ctx *ctxt;
int ret;
ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
if (!ctxt)
return -ENOMEM;
ice_restore_vlan_info(&vsi->info, &vsi->vlan_info);
vsi->info.port_based_inner_vlan = 0;
ctxt->info = vsi->info;
info = &ctxt->info;
info->valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID |
ICE_AQ_VSI_PROP_SW_VALID);
ret = ice_update_vsi(hw, vsi->idx, ctxt, NULL);
if (ret)
dev_err(ice_hw_to_dev(hw), "update VSI for port VLAN failed, err %d aq_err %s\n",
ret, ice_aq_str(hw->adminq.sq_last_status));
kfree(ctxt);
return ret;
}
/**
* ice_cfg_vlan_pruning - enable or disable VLAN pruning on the VSI
* @vsi: VSI to enable or disable VLAN pruning on
......@@ -647,6 +693,7 @@ __ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, u16 vlan_info, u16 tpid)
if (!ctxt)
return -ENOMEM;
ice_save_vlan_info(&vsi->info, &vsi->vlan_info);
ctxt->info = vsi->info;
ctxt->info.sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA;
......@@ -689,9 +736,6 @@ __ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, u16 vlan_info, u16 tpid)
* used if DVM is supported. Also, this function should never be called directly
* as it should be part of ice_vsi_vlan_ops if it's needed.
*
* This function does not support clearing the port VLAN as there is currently
* no use case for this.
*
* Use the ice_vlan structure passed in to set this VSI in a port VLAN.
*/
int ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan)
......@@ -705,3 +749,37 @@ int ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan)
return __ice_vsi_set_outer_port_vlan(vsi, port_vlan_info, vlan->tpid);
}
/**
* ice_vsi_clear_outer_port_vlan - clear outer port vlan
* @vsi: VSI to configure
*
* The function is restoring previously set vlan config (saved in
* vsi->vlan_info). Setting happens in port vlan configuration.
*/
int ice_vsi_clear_outer_port_vlan(struct ice_vsi *vsi)
{
struct ice_hw *hw = &vsi->back->hw;
struct ice_vsi_ctx *ctxt;
int err;
ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
if (!ctxt)
return -ENOMEM;
ice_restore_vlan_info(&vsi->info, &vsi->vlan_info);
vsi->info.port_based_outer_vlan = 0;
ctxt->info = vsi->info;
ctxt->info.valid_sections =
cpu_to_le16(ICE_AQ_VSI_PROP_OUTER_TAG_VALID |
ICE_AQ_VSI_PROP_SW_VALID);
err = ice_update_vsi(hw, vsi->idx, ctxt, NULL);
if (err)
dev_err(ice_pf_to_dev(vsi->back), "update VSI for clearing outer port based VLAN failed, err %d aq_err %s\n",
err, ice_aq_str(hw->adminq.sq_last_status));
kfree(ctxt);
return err;
}
......@@ -7,6 +7,12 @@
#include <linux/types.h>
#include "ice_vlan.h"
struct ice_vsi_vlan_info {
u8 sw_flags2;
u8 inner_vlan_flags;
u8 outer_vlan_flags;
};
struct ice_vsi;
int ice_vsi_add_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan);
......@@ -17,6 +23,7 @@ int ice_vsi_dis_inner_stripping(struct ice_vsi *vsi);
int ice_vsi_ena_inner_insertion(struct ice_vsi *vsi, u16 tpid);
int ice_vsi_dis_inner_insertion(struct ice_vsi *vsi);
int ice_vsi_set_inner_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan);
int ice_vsi_clear_inner_port_vlan(struct ice_vsi *vsi);
int ice_vsi_ena_rx_vlan_filtering(struct ice_vsi *vsi);
int ice_vsi_dis_rx_vlan_filtering(struct ice_vsi *vsi);
......@@ -28,5 +35,6 @@ int ice_vsi_dis_outer_stripping(struct ice_vsi *vsi);
int ice_vsi_ena_outer_insertion(struct ice_vsi *vsi, u16 tpid);
int ice_vsi_dis_outer_insertion(struct ice_vsi *vsi);
int ice_vsi_set_outer_port_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan);
int ice_vsi_clear_outer_port_vlan(struct ice_vsi *vsi);
#endif /* _ICE_VSI_VLAN_LIB_H_ */
......@@ -21,6 +21,7 @@ struct ice_vsi_vlan_ops {
int (*ena_tx_filtering)(struct ice_vsi *vsi);
int (*dis_tx_filtering)(struct ice_vsi *vsi);
int (*set_port_vlan)(struct ice_vsi *vsi, struct ice_vlan *vlan);
int (*clear_port_vlan)(struct ice_vsi *vsi);
};
void ice_vsi_init_vlan_ops(struct ice_vsi *vsi);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment