Эх сурвалжийг харах

HACK: drivers: net: cpsw: add broadcast/multicast rate limit support

Add broadcast/multicast rate limit support via switch config ioctl to
limit the number of broadcast/multicast packets processed by ALE.

For CPSW ALE multicast (MC) / broadcast (BC) Rate limit, ENABLE_RATE_LIMIT
bit has to be set in ALE_CONTROL Register.

The MC/BC Rate limit feature expected to limit number of BC/MC packets per
sec which need to be configured as below:

- ale_port.BCASTMCAST/_LIMIT fields and ALE_PRESCALE register have to be
  configured to achieve desired MC/BC Rate limit:

 number_of_packets/sec = (Fclk / ALE_PRESCALE) * port.BCASTMCAST/_LIMIT
 where: ALE_PRESCALE width is 19bit and min value 0x10.

 with Fclk = 125MHz and port.BCASTMCAST/_LIMIT = 1
  max number_of_packets/sec = (125MHz / 0x10) * 1 = 7 812 500
  min number_of_packets/sec = (125MHz / 0xFFFFF) * 1 = 119

above values are more than enough (with higher Fclk they will be just
better), so port.BCASTMCAST/_LIMIT can be selected to be 1 while
ALE_PRESCALE is calculated as:

 ALE_PRESCALE = Fclk / number_of_packets

- RATE_LIMIT_TX bit has to be set in ALE_CONTROL Register to 1 for TX and 0
  for RX

- ENABLE_RATE_LIMIT bit has to be set in ALE_CONTROL Register

This patch implements above logic.

Testing:
 # ifconfig - check "RX packets:X"
 # switch-config --add-multi 01:00:5E:00:00:05 -n 7
 # switch-config -l -n 1 -L 1000 -B 1000
 # on host PC generate BC/MC traffic
  (packeth: gen-b->num_packets=10000
  gen-b->delay=100
 # ifconfig - check "RX packets:X+1000"

TODO:
The patch is marked as a hack because switch-configuration in general
uses some non-standard private ioctls. This needs to be moved to a
standard kernel framework.

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Grygorii Strashko 7 жил өмнө
parent
commit
31434ca71b

+ 17 - 0
drivers/net/ethernet/ti/cpsw.c

@@ -2456,6 +2456,23 @@ static int cpsw_switch_config_ioctl(struct net_device *ndev,
 
 		break;
 	}
+	case CONFIG_SWITCH_RATELIMIT:
+	{
+		if (config.port > 2) {
+			dev_err(priv->dev, "Invalid Port number\n");
+			break;
+		}
+
+		ret = cpsw_ale_set_ratelimit(cpsw->ale,
+					     cpsw->bus_freq_mhz * 1000000,
+					     config.port,
+					     config.bcast_rate_limit,
+					     config.mcast_rate_limit,
+					     !!config.direction);
+		if (ret)
+			dev_err(priv->dev, "CPSW_ALE set ratelimit failed");
+		break;
+	}
 
 	default:
 		ret = -EOPNOTSUPP;

+ 49 - 0
drivers/net/ethernet/ti/cpsw_ale.c

@@ -765,6 +765,55 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
 }
 EXPORT_SYMBOL_GPL(cpsw_ale_control_get);
 
+int cpsw_ale_set_ratelimit(struct cpsw_ale *ale, unsigned long freq, int port,
+			   unsigned int bcast_rate_limit,
+			   unsigned int mcast_rate_limit,
+			   bool direction)
+
+{
+	unsigned int rate_limit;
+	unsigned long ale_prescale;
+	int val;
+
+	if (!bcast_rate_limit && !mcast_rate_limit) {
+		/* disable rate limit */
+		cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT, 0);
+		cpsw_ale_control_set(ale, port, ALE_PORT_BCAST_LIMIT, 0);
+		cpsw_ale_control_set(ale, port, ALE_PORT_MCAST_LIMIT, 0);
+		writel(0, ale->params.ale_regs + ALE_PRESCALE);
+		return 0;
+	}
+
+	/* configure Broadcast and Multicast Rate Limit
+	 * number_of_packets = (Fclk / ALE_PRESCALE) * port.BCASTMCAST/_LIMIT
+	 * ALE_PRESCALE width is 19bit and min value 0x10
+	 * with Fclk = 125MHz and port.BCASTMCAST/_LIMIT = 1
+	 *
+	 * max number_of_packets = (125MHz / 0x10) * 1 = 7812500
+	 * min number_of_packets = (125MHz / 0xFFFFF) * 1 = 119
+	 *
+	 * above values are more than enough (with higher Fclk they will be
+	 * just better), so port.BCASTMCAST/_LIMIT can be selected to be 1
+	 * while ALE_PRESCALE is calculated as:
+	 *  ALE_PRESCALE = Fclk / number_of_packets
+	 */
+	rate_limit = max_t(unsigned int, bcast_rate_limit, mcast_rate_limit);
+	ale_prescale = freq / rate_limit;
+	if (ale_prescale & (~0xfffff))
+		return -EINVAL;
+
+	cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT_TX, direction);
+	val = bcast_rate_limit ? 1 : 0;
+	cpsw_ale_control_set(ale, port, ALE_PORT_BCAST_LIMIT, val);
+	val = mcast_rate_limit ? 1 : 0;
+	cpsw_ale_control_set(ale, port, ALE_PORT_MCAST_LIMIT, val);
+	writel((u32)ale_prescale, ale->params.ale_regs + ALE_PRESCALE);
+	cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT, 1);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpsw_ale_set_ratelimit);
+
 static void cpsw_ale_timer(struct timer_list *t)
 {
 	struct cpsw_ale *ale = from_timer(ale, t, timer);

+ 4 - 0
drivers/net/ethernet/ti/cpsw_ale.h

@@ -117,6 +117,10 @@ 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 cpsw_ale_set_ratelimit(struct cpsw_ale *ale, unsigned long freq, int port,
+			   unsigned int bcast_rate_limit,
+			   unsigned int mcast_rate_limit,
+			   bool direction);
 
 int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
 int cpsw_ale_control_set(struct cpsw_ale *ale, int port,

+ 4 - 0
include/uapi/linux/net_switch_config.h

@@ -23,6 +23,7 @@ enum {
 	CONFIG_SWITCH_SET_PORT_STATE,
 	CONFIG_SWITCH_GET_PORT_VLAN_CONFIG,
 	CONFIG_SWITCH_SET_PORT_VLAN_CONFIG,
+	CONFIG_SWITCH_RATELIMIT,
 };
 
 enum {
@@ -53,6 +54,9 @@ struct net_switch_config {
 	unsigned int	port_state;
 	unsigned int	prio;
 	bool		vlan_cfi;
+	unsigned int	bcast_rate_limit;
+	unsigned int	mcast_rate_limit;
+	bool		direction;
 
 	unsigned int ret_type;   /* Return  Success/Failure */
 };