Commit 2946204b authored by Michal Swiatkowski's avatar Michal Swiatkowski Committed by Tony Nguyen
Browse files

ice: implement bridge port vlan



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: default avatarSimon Horman <simon.horman@corigine.com>
Signed-off-by: default avatarMichal Swiatkowski <michal.swiatkowski@linux.intel.com>
Signed-off-by: default avatarWojciech Drewek <wojciech.drewek@intel.com>
Tested-by: default avatarSujai Buvaneswaran <sujai.buvaneswaran@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent e9dda2cf
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+88 −4
Original line number Diff line number Diff line
@@ -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),
@@ -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)
@@ -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);
}

@@ -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;

@@ -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
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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;
};

+103 −83
Original line number Diff line number Diff line
@@ -21,42 +21,18 @@ noop_vlan(struct ice_vsi __always_unused *vsi)
	return 0;
}

/**
 * ice_vf_vsi_init_vlan_ops - Initialize default VSI VLAN ops for VF VSI
 * @vsi: VF's VSI being configured
 *
 * If Double VLAN Mode (DVM) is enabled, assume that the VF supports the new
 * VIRTCHNL_VF_VLAN_OFFLOAD_V2 capability and set up the VLAN ops accordingly.
 * If SVM is enabled maintain the same level of VLAN support previous to
 * VIRTCHNL_VF_VLAN_OFFLOAD_V2.
 */
void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
static void ice_port_vlan_on(struct ice_vsi *vsi)
{
	struct ice_vsi_vlan_ops *vlan_ops;
	struct ice_pf *pf = vsi->back;
	struct ice_vf *vf = vsi->vf;

	if (WARN_ON(!vf))
		return;

	if (ice_is_dvm_ena(&pf->hw)) {
		vlan_ops = &vsi->outer_vlan_ops;

		/* 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;
		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;
@@ -67,20 +43,19 @@ void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
		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;
		vlan_ops = &vsi->inner_vlan_ops;

			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->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;
}

			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;
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;
@@ -89,41 +64,86 @@ void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
	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;

		/* 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_is_dvm_ena(&pf->hw)) {
		vlan_ops = &vsi->outer_vlan_ops;

		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;
		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->dis_rx_filtering =
				ice_vsi_dis_rx_vlan_filtering;
		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;
}

			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;
/**
 * 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
 *
 * If Double VLAN Mode (DVM) is enabled, assume that the VF supports the new
 * VIRTCHNL_VF_VLAN_OFFLOAD_V2 capability and set up the VLAN ops accordingly.
 * If SVM is enabled maintain the same level of VLAN support previous to
 * VIRTCHNL_VF_VLAN_OFFLOAD_V2.
 */
void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
{
	struct ice_vsi_vlan_ops *vlan_ops;
	struct ice_pf *pf = vsi->back;
	struct ice_vf *vf = vsi->vf;

	if (WARN_ON(!vf))
		return;

	if (ice_vf_is_port_vlan_ena(vf))
		ice_port_vlan_on(vsi);
	else
		ice_port_vlan_off(vsi);

	vlan_ops = ice_is_dvm_ena(&pf->hw) ?
		&vsi->outer_vlan_ops : &vsi->inner_vlan_ops;

	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;
}

/**
+4 −0
Original line number Diff line number Diff line
@@ -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