Skip to content

Commit

Permalink
ice: implement bridge port vlan
Browse files Browse the repository at this point in the history
Port VLAN in this case means push and pop VLAN action on specific vid.
There are a few limitation in hardware:
- push and pop can't be used separately
- if port VLAN is used there can't be any trunk VLANs, because pop
  action is done on all traffic received by VSI in port VLAN mode
- port VLAN mode on uplink port isn't supported

Reflect these limitations in code using dev_info to inform the user
about unsupported configuration.

In bridge mode there is a need to configure port vlan without resetting
VFs. To do that implement ice_port_vlan_on/off() functions. They are
only configuring correct vlan_ops to allow setting port vlan.

We also need to clear port vlan without resetting the VF which is not
supported right now. Change it by implementing clear_port_vlan ops.
As previous VLAN configuration isn't always the same, store current
config while creating port vlan and restore it in clear function.

Configuration steps:
- configure switchdev with bridge
- #bridge vlan add dev eth0 vid 120 pvid untagged
- #bridge vlan add dev eth1 vid 120 pvid untagged
- ping from VF0 to VF1

Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com>
Tested-by: Sujai Buvaneswaran <sujai.buvaneswaran@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
  • Loading branch information
Michal Swiatkowski authored and Tony Nguyen committed Jul 24, 2023
1 parent e9dda2c commit 2946204
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 90 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/ice/ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
92 changes: 88 additions & 4 deletions drivers/net/ethernet/intel/ice/ice_eswitch_br.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "ice_eswitch_br.h"
#include "ice_repr.h"
#include "ice_switch.h"
#include "ice_vlan.h"
#include "ice_vf_vsi_vlan_ops.h"

static const struct rhashtable_params ice_fdb_ht_params = {
.key_offset = offsetof(struct ice_esw_br_fdb_entry, data),
Expand Down Expand Up @@ -569,6 +571,22 @@ ice_eswitch_br_vlan_filtering_set(struct ice_esw_br *bridge, bool enable)
bridge->flags &= ~ICE_ESWITCH_BR_VLAN_FILTERING;
}

static void
ice_eswitch_br_clear_pvid(struct ice_esw_br_port *port)
{
struct ice_vlan port_vlan = ICE_VLAN(ETH_P_8021Q, port->pvid, 0);
struct ice_vsi_vlan_ops *vlan_ops;

vlan_ops = ice_get_compat_vsi_vlan_ops(port->vsi);

vlan_ops->del_vlan(port->vsi, &port_vlan);
vlan_ops->clear_port_vlan(port->vsi);

ice_vf_vsi_disable_port_vlan(port->vsi);

port->pvid = 0;
}

static void
ice_eswitch_br_vlan_cleanup(struct ice_esw_br_port *port,
struct ice_esw_br_vlan *vlan)
Expand All @@ -582,6 +600,8 @@ ice_eswitch_br_vlan_cleanup(struct ice_esw_br_port *port,
}

xa_erase(&port->vlans, vlan->vid);
if (port->pvid == vlan->vid)
ice_eswitch_br_clear_pvid(port);
kfree(vlan);
}

Expand All @@ -594,9 +614,50 @@ static void ice_eswitch_br_port_vlans_flush(struct ice_esw_br_port *port)
ice_eswitch_br_vlan_cleanup(port, vlan);
}

static int
ice_eswitch_br_set_pvid(struct ice_esw_br_port *port,
struct ice_esw_br_vlan *vlan)
{
struct ice_vlan port_vlan = ICE_VLAN(ETH_P_8021Q, vlan->vid, 0);
struct device *dev = ice_pf_to_dev(port->vsi->back);
struct ice_vsi_vlan_ops *vlan_ops;
int err;

if (port->pvid == vlan->vid || vlan->vid == 1)
return 0;

/* Setting port vlan on uplink isn't supported by hw */
if (port->type == ICE_ESWITCH_BR_UPLINK_PORT)
return -EOPNOTSUPP;

if (port->pvid) {
dev_info(dev,
"Port VLAN (vsi=%u, vid=%u) already exists on the port, remove it before adding new one\n",
port->vsi_idx, port->pvid);
return -EEXIST;
}

ice_vf_vsi_enable_port_vlan(port->vsi);

vlan_ops = ice_get_compat_vsi_vlan_ops(port->vsi);
err = vlan_ops->set_port_vlan(port->vsi, &port_vlan);
if (err)
return err;

err = vlan_ops->add_vlan(port->vsi, &port_vlan);
if (err)
return err;

ice_eswitch_br_port_vlans_flush(port);
port->pvid = vlan->vid;

return 0;
}

static struct ice_esw_br_vlan *
ice_eswitch_br_vlan_create(u16 vid, u16 flags, struct ice_esw_br_port *port)
{
struct device *dev = ice_pf_to_dev(port->vsi->back);
struct ice_esw_br_vlan *vlan;
int err;

Expand All @@ -606,14 +667,30 @@ ice_eswitch_br_vlan_create(u16 vid, u16 flags, struct ice_esw_br_port *port)

vlan->vid = vid;
vlan->flags = flags;
if ((flags & BRIDGE_VLAN_INFO_PVID) &&
(flags & BRIDGE_VLAN_INFO_UNTAGGED)) {
err = ice_eswitch_br_set_pvid(port, vlan);
if (err)
goto err_set_pvid;
} else if ((flags & BRIDGE_VLAN_INFO_PVID) ||
(flags & BRIDGE_VLAN_INFO_UNTAGGED)) {
dev_info(dev, "VLAN push and pop are supported only simultaneously\n");
err = -EOPNOTSUPP;
goto err_set_pvid;
}

err = xa_insert(&port->vlans, vlan->vid, vlan, GFP_KERNEL);
if (err) {
kfree(vlan);
return ERR_PTR(err);
}
if (err)
goto err_insert;

return vlan;

err_insert:
if (port->pvid)
ice_eswitch_br_clear_pvid(port);
err_set_pvid:
kfree(vlan);
return ERR_PTR(err);
}

static int
Expand All @@ -627,6 +704,13 @@ ice_eswitch_br_port_vlan_add(struct ice_esw_br *bridge, u16 vsi_idx, u16 vid,
if (!port)
return -EINVAL;

if (port->pvid) {
dev_info(ice_pf_to_dev(port->vsi->back),
"Port VLAN (vsi=%u, vid=%d) exists on the port, remove it to add trunk VLANs\n",
port->vsi_idx, port->pvid);
return -EEXIST;
}

vlan = xa_load(&port->vlans, vid);
if (vlan) {
if (vlan->flags == flags)
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/ice/ice_eswitch_br.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct ice_esw_br_port {
struct ice_vsi *vsi;
enum ice_esw_br_port_type type;
u16 vsi_idx;
u16 pvid;
struct xarray vlans;
};

Expand Down
186 changes: 103 additions & 83 deletions drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
Loading

0 comments on commit 2946204

Please sign in to comment.