]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
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 2db9cd78201da7f89d30ea1c8460aea0aa759bc9..2ea3a4dac49d92f0b743e0b489f6ef925f3276b6 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 996a59dcd01db8c8fadc7e638d997368216bc79f..24b17d6e09af5c68813b14b8ec9e8c2df0835175 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 56f3c23fce0762ccf8344a964f90165b7462b5e6..6c787d4b85f978995f958c2c6408c03e95f41873 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 f108cc61bb27b09cd529e0f092c9e8f29e6b3903..96837b10cc94bc1f30313bbb24daf40596a6adcc 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 */