Skip to content

Commit

Permalink
Merge branch 'dpaa2-switch-STP'
Browse files Browse the repository at this point in the history
Ioana Ciornei says:

====================
dpaa2-switch: add STP support

This patch set adds support for STP to the dpaa2-switch.

First of all, it fixes a bug which was determined by the improper usage
of bridge BR_STATE_* values directly in the MC ABI.
The next patches deal with creating an ACL table per port and trapping
the STP frames to the control interface by adding an entry into each
table.
The last patch configures proper learning state depending on the STP
state.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Mar 31, 2021
2 parents e48792a + bc96781 commit 578c97b
Show file tree
Hide file tree
Showing 5 changed files with 533 additions and 12 deletions.
152 changes: 140 additions & 12 deletions drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,17 +318,34 @@ static int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv,
return 0;
}

static enum dpsw_stp_state br_stp_state_to_dpsw(u8 state)
{
switch (state) {
case BR_STATE_DISABLED:
return DPSW_STP_STATE_DISABLED;
case BR_STATE_LISTENING:
return DPSW_STP_STATE_LISTENING;
case BR_STATE_LEARNING:
return DPSW_STP_STATE_LEARNING;
case BR_STATE_FORWARDING:
return DPSW_STP_STATE_FORWARDING;
case BR_STATE_BLOCKING:
return DPSW_STP_STATE_BLOCKING;
default:
return DPSW_STP_STATE_DISABLED;
}
}

static int dpaa2_switch_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state)
{
struct dpsw_stp_cfg stp_cfg = {
.state = state,
};
struct dpsw_stp_cfg stp_cfg = {0};
int err;
u16 vid;

if (!netif_running(port_priv->netdev) || state == port_priv->stp_state)
return 0; /* Nothing to do */

stp_cfg.state = br_stp_state_to_dpsw(state);
for (vid = 0; vid <= VLAN_VID_MASK; vid++) {
if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
stp_cfg.vlan_id = vid;
Expand Down Expand Up @@ -1233,14 +1250,6 @@ static void dpaa2_switch_teardown_irqs(struct fsl_mc_device *sw_dev)
fsl_mc_free_irqs(sw_dev);
}

static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
u8 state)
{
struct ethsw_port_priv *port_priv = netdev_priv(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;
Expand All @@ -1263,6 +1272,32 @@ static int dpaa2_switch_port_set_learning(struct ethsw_port_priv *port_priv, boo
return err;
}

static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
u8 state)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int err;

err = dpaa2_switch_port_set_stp_state(port_priv, state);
if (err)
return err;

switch (state) {
case BR_STATE_DISABLED:
case BR_STATE_BLOCKING:
case BR_STATE_LISTENING:
err = dpaa2_switch_port_set_learning(port_priv, false);
break;
case BR_STATE_LEARNING:
case BR_STATE_FORWARDING:
err = dpaa2_switch_port_set_learning(port_priv,
port_priv->learn_ena);
break;
}

return err;
}

