Commit 0ca99c84 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dpaa2-switch-offload-port-flags'



Ioana Ciornei says:

====================
dpaa2-switch: offload bridge port flags to device

Add support for offloading bridge port flags to the switch. With this
patch set, the learning, broadcast flooding and unknown ucast/mcast
flooding states will be user configurable.

Apart from that, the last patch is a small fix that configures the
offload_fwd_mark if the switch port is under a bridge or not.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3adffc76 b175dfd7
Loading
Loading
Loading
Loading
+172 −42
Original line number Diff line number Diff line
@@ -110,6 +110,63 @@ static u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv,
	return 0;
}

static void dpaa2_switch_fdb_get_flood_cfg(struct ethsw_core *ethsw, u16 fdb_id,
					   enum dpsw_flood_type type,
					   struct dpsw_egress_flood_cfg *cfg)
{
	int i = 0, j;

	memset(cfg, 0, sizeof(*cfg));

	/* Add all the DPAA2 switch ports found in the same bridging domain to
	 * the egress flooding domain
	 */
	for (j = 0; j < ethsw->sw_attr.num_ifs; j++) {
		if (!ethsw->ports[j])
			continue;
		if (ethsw->ports[j]->fdb->fdb_id != fdb_id)
			continue;

		if (type == DPSW_BROADCAST && ethsw->ports[j]->bcast_flood)
			cfg->if_id[i++] = ethsw->ports[j]->idx;
		else if (type == DPSW_FLOODING && ethsw->ports[j]->ucast_flood)
			cfg->if_id[i++] = ethsw->ports[j]->idx;
	}

	/* Add the CTRL interface to the egress flooding domain */
	cfg->if_id[i++] = ethsw->sw_attr.num_ifs;

	cfg->fdb_id = fdb_id;
	cfg->flood_type = type;
	cfg->num_ifs = i;
}

static int dpaa2_switch_fdb_set_egress_flood(struct ethsw_core *ethsw, u16 fdb_id)
{
	struct dpsw_egress_flood_cfg flood_cfg;
	int err;

	/* Setup broadcast flooding domain */
	dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_BROADCAST, &flood_cfg);
	err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle,
				    &flood_cfg);
	if (err) {
		dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err);
		return err;
	}

	/* Setup unknown flooding domain */
	dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_FLOODING, &flood_cfg);
	err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle,
				    &flood_cfg);
	if (err) {
		dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err);
		return err;
	}

	return 0;
}

static void *dpaa2_iova_to_virt(struct iommu_domain *domain,
				dma_addr_t iova_addr)
{
@@ -1184,6 +1241,88 @@ static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
	return dpaa2_switch_port_set_stp_state(port_priv, state);
}

static int dpaa2_switch_port_set_learning(struct ethsw_port_priv *port_priv, bool enable)
{
	struct ethsw_core *ethsw = port_priv->ethsw_data;
	enum dpsw_learning_mode learn_mode;
	int err;

	if (enable)
		learn_mode = DPSW_LEARNING_MODE_HW;
	else
		learn_mode = DPSW_LEARNING_MODE_DIS;

	err = dpsw_if_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle,
					port_priv->idx, learn_mode);
	if (err)
		netdev_err(port_priv->netdev, "dpsw_if_set_learning_mode err %d\n", err);

	if (!enable)
		dpaa2_switch_port_fast_age(port_priv);

	return err;
}

static int dpaa2_switch_port_flood(struct ethsw_port_priv *port_priv,
				   struct switchdev_brport_flags flags)
{
	struct ethsw_core *ethsw = port_priv->ethsw_data;

	if (flags.mask & BR_BCAST_FLOOD)
		port_priv->bcast_flood = !!(flags.val & BR_BCAST_FLOOD);

	if (flags.mask & BR_FLOOD)
		port_priv->ucast_flood = !!(flags.val & BR_FLOOD);

	return dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
}

static int dpaa2_switch_port_pre_bridge_flags(struct net_device *netdev,
					      struct switchdev_brport_flags flags,
					      struct netlink_ext_ack *extack)
{
	if (flags.mask & ~(BR_LEARNING | BR_BCAST_FLOOD | BR_FLOOD |
			   BR_MCAST_FLOOD))
		return -EINVAL;

	if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD)) {
		bool multicast = !!(flags.val & BR_MCAST_FLOOD);
		bool unicast = !!(flags.val & BR_FLOOD);

		if (unicast != multicast) {
			NL_SET_ERR_MSG_MOD(extack,
					   "Cannot configure multicast flooding independently of unicast");
			return -EINVAL;
		}
	}

	return 0;
}

static int dpaa2_switch_port_bridge_flags(struct net_device *netdev,
					  struct switchdev_brport_flags flags,
					  struct netlink_ext_ack *extack)
{
	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
	int err;

	if (flags.mask & BR_LEARNING) {
		bool learn_ena = !!(flags.val & BR_LEARNING);

		err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
		if (err)
			return err;
	}

