drivers: net: cpsw: ale: add broadcast/multicast rate limit support
authorGrygorii Strashko <grygorii.strashko@ti.com>
Tue, 12 Apr 2022 10:29:27 +0000 (13:29 +0300)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 Apr 2022 11:42:39 +0000 (12:42 +0100)
The CPSW ALE supports feature to rate limit number ingress
broadcast(BC)/multicast(MC) packets per/sec which main purpose is BC/MC
storm prevention.

The ALE BC/MC packet rate limit configuration consist of two parts:
- global
  ALE_CONTROL.ENABLE_RATE_LIMIT bit 0 which enables rate limiting globally
  ALE_PRESCALE.PRESCALE specifies rate limiting interval
- per-port
  ALE_PORTCTLx.BCASTMCAST/_LIMIT specifies number of BC/MC packets allowed
  per rate limiting interval.
  When port.BCASTMCAST/_LIMIT is 0 rate limiting is disabled for Port.

When BC/MC packet rate limiting is enabled the number of allowed packets
per/sec is defined as:
  number_of_packets/sec = (Fclk / ALE_PRESCALE) * port.BCASTMCAST/_LIMIT

Hence, the ALE_PRESCALE configuration is common for all ports the 1ms
interval is selected and configured during ALE initialization while
port.BCAST/MCAST_LIMIT are configured per-port.
This allows to achieve:
 - min number_of_packets = 1000 when port.BCAST/MCAST_LIMIT = 1
 - max number_of_packets = 1000 * 255 = 255000
   when port.BCAST/MCAST_LIMIT = 0xFF

The ALE_CONTROL.ENABLE_RATE_LIMIT can also be enabled once during ALE
initialization as rate limiting enabled by non zero port.BCASTMCAST/_LIMIT
values.

This patch implements above logic in ALE and adds new ALE APIs
 cpsw_ale_rx_ratelimit_bc();
 cpsw_ale_rx_ratelimit_mc();

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_ale.h

index 1ef0aaef5c61cd25d8b10c1e5396c622196a8b87..231370e9a8017401fca51bbec3a0d82ae0aa6604 100644 (file)
@@ -50,6 +50,8 @@
 /* ALE_AGING_TIMER */
 #define ALE_AGING_TIMER_MASK   GENMASK(23, 0)
 
+#define ALE_RATE_LIMIT_MIN_PPS 1000
+
 /**
  * struct ale_entry_fld - The ALE tbl entry field description
  * @start_bit: field start bit
@@ -1136,6 +1138,50 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
        return tmp & BITMASK(info->bits);
 }
 
+int cpsw_ale_rx_ratelimit_mc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps)
+
+{
+       int val = ratelimit_pps / ALE_RATE_LIMIT_MIN_PPS;
+       u32 remainder = ratelimit_pps % ALE_RATE_LIMIT_MIN_PPS;
+
+       if (ratelimit_pps && !val) {
+               dev_err(ale->params.dev, "ALE MC port:%d ratelimit min value 1000pps\n", port);
+               return -EINVAL;
+       }
+
+       if (remainder)
+               dev_info(ale->params.dev, "ALE port:%d MC ratelimit set to %dpps (requested %d)\n",
+                        port, ratelimit_pps - remainder, ratelimit_pps);
+
+       cpsw_ale_control_set(ale, port, ALE_PORT_MCAST_LIMIT, val);
+
+       dev_dbg(ale->params.dev, "ALE port:%d MC ratelimit set %d\n",
+               port, val * ALE_RATE_LIMIT_MIN_PPS);
+       return 0;
+}
+
+int cpsw_ale_rx_ratelimit_bc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps)
+
+{
+       int val = ratelimit_pps / ALE_RATE_LIMIT_MIN_PPS;
+       u32 remainder = ratelimit_pps % ALE_RATE_LIMIT_MIN_PPS;
+
+       if (ratelimit_pps && !val) {
+               dev_err(ale->params.dev, "ALE port:%d BC ratelimit min value 1000pps\n", port);
+               return -EINVAL;
+       }
+
+       if (remainder)
+               dev_info(ale->params.dev, "ALE port:%d BC ratelimit set to %dpps (requested %d)\n",
+                        port, ratelimit_pps - remainder, ratelimit_pps);
+
+       cpsw_ale_control_set(ale, port, ALE_PORT_BCAST_LIMIT, val);
+
+       dev_dbg(ale->params.dev, "ALE port:%d BC ratelimit set %d\n",
+               port, val * ALE_RATE_LIMIT_MIN_PPS);
+       return 0;
+}
+
 static void cpsw_ale_timer(struct timer_list *t)
 {
        struct cpsw_ale *ale = from_timer(ale, t, timer);
@@ -1199,6 +1245,26 @@ static void cpsw_ale_aging_stop(struct cpsw_ale *ale)
 
 void cpsw_ale_start(struct cpsw_ale *ale)
 {
+       unsigned long ale_prescale;
+
+       /* configure Broadcast and Multicast Rate Limit
+        * number_of_packets = (Fclk / ALE_PRESCALE) * port.BCAST/MCAST_LIMIT
+        * ALE_PRESCALE width is 19bit and min value 0x10
+        * port.BCAST/MCAST_LIMIT is 8bit
+        *
+        * For multi port configuration support the ALE_PRESCALE is configured to 1ms interval,
+        * which allows to configure port.BCAST/MCAST_LIMIT per port and achieve:
+        * min number_of_packets = 1000 when port.BCAST/MCAST_LIMIT = 1
+        * max number_of_packets = 1000 * 255 = 255000 when port.BCAST/MCAST_LIMIT = 0xFF
+        */
+       ale_prescale = ale->params.bus_freq / ALE_RATE_LIMIT_MIN_PPS;
+       writel((u32)ale_prescale, ale->params.ale_regs + ALE_PRESCALE);
+
+       /* Allow MC/BC rate limiting globally.
+        * The actual Rate Limit cfg enabled per-port by port.BCAST/MCAST_LIMIT
+        */
+       cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT, 1);
+
        cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
        cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
 
index 13fe47687fded18498d126fda30177306960c876..aba4572cfa3bc50a4fda088902258f0ac0726d5c 100644 (file)
@@ -120,6 +120,8 @@ int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
                        int reg_mcast, int unreg_mcast);
 int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port);
 void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti, int port);
+int cpsw_ale_rx_ratelimit_bc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps);
+int cpsw_ale_rx_ratelimit_mc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps);
 
 int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
 int cpsw_ale_control_set(struct cpsw_ale *ale, int port,