static int dpaa2_switch_port_flood(struct ethsw_port_priv *port_priv,
struct switchdev_brport_flags flags)
{
Expand Down Expand Up @@ -1312,6 +1347,7 @@ static int dpaa2_switch_port_bridge_flags(struct net_device *netdev,
err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
if (err)
return err;
port_priv->learn_ena = learn_ena;
}

if (flags.mask & (BR_BCAST_FLOOD | BR_FLOOD | BR_MCAST_FLOOD)) {
Expand Down Expand Up @@ -1620,6 +1656,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
/* 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);
port_priv->learn_ena = learn_ena;

/* Setup the egress flood policy (broadcast, unknown unicast) */
err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
Expand Down Expand Up @@ -1702,6 +1739,7 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
err = dpaa2_switch_port_set_learning(port_priv, false);
if (err)
return err;
port_priv->learn_ena = false;

/* 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
Expand Down Expand Up @@ -2632,8 +2670,72 @@ static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
return err;
}

/* Add an ACL to redirect frames with specific destination MAC address to
* control interface
*/
static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,
const char *mac)
{
struct net_device *netdev = port_priv->netdev;
struct dpsw_acl_entry_cfg acl_entry_cfg;
struct dpsw_acl_fields *acl_h;
struct dpsw_acl_fields *acl_m;
struct dpsw_acl_key acl_key;
struct device *dev;
u8 *cmd_buff;
int err;

dev = port_priv->netdev->dev.parent;
acl_h = &acl_key.match;
acl_m = &acl_key.mask;

if (port_priv->acl_num_rules >= DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES) {
netdev_err(netdev, "ACL full\n");
return -ENOMEM;
}

memset(&acl_entry_cfg, 0, sizeof(acl_entry_cfg));
memset(&acl_key, 0, sizeof(acl_key));

/* Match on the destination MAC address */
ether_addr_copy(acl_h->l2_dest_mac, mac);
eth_broadcast_addr(acl_m->l2_dest_mac);

cmd_buff = kzalloc(DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE, GFP_KERNEL);
if (!cmd_buff)
return -ENOMEM;
dpsw_acl_prepare_entry_cfg(&acl_key, cmd_buff);

memset(&acl_entry_cfg, 0, sizeof(acl_entry_cfg));
acl_entry_cfg.precedence = port_priv->acl_num_rules;
acl_entry_cfg.result.action = DPSW_ACL_ACTION_REDIRECT_TO_CTRL_IF;
acl_entry_cfg.key_iova = dma_map_single(dev, cmd_buff,
DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, acl_entry_cfg.key_iova))) {
netdev_err(netdev, "DMA mapping failed\n");
return -EFAULT;
}

err = dpsw_acl_add_entry(port_priv->ethsw_data->mc_io, 0,
port_priv->ethsw_data->dpsw_handle,
port_priv->acl_tbl, &acl_entry_cfg);

dma_unmap_single(dev, acl_entry_cfg.key_iova, sizeof(cmd_buff),
DMA_TO_DEVICE);
if (err) {
netdev_err(netdev, "dpsw_acl_add_entry() failed %d\n", err);
return err;
}

port_priv->acl_num_rules++;

return 0;
}