	if (flags.mask & (BR_BCAST_FLOOD | BR_FLOOD | BR_MCAST_FLOOD)) {
		err = dpaa2_switch_port_flood(port_priv, flags);
		if (err)
			return err;
	}

	return 0;
}

static int dpaa2_switch_port_attr_set(struct net_device *netdev,
				      const struct switchdev_attr *attr,
				      struct netlink_ext_ack *extack)
@@ -1202,6 +1341,12 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev,
			return -EOPNOTSUPP;
		}
		break;
	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
		err = dpaa2_switch_port_pre_bridge_flags(netdev, attr->u.brport_flags, extack);
		break;
	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
		err = dpaa2_switch_port_bridge_flags(netdev, attr->u.brport_flags, extack);
		break;
	default:
		err = -EOPNOTSUPP;
		break;
@@ -1442,48 +1587,6 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
	return notifier_from_errno(err);
}

static int dpaa2_switch_fdb_set_egress_flood(struct ethsw_core *ethsw, u16 fdb_id)
{
	struct dpsw_egress_flood_cfg flood_cfg;
	int i = 0, j;
	int err;

	/* Add all the DPAA2 switch ports found in the same bridging domain to
	 * the egress flooding domain
	 */
	for (j = 0; j < ethsw->sw_attr.num_ifs; j++)
		if (ethsw->ports[j] && ethsw->ports[j]->fdb->fdb_id == fdb_id)
			flood_cfg.if_id[i++] = ethsw->ports[j]->idx;

	/* Add the CTRL interface to the egress flooding domain */
	flood_cfg.if_id[i++] = ethsw->sw_attr.num_ifs;

	/* Use the FDB of the first dpaa2 switch port added to the bridge */
	flood_cfg.fdb_id = fdb_id;

	/* Setup broadcast flooding domain */
	flood_cfg.flood_type = DPSW_BROADCAST;
	flood_cfg.num_ifs = i;
	err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle,
				    &flood_cfg);
	if (err) {
		dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err);
		return err;
	}

	/* Setup unknown flooding domain */
	flood_cfg.flood_type = DPSW_FLOODING;
	flood_cfg.num_ifs = i;
	err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle,
				    &flood_cfg);
	if (err) {
		dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err);
		return err;
	}

	return 0;
}

static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
					 struct net_device *upper_dev)
{
@@ -1492,6 +1595,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
	struct ethsw_port_priv *other_port_priv;
	struct net_device *other_dev;
	struct list_head *iter;
	bool learn_ena;
	int err;

	netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
@@ -1513,6 +1617,10 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,

	dpaa2_switch_port_set_fdb(port_priv, upper_dev);

	/* Inherit the initial bridge port learning state */
	learn_ena = br_port_flag_is_set(netdev, BR_LEARNING);
	err = dpaa2_switch_port_set_learning(port_priv, learn_ena);

	/* Setup the egress flood policy (broadcast, unknown unicast) */
	err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
	if (err)
@@ -1570,6 +1678,13 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
	if (err)
		netdev_err(netdev, "Unable to restore RX VLANs to the new FDB, err (%d)\n", err);

	/* Reset the flooding state to denote that this port can send any
	 * packet in standalone mode. With this, we are also ensuring that any
	 * later bridge join will have the flooding flag on.
	 */
	port_priv->bcast_flood = true;
	port_priv->ucast_flood = true;

	/* Setup the egress flood policy (broadcast, unknown unicast).
	 * When the port is not under a bridge, only the CTRL interface is part
	 * of the flooding domain besides the actual port
@@ -1583,6 +1698,11 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
	if (err)
		return err;

	/* No HW learning when not under a bridge */
	err = dpaa2_switch_port_set_learning(port_priv, false);
	if (err)
		return err;

	/* Add the VLAN 1 as PVID when not under a bridge. We need this since
	 * the dpaa2 switch interfaces are not capable to be VLAN unaware
	 */
@@ -1885,6 +2005,9 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,
	skb->dev = netdev;
	skb->protocol = eth_type_trans(skb, skb->dev);

	/* Setup the offload_fwd_mark only if the port is under a bridge */
	skb->offload_fwd_mark = !!(port_priv->fdb->bridge_dev);

	netif_receive_skb(skb);

	return;
@@ -2650,6 +2773,9 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,

	port_netdev->needed_headroom = DPAA2_SWITCH_NEEDED_HEADROOM;

	port_priv->bcast_flood = true;
	port_priv->ucast_flood = true;

	/* Set MTU limits */
	port_netdev->min_mtu = ETH_MIN_MTU;
	port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
@@ -2672,6 +2798,10 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
	if (err)
		goto err_port_probe;

	err = dpaa2_switch_port_set_learning(port_priv, false);
	if (err)
		goto err_port_probe;

