dpaa2-switch: add support for configuring learning state per port
authorIoana Ciornei <ioana.ciornei@nxp.com>
Mon, 22 Mar 2021 20:58:56 +0000 (22:58 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 22 Mar 2021 23:37:44 +0000 (16:37 -0700)
Add support for configuring the learning state of a switch port.
When the user requests the HW learning to be disabled, a fast-age
procedure on that specific port is run so that previously learnt
addresses do not linger.

At device probe as well as on a bridge leave action, the ports are
configured with HW learning disabled since they are basically a
standalone port.

At the same time, at bridge join we inherit the bridge port BR_LEARNING
flag state and configure it on the switch port.

There were already some MC firmware ABI functions for changing the
learning state, but those were per FDB (bridging domain) and not per
port so we need to adjust those to use the new MC fw command which is
per port.

Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
drivers/net/ethernet/freescale/dpaa2/dpsw.c
drivers/net/ethernet/freescale/dpaa2/dpsw.h

index 2db9cd7..2ea3a4d 100644 (file)
@@ -1238,6 +1238,56 @@ 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_pre_bridge_flags(struct net_device *netdev,
+                                             struct switchdev_brport_flags flags,
+                                             struct netlink_ext_ack *extack)
+{
+       if (flags.mask & ~(BR_LEARNING))
+               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;
+       }
+
+       return 0;
+}
+
 static int dpaa2_switch_port_attr_set(struct net_device *netdev,
                                      const struct switchdev_attr *attr,
                                      struct netlink_ext_ack *extack)
@@ -1256,6 +1306,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;
@@ -1504,6 +1560,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) {
@@ -1525,6 +1582,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)
@@ -1595,6 +1656,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
         */
@@ -2684,6 +2750,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:
index 996a59d..24b17d6 100644 (file)
@@ -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 */
index 56f3c23..6c787d4 100644 (file)
@@ -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);
+}
index f108cc6..96837b1 100644 (file)
@@ -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 */