static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
{
const char stpa[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
struct switchdev_obj_port_vlan vlan = {
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid = DEFAULT_VLAN_ID,
Expand All @@ -2642,8 +2744,10 @@ static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
struct net_device *netdev = port_priv->netdev;
struct ethsw_core *ethsw = port_priv->ethsw_data;
struct dpsw_fdb_cfg fdb_cfg = {0};
struct dpaa2_switch_fdb *fdb;
struct dpsw_acl_if_cfg acl_if_cfg;
struct dpsw_if_attr dpsw_if_attr;
struct dpaa2_switch_fdb *fdb;
struct dpsw_acl_cfg acl_cfg;
u16 fdb_id;
int err;

Expand Down Expand Up @@ -2685,6 +2789,29 @@ static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
if (err)
return err;

/* Create an ACL table to be used by this switch port */
acl_cfg.max_entries = DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES;
err = dpsw_acl_add(ethsw->mc_io, 0, ethsw->dpsw_handle,
&port_priv->acl_tbl, &acl_cfg);
if (err) {
netdev_err(netdev, "dpsw_acl_add err %d\n", err);
return err;
}

acl_if_cfg.if_id[0] = port_priv->idx;
acl_if_cfg.num_ifs = 1;
err = dpsw_acl_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
port_priv->acl_tbl, &acl_if_cfg);
if (err) {
netdev_err(netdev, "dpsw_acl_add_if err %d\n", err);
dpsw_acl_remove(ethsw->mc_io, 0, ethsw->dpsw_handle,
port_priv->acl_tbl);
}

err = dpaa2_switch_port_trap_mac_addr(port_priv, stpa);
if (err)
return err;

return err;
}

Expand Down Expand Up @@ -2801,6 +2928,7 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
err = dpaa2_switch_port_set_learning(port_priv, false);
if (err)
goto err_port_probe;
port_priv->learn_ena = false;

return 0;

Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
#define DPAA2_SWITCH_NEEDED_HEADROOM \
(DPAA2_SWITCH_TX_DATA_OFFSET + DPAA2_SWITCH_TX_BUF_ALIGN)

#define DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES 16
#define DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE 256

extern const struct ethtool_ops dpaa2_switch_port_ethtool_ops;

struct ethsw_core;
Expand Down Expand Up @@ -113,6 +116,10 @@ struct ethsw_port_priv {
struct dpaa2_switch_fdb *fdb;
bool bcast_flood;
bool ucast_flood;
bool learn_ena;

u16 acl_tbl;
u8 acl_num_rules;
};

/* Switch data */
Expand Down
75 changes: 75 additions & 0 deletions drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@
#define DPSW_CMDID_FDB_REMOVE_MULTICAST DPSW_CMD_ID(0x087)
#define DPSW_CMDID_FDB_DUMP DPSW_CMD_ID(0x08A)

#define DPSW_CMDID_ACL_ADD DPSW_CMD_ID(0x090)
#define DPSW_CMDID_ACL_REMOVE DPSW_CMD_ID(0x091)
#define DPSW_CMDID_ACL_ADD_ENTRY DPSW_CMD_ID(0x092)
#define DPSW_CMDID_ACL_ADD_IF DPSW_CMD_ID(0x094)
#define DPSW_CMDID_ACL_REMOVE_IF DPSW_CMD_ID(0x095)

#define DPSW_CMDID_IF_GET_PORT_MAC_ADDR DPSW_CMD_ID(0x0A7)

#define DPSW_CMDID_CTRL_IF_GET_ATTR DPSW_CMD_ID(0x0A0)
Expand Down Expand Up @@ -457,5 +463,74 @@ struct dpsw_cmd_if_set_learning_mode {
/* only the first 4 bits from LSB */
u8 mode;
};

struct dpsw_cmd_acl_add {
__le16 pad;
__le16 max_entries;
};

struct dpsw_rsp_acl_add {
__le16 acl_id;
};

struct dpsw_cmd_acl_remove {
__le16 acl_id;
};

struct dpsw_cmd_acl_if {
__le16 acl_id;
__le16 num_ifs;
__le32 pad;
__le64 if_id;
};

struct dpsw_prep_acl_entry {
u8 match_l2_dest_mac[6];
__le16 match_l2_tpid;

u8 match_l2_source_mac[6];
__le16 match_l2_vlan_id;

__le32 match_l3_dest_ip;
__le32 match_l3_source_ip;

__le16 match_l4_dest_port;
__le16 match_l4_source_port;
__le16 match_l2_ether_type;
u8 match_l2_pcp_dei;
u8 match_l3_dscp;

u8 mask_l2_dest_mac[6];
__le16 mask_l2_tpid;

u8 mask_l2_source_mac[6];
__le16 mask_l2_vlan_id;

__le32 mask_l3_dest_ip;
__le32 mask_l3_source_ip;

__le16 mask_l4_dest_port;
__le16 mask_l4_source_port;
__le16 mask_l2_ether_type;
u8 mask_l2_pcp_dei;
u8 mask_l3_dscp;

u8 match_l3_protocol;
u8 mask_l3_protocol;
};

#define DPSW_RESULT_ACTION_SHIFT 0
#define DPSW_RESULT_ACTION_SIZE 4

struct dpsw_cmd_acl_entry {
__le16 acl_id;
__le16 result_if_id;
__le32 precedence;
/* from LSB only the first 4 bits */
u8 result_action;
u8 pad[7];
__le64 pad2[4];
__le64 key_iova;
};
#pragma pack(pop)
#endif /* __FSL_DPSW_CMD_H */
Loading

0 comments on commit 578c97b

Please sign in to comment.