diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c index 0e34116376..2b743dd53a 100644 --- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c @@ -2495,6 +2495,118 @@ static int rtldsa_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val return mdiobus_write_nested(priv->parent_bus, addr, regnum, val); } +static const struct flow_action_entry *rtldsa_rate_policy_extract(struct flow_cls_offload *cls) +{ + struct flow_rule *rule; + + /* only simple rules with a single action are supported */ + rule = flow_cls_offload_flow_rule(cls); + + if (!flow_action_basic_hw_stats_check(&cls->rule->action, + cls->common.extack)) + return NULL; + + if (!flow_offload_has_one_action(&rule->action)) + return NULL; + + return &rule->action.entries[0]; +} + +static bool rtldsa_port_rate_police_validate(const struct flow_action_entry *act) +{ + if (!act) + return false; + + /* only allow action which just limit rate with by dropping packets */ + if (act->id != FLOW_ACTION_POLICE) + return false; + + if (act->police.rate_pkt_ps > 0) + return false; + + if (act->police.exceed.act_id != FLOW_ACTION_DROP) + return false; + + if (act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) + return false; + + return true; +} + +static int rtldsa_cls_flower_add(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, + bool ingress) +{ + struct rtl838x_switch_priv *priv = ds->priv; + struct rtl838x_port *p = &priv->ports[port]; + const struct flow_action_entry *act; + int ret; + + if (!priv->r->port_rate_police_add) + return -EOPNOTSUPP; + + /* the single action must be a rate/bandwidth limiter */ + act = rtldsa_rate_policy_extract(cls); + + if (!rtldsa_port_rate_police_validate(act)) + return -EOPNOTSUPP; + + mutex_lock(&priv->reg_mutex); + + /* only allow one offloaded police for ingress/egress */ + if (ingress && p->rate_police_ingress) { + ret = -EOPNOTSUPP; + goto unlock; + } + + if (!ingress && p->rate_police_egress) { + ret = -EOPNOTSUPP; + goto unlock; + } + + ret = priv->r->port_rate_police_add(ds, port, act, ingress); + if (ret < 0) + goto unlock; + + if (ingress) + p->rate_police_ingress = true; + else + p->rate_police_egress = true; + +unlock: + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +static int rtldsa_cls_flower_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, + bool ingress) +{ + struct rtl838x_switch_priv *priv = ds->priv; + struct rtl838x_port *p = &priv->ports[port]; + int ret; + + if (!priv->r->port_rate_police_del) + return -EOPNOTSUPP; + + mutex_lock(&priv->reg_mutex); + + ret = priv->r->port_rate_police_del(ds, port, cls, ingress); + if (ret < 0) + goto unlock; + + if (ingress) + p->rate_police_ingress = false; + else + p->rate_police_egress = false; + +unlock: + mutex_unlock(&priv->reg_mutex); + + return ret; +} + const struct dsa_switch_ops rtl83xx_switch_ops = { .get_tag_protocol = rtl83xx_get_tag_protocol, .setup = rtl83xx_setup, @@ -2607,4 +2719,7 @@ const struct dsa_switch_ops rtl93xx_switch_ops = { .port_pre_bridge_flags = rtldsa_port_pre_bridge_flags, .port_bridge_flags = rtl83xx_port_bridge_flags, + + .cls_flower_add = rtldsa_cls_flower_add, + .cls_flower_del = rtldsa_cls_flower_del, }; diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h index 5dba9d5fef..1ab943a681 100644 --- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h +++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h @@ -406,6 +406,27 @@ #define RTL839X_SPCL_TRAP_SWITCH_MAC_CTRL (0x1068) #define RTL839X_SPCL_TRAP_SWITCH_IPV4_ADDR_CTRL (0x106C) #define RTL839X_SPCL_TRAP_CRC_CTRL (0x1070) + +#define RTL930X_BANDWIDTH_CTRL_EGRESS(port) (0x7660 + (port * 16)) +#define RTL930X_BANDWIDTH_CTRL_INGRESS(port) (0x8068 + (port * 4)) +#define RTL930X_BANDWIDTH_CTRL_MAX_BURST (64 * 1000) +#define RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_ON(port) \ + (0x80DC + (port * 8)) +#define RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_OFF(port) \ + (0x80E0 + (port * 8)) +#define RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_MAX \ + GENMASK(30, 0) + +#define RTL931X_BANDWIDTH_CTRL_EGRESS(port) (0x2164 + (port * 8)) +#define RTL931X_BANDWIDTH_CTRL_INGRESS(port) (0xe008 + (port * 8)) + +#define RTL93XX_BANDWIDTH_CTRL_RATE_MAX GENMASK(19, 0) +#define RTL93XX_BANDWIDTH_CTRL_ENABLE BIT(20) +#define RTL931X_BANDWIDTH_CTRL_MAX_BURST GENMASK(15, 0) + +#define RTL930X_INGRESS_FC_CTRL(port) (0x81CC + ((port / 29) * 4)) +#define RTL930X_INGRESS_FC_CTRL_EN(port) BIT(port % 29) + /* special port action controls */ /* values: * 0 = FORWARD (default) @@ -684,6 +705,8 @@ struct rtl838x_port { bool is10G:1; bool is2G5:1; bool isolated:1; + bool rate_police_egress:1; + bool rate_police_ingress:1; u64 pm; u16 pvid; bool eee_enabled; @@ -1074,6 +1097,10 @@ struct rtl838x_reg { int (*l2_port_new_sa_fwd)(int port); int (*set_ageing_time)(unsigned long msec); int (*get_mirror_config)(struct rtldsa_mirror_config *config, int group, int port); + int (*port_rate_police_add)(struct dsa_switch *ds, int port, + const struct flow_action_entry *act, bool ingress); + int (*port_rate_police_del)(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, + bool ingress); u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e); void (*write_l2_entry_using_hash)(u32 hash, u32 pos, struct rtl838x_l2_entry *e); u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e); diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl930x.c index d8eee64354..dddee9b7a9 100644 --- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl930x.c +++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl930x.c @@ -190,6 +190,68 @@ static int rtldsa_930x_get_mirror_config(struct rtldsa_mirror_config *config, return 0; } +static int rtldsa_930x_port_rate_police_add(struct dsa_switch *ds, int port, + const struct flow_action_entry *act, + bool ingress) +{ + u32 burst; + u64 rate; + u32 addr; + + /* rate has unit 16000 bit */ + rate = div_u64(act->police.rate_bytes_ps, 2000); + rate = min_t(u64, rate, RTL93XX_BANDWIDTH_CTRL_RATE_MAX); + rate |= RTL93XX_BANDWIDTH_CTRL_ENABLE; + + if (ingress) + addr = RTL930X_BANDWIDTH_CTRL_INGRESS(port); + else + addr = RTL930X_BANDWIDTH_CTRL_EGRESS(port); + + if (ingress) { + burst = min_t(u32, act->police.burst, RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_MAX); + + /* set burst high on/off the same to avoid TCP oscillation */ + sw_w32(burst, RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_ON(port)); + sw_w32(burst, RTL930X_BANDWIDTH_CTRL_INGRESS_BURST_HIGH_OFF(port)); + + /* Enable ingress bandwidth flow control to improve TCP throughput and avoid + * the drops behavior of the RTL930x ingress rate limiter which seem to not + * play well with any congestion control algorithm + */ + sw_w32_mask(0, RTL930X_INGRESS_FC_CTRL_EN(port), + RTL930X_INGRESS_FC_CTRL(port)); + } else { + burst = min_t(u32, act->police.burst, RTL930X_BANDWIDTH_CTRL_MAX_BURST); + + sw_w32(burst, addr + 4); + } + + sw_w32(rate, addr); + + return 0; +} + +static int rtldsa_930x_port_rate_police_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, + bool ingress) +{ + u32 addr; + + if (ingress) + addr = RTL930X_BANDWIDTH_CTRL_INGRESS(port); + else + addr = RTL930X_BANDWIDTH_CTRL_EGRESS(port); + + sw_w32_mask(RTL93XX_BANDWIDTH_CTRL_ENABLE, 0, addr); + + if (ingress) + sw_w32_mask(RTL930X_INGRESS_FC_CTRL_EN(port), 0, + RTL930X_INGRESS_FC_CTRL(port)); + + return 0; +} + inline static int rtl930x_trk_mbr_ctr(int group) { return RTL930X_TRK_MBR_CTRL + (group << 2); @@ -2495,6 +2557,8 @@ const struct rtl838x_reg rtl930x_reg = { .l2_port_new_salrn = rtl930x_l2_port_new_salrn, .l2_port_new_sa_fwd = rtl930x_l2_port_new_sa_fwd, .get_mirror_config = rtldsa_930x_get_mirror_config, + .port_rate_police_add = rtldsa_930x_port_rate_police_add, + .port_rate_police_del = rtldsa_930x_port_rate_police_del, .read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash, .write_l2_entry_using_hash = rtl930x_write_l2_entry_using_hash, .read_cam = rtl930x_read_cam, diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl931x.c index 90e450cf96..9f25f195e9 100644 --- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl931x.c +++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl931x.c @@ -304,6 +304,48 @@ static int rtldsa_931x_get_mirror_config(struct rtldsa_mirror_config *config, return 0; } +static int rtldsa_931x_port_rate_police_add(struct dsa_switch *ds, int port, + const struct flow_action_entry *act, + bool ingress) +{ + u32 burst; + u64 rate; + u32 addr; + + /* rate has unit 16000 bit */ + rate = div_u64(act->police.rate_bytes_ps, 2000); + rate = min_t(u64, rate, RTL93XX_BANDWIDTH_CTRL_RATE_MAX); + rate |= RTL93XX_BANDWIDTH_CTRL_ENABLE; + + burst = min_t(u32, act->police.burst, RTL931X_BANDWIDTH_CTRL_MAX_BURST); + + if (ingress) + addr = RTL931X_BANDWIDTH_CTRL_INGRESS(port); + else + addr = RTL931X_BANDWIDTH_CTRL_EGRESS(port); + + sw_w32(burst, addr + 4); + sw_w32(rate, addr); + + return 0; +} + +static int rtldsa_931x_port_rate_police_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, + bool ingress) +{ + u32 addr; + + if (ingress) + addr = RTL931X_BANDWIDTH_CTRL_INGRESS(port); + else + addr = RTL931X_BANDWIDTH_CTRL_EGRESS(port); + + sw_w32_mask(RTL93XX_BANDWIDTH_CTRL_ENABLE, 0, addr); + + return 0; +} + irqreturn_t rtl931x_switch_irq(int irq, void *dev_id) { struct dsa_switch *ds = dev_id; @@ -1618,6 +1660,8 @@ const struct rtl838x_reg rtl931x_reg = { .l2_port_new_salrn = rtl931x_l2_port_new_salrn, .l2_port_new_sa_fwd = rtl931x_l2_port_new_sa_fwd, .get_mirror_config = rtldsa_931x_get_mirror_config, + .port_rate_police_add = rtldsa_931x_port_rate_police_add, + .port_rate_police_del = rtldsa_931x_port_rate_police_del, .read_l2_entry_using_hash = rtl931x_read_l2_entry_using_hash, .write_l2_entry_using_hash = rtl931x_write_l2_entry_using_hash, .read_cam = rtl931x_read_cam,