	return 0;

err_port_probe:
+2 −1
Original line number Diff line number Diff line
@@ -105,13 +105,14 @@ struct ethsw_port_priv {
	struct ethsw_core	*ethsw_data;
	u8			link_state;
	u8			stp_state;
	bool			flood;

	u8			vlans[VLAN_VID_MASK + 1];
	u16			pvid;
	u16			tx_qdid;

	struct dpaa2_switch_fdb	*fdb;
	bool			bcast_flood;
	bool			ucast_flood;
};

/* Switch data */
+10 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@
#define DPSW_CMDID_CTRL_IF_SET_QUEUE        DPSW_CMD_ID(0x0A6)

#define DPSW_CMDID_SET_EGRESS_FLOOD         DPSW_CMD_ID(0x0AC)
#define DPSW_CMDID_IF_SET_LEARNING_MODE     DPSW_CMD_ID(0x0AD)

/* Macros for accessing command fields smaller than 1byte */
#define DPSW_MASK(field)        \
@@ -447,5 +448,14 @@ struct dpsw_cmd_set_egress_flood {
	u8 pad[5];
	__le64 if_id;
};

#define DPSW_LEARNING_MODE_SHIFT	0
#define DPSW_LEARNING_MODE_SIZE		4

struct dpsw_cmd_if_set_learning_mode {
	__le16 if_id;
	/* only the first 4 bits from LSB */
	u8 mode;
};
#pragma pack(pop)
#endif /* __FSL_DPSW_CMD_H */
+27 −0
Original line number Diff line number Diff line
@@ -1327,3 +1327,30 @@ int dpsw_set_egress_flood(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,

	return mc_send_command(mc_io, &cmd);
}

/**
 * dpsw_if_set_learning_mode() - Configure the learning mode on an interface.
 * If this API is used, it will take precedence over the FDB configuration.
 * @mc_io:	Pointer to MC portal's I/O object
 * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
 * @token:	Token of DPSW object
 * @if_id:	InterfaceID
 * @mode:	Learning mode
 *
 * Return:	Completion status. '0' on Success; Error code otherwise.
 */
int dpsw_if_set_learning_mode(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
			      u16 if_id, enum dpsw_learning_mode mode)
{
	struct dpsw_cmd_if_set_learning_mode *cmd_params;
	struct fsl_mc_command cmd = { 0 };

	cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_LEARNING_MODE,
					  cmd_flags,
					  token);
	cmd_params = (struct dpsw_cmd_if_set_learning_mode *)cmd.params;
	cmd_params->if_id = cpu_to_le16(if_id);
	dpsw_set_field(cmd_params->mode, LEARNING_MODE, mode);

	return mc_send_command(mc_io, &cmd);
}
+14 −11
Original line number Diff line number Diff line
@@ -532,11 +532,11 @@ int dpsw_fdb_remove_multicast(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
			      u16 fdb_id, const struct dpsw_fdb_multicast_cfg *cfg);

/**
 * enum dpsw_fdb_learning_mode - Auto-learning modes
 * @DPSW_FDB_LEARNING_MODE_DIS: Disable Auto-learning
 * @DPSW_FDB_LEARNING_MODE_HW: Enable HW auto-Learning
 * @DPSW_FDB_LEARNING_MODE_NON_SECURE: Enable None secure learning by CPU
 * @DPSW_FDB_LEARNING_MODE_SECURE: Enable secure learning by CPU
 * enum dpsw_learning_mode - Auto-learning modes
 * @DPSW_LEARNING_MODE_DIS: Disable Auto-learning
 * @DPSW_LEARNING_MODE_HW: Enable HW auto-Learning
 * @DPSW_LEARNING_MODE_NON_SECURE: Enable None secure learning by CPU
 * @DPSW_LEARNING_MODE_SECURE: Enable secure learning by CPU
 *
 *	NONE - SECURE LEARNING
 *	SMAC found	DMAC found	CTLU Action
@@ -561,11 +561,11 @@ int dpsw_fdb_remove_multicast(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
 *	-		-		Forward frame to
 *						1.  Control interface
 */
enum dpsw_fdb_learning_mode {
	DPSW_FDB_LEARNING_MODE_DIS = 0,
	DPSW_FDB_LEARNING_MODE_HW = 1,
	DPSW_FDB_LEARNING_MODE_NON_SECURE = 2,
	DPSW_FDB_LEARNING_MODE_SECURE = 3
enum dpsw_learning_mode {
	DPSW_LEARNING_MODE_DIS = 0,
	DPSW_LEARNING_MODE_HW = 1,
	DPSW_LEARNING_MODE_NON_SECURE = 2,
	DPSW_LEARNING_MODE_SECURE = 3
};

/**
@@ -579,7 +579,7 @@ enum dpsw_fdb_learning_mode {
struct dpsw_fdb_attr {
	u16 max_fdb_entries;
	u16 fdb_ageing_time;
	enum dpsw_fdb_learning_mode learning_mode;
	enum dpsw_learning_mode learning_mode;
	u16 num_fdb_mc_groups;
	u16 max_fdb_mc_groups;
};
@@ -625,4 +625,7 @@ struct dpsw_egress_flood_cfg {
int dpsw_set_egress_flood(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
			  const struct dpsw_egress_flood_cfg *cfg);

int dpsw_if_set_learning_mode(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
			      u16 if_id, enum dpsw_learning_mode mode);

#endif /* __FSL_DPSW_H */