Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: airoha: Add matchall filter offload support

Introduce tc matchall filter offload support in airoha_eth driver.
Matchall hw filter is used to implement hw rate policing via tc action
police:

$tc qdisc add dev eth0 handle ffff: ingress
$tc filter add dev eth0 parent ffff: matchall action police \
rate 100mbit burst 1000k drop

The current implementation supports just drop/accept as exceed/notexceed
actions. Moreover, rate and burst are the only supported configuration
parameters.

Reviewed-by: Davide Caratti <dcaratti@redhat.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20250415-airoha-hw-rx-ratelimit-v4-1-03458784fbc3@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Lorenzo Bianconi and committed by
Paolo Abeni
df8398fb 4e34a840

+286 -11
+271 -2
drivers/net/ethernet/airoha/airoha_eth.c
··· 527 527 /* disable IFC by default */ 528 528 airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); 529 529 530 + airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), 531 + FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) | 532 + FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) | 533 + FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) | 534 + FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) | 535 + FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) | 536 + FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) | 537 + FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) | 538 + FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1)); 539 + airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1), 540 + FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) | 541 + FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) | 542 + FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) | 543 + FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) | 544 + FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) | 545 + FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) | 546 + FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) | 547 + FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2)); 548 + 530 549 /* enable 1:N vlan action, init vlan table */ 531 550 airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); 532 551 ··· 1650 1631 1651 1632 if (port->id == 3) { 1652 1633 /* FIXME: handle XSI_PCE1_PORT */ 1653 - airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), 0x5500); 1654 1634 airoha_fe_rmw(eth, REG_FE_WAN_PORT, 1655 1635 WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, 1656 1636 FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT)); ··· 2127 2109 } 2128 2110 } 2129 2111 2112 + static int airoha_qdma_get_rl_param(struct airoha_qdma *qdma, int queue_id, 2113 + u32 addr, enum trtcm_param_type param, 2114 + u32 *val_low, u32 *val_high) 2115 + { 2116 + u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); 2117 + u32 val, config = FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | 2118 + FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | 2119 + FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); 2120 + 2121 + airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); 2122 + if (read_poll_timeout(airoha_qdma_rr, val, 2123 + val & RATE_LIMIT_PARAM_RW_DONE_MASK, 2124 + USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, qdma, 2125 + REG_TRTCM_CFG_PARAM(addr))) 2126 + return -ETIMEDOUT; 2127 + 2128 + *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); 2129 + if (val_high) 2130 + *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); 2131 + 2132 + return 0; 2133 + } 2134 + 2135 + static int airoha_qdma_set_rl_param(struct airoha_qdma *qdma, int queue_id, 2136 + u32 addr, enum trtcm_param_type param, 2137 + u32 val) 2138 + { 2139 + u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); 2140 + u32 config = RATE_LIMIT_PARAM_RW_MASK | 2141 + FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | 2142 + FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | 2143 + FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); 2144 + 2145 + airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); 2146 + airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); 2147 + 2148 + return read_poll_timeout(airoha_qdma_rr, val, 2149 + val & RATE_LIMIT_PARAM_RW_DONE_MASK, 2150 + USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, 2151 + qdma, REG_TRTCM_CFG_PARAM(addr)); 2152 + } 2153 + 2154 + static int airoha_qdma_set_rl_config(struct airoha_qdma *qdma, int queue_id, 2155 + u32 addr, bool enable, u32 enable_mask) 2156 + { 2157 + u32 val; 2158 + int err; 2159 + 2160 + err = airoha_qdma_get_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, 2161 + &val, NULL); 2162 + if (err) 2163 + return err; 2164 + 2165 + val = enable ? val | enable_mask : val & ~enable_mask; 2166 + 2167 + return airoha_qdma_set_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, 2168 + val); 2169 + } 2170 + 2171 + static int airoha_qdma_set_rl_token_bucket(struct airoha_qdma *qdma, 2172 + int queue_id, u32 rate_val, 2173 + u32 bucket_size) 2174 + { 2175 + u32 val, config, tick, unit, rate, rate_frac; 2176 + int err; 2177 + 2178 + err = airoha_qdma_get_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2179 + TRTCM_MISC_MODE, &config, NULL); 2180 + if (err) 2181 + return err; 2182 + 2183 + val = airoha_qdma_rr(qdma, REG_INGRESS_TRTCM_CFG); 2184 + tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); 2185 + if (config & TRTCM_TICK_SEL) 2186 + tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); 2187 + if (!tick) 2188 + return -EINVAL; 2189 + 2190 + unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; 2191 + if (!unit) 2192 + return -EINVAL; 2193 + 2194 + rate = rate_val / unit; 2195 + rate_frac = rate_val % unit; 2196 + rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; 2197 + rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | 2198 + FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); 2199 + 2200 + err = airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2201 + TRTCM_TOKEN_RATE_MODE, rate); 2202 + if (err) 2203 + return err; 2204 + 2205 + val = bucket_size; 2206 + if (!(config & TRTCM_PKT_MODE)) 2207 + val = max_t(u32, val, MIN_TOKEN_SIZE); 2208 + val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); 2209 + 2210 + return airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2211 + TRTCM_BUCKETSIZE_SHIFT_MODE, val); 2212 + } 2213 + 2214 + static int airoha_qdma_init_rl_config(struct airoha_qdma *qdma, int queue_id, 2215 + bool enable, enum trtcm_unit_type unit) 2216 + { 2217 + bool tick_sel = queue_id == 0 || queue_id == 2 || queue_id == 8; 2218 + enum trtcm_param mode = TRTCM_METER_MODE; 2219 + int err; 2220 + 2221 + mode |= unit == TRTCM_PACKET_UNIT ? TRTCM_PKT_MODE : 0; 2222 + err = airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2223 + enable, mode); 2224 + if (err) 2225 + return err; 2226 + 2227 + return airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, 2228 + tick_sel, TRTCM_TICK_SEL); 2229 + } 2230 + 2130 2231 static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, 2131 2232 u32 addr, enum trtcm_param_type param, 2132 2233 enum trtcm_mode_type mode, ··· 2410 2273 return 0; 2411 2274 } 2412 2275 2276 + static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port, 2277 + u32 rate, u32 bucket_size, 2278 + enum trtcm_unit_type unit_type) 2279 + { 2280 + struct airoha_qdma *qdma = port->qdma; 2281 + int i; 2282 + 2283 + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { 2284 + int err; 2285 + 2286 + if (!qdma->q_rx[i].ndesc) 2287 + continue; 2288 + 2289 + err = airoha_qdma_init_rl_config(qdma, i, !!rate, unit_type); 2290 + if (err) 2291 + return err; 2292 + 2293 + err = airoha_qdma_set_rl_token_bucket(qdma, i, rate, 2294 + bucket_size); 2295 + if (err) 2296 + return err; 2297 + } 2298 + 2299 + return 0; 2300 + } 2301 + 2302 + static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f) 2303 + { 2304 + const struct flow_action *actions = &f->rule->action; 2305 + const struct flow_action_entry *act; 2306 + 2307 + if (!flow_action_has_entries(actions)) { 2308 + NL_SET_ERR_MSG_MOD(f->common.extack, 2309 + "filter run with no actions"); 2310 + return -EINVAL; 2311 + } 2312 + 2313 + if (!flow_offload_has_one_action(actions)) { 2314 + NL_SET_ERR_MSG_MOD(f->common.extack, 2315 + "only once action per filter is supported"); 2316 + return -EOPNOTSUPP; 2317 + } 2318 + 2319 + act = &actions->entries[0]; 2320 + if (act->id != FLOW_ACTION_POLICE) { 2321 + NL_SET_ERR_MSG_MOD(f->common.extack, "unsupported action"); 2322 + return -EOPNOTSUPP; 2323 + } 2324 + 2325 + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { 2326 + NL_SET_ERR_MSG_MOD(f->common.extack, 2327 + "invalid exceed action id"); 2328 + return -EOPNOTSUPP; 2329 + } 2330 + 2331 + if (act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { 2332 + NL_SET_ERR_MSG_MOD(f->common.extack, 2333 + "invalid notexceed action id"); 2334 + return -EOPNOTSUPP; 2335 + } 2336 + 2337 + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && 2338 + !flow_action_is_last_entry(actions, act)) { 2339 + NL_SET_ERR_MSG_MOD(f->common.extack, 2340 + "action accept must be last"); 2341 + return -EOPNOTSUPP; 2342 + } 2343 + 2344 + if (act->police.peakrate_bytes_ps || act->police.avrate || 2345 + act->police.overhead || act->police.mtu) { 2346 + NL_SET_ERR_MSG_MOD(f->common.extack, 2347 + "peakrate/avrate/overhead/mtu unsupported"); 2348 + return -EOPNOTSUPP; 2349 + } 2350 + 2351 + return 0; 2352 + } 2353 + 2354 + static int airoha_dev_tc_matchall(struct net_device *dev, 2355 + struct tc_cls_matchall_offload *f) 2356 + { 2357 + enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; 2358 + struct airoha_gdm_port *port = netdev_priv(dev); 2359 + u32 rate = 0, bucket_size = 0; 2360 + 2361 + switch (f->command) { 2362 + case TC_CLSMATCHALL_REPLACE: { 2363 + const struct flow_action_entry *act; 2364 + int err; 2365 + 2366 + err = airoha_tc_matchall_act_validate(f); 2367 + if (err) 2368 + return err; 2369 + 2370 + act = &f->rule->action.entries[0]; 2371 + if (act->police.rate_pkt_ps) { 2372 + rate = act->police.rate_pkt_ps; 2373 + bucket_size = act->police.burst_pkt; 2374 + unit_type = TRTCM_PACKET_UNIT; 2375 + } else { 2376 + rate = div_u64(act->police.rate_bytes_ps, 1000); 2377 + rate = rate << 3; /* Kbps */ 2378 + bucket_size = act->police.burst; 2379 + } 2380 + fallthrough; 2381 + } 2382 + case TC_CLSMATCHALL_DESTROY: 2383 + return airoha_qdma_set_rx_meter(port, rate, bucket_size, 2384 + unit_type); 2385 + default: 2386 + return -EOPNOTSUPP; 2387 + } 2388 + } 2389 + 2390 + static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type, 2391 + void *type_data, void *cb_priv) 2392 + { 2393 + struct net_device *dev = cb_priv; 2394 + 2395 + if (!tc_can_offload(dev)) 2396 + return -EOPNOTSUPP; 2397 + 2398 + switch (type) { 2399 + case TC_SETUP_CLSFLOWER: 2400 + return airoha_ppe_setup_tc_block_cb(dev, type_data); 2401 + case TC_SETUP_CLSMATCHALL: 2402 + return airoha_dev_tc_matchall(dev, type_data); 2403 + default: 2404 + return -EOPNOTSUPP; 2405 + } 2406 + } 2407 + 2413 2408 static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port, 2414 2409 struct flow_block_offload *f) 2415 2410 { 2416 - flow_setup_cb_t *cb = airoha_ppe_setup_tc_block_cb; 2411 + flow_setup_cb_t *cb = airoha_dev_setup_tc_block_cb; 2417 2412 static LIST_HEAD(block_cb_list); 2418 2413 struct flow_block_cb *block_cb; 2419 2414
+6 -2
drivers/net/ethernet/airoha/airoha_eth.h
··· 127 127 TC_SCH_WRR2, 128 128 }; 129 129 130 + enum trtcm_unit_type { 131 + TRTCM_BYTE_UNIT, 132 + TRTCM_PACKET_UNIT, 133 + }; 134 + 130 135 enum trtcm_param_type { 131 136 TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ 132 137 TRTCM_TOKEN_RATE_MODE, ··· 559 554 560 555 void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, 561 556 u16 hash); 562 - int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, 563 - void *cb_priv); 557 + int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); 564 558 int airoha_ppe_init(struct airoha_eth *eth); 565 559 void airoha_ppe_deinit(struct airoha_eth *eth); 566 560 struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
+2 -7
drivers/net/ethernet/airoha/airoha_ppe.c
··· 967 967 return err; 968 968 } 969 969 970 - int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, 971 - void *cb_priv) 970 + int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) 972 971 { 973 - struct flow_cls_offload *cls = type_data; 974 - struct net_device *dev = cb_priv; 975 972 struct airoha_gdm_port *port = netdev_priv(dev); 973 + struct flow_cls_offload *cls = type_data; 976 974 struct airoha_eth *eth = port->qdma->eth; 977 975 int err = 0; 978 - 979 - if (!tc_can_offload(dev) || type != TC_SETUP_CLSFLOWER) 980 - return -EOPNOTSUPP; 981 976 982 977 mutex_lock(&flow_offload_mutex); 983 978
+7
drivers/net/ethernet/airoha/airoha_regs.h
··· 283 283 #define PPE_HASH_SEED 0x12345678 284 284 285 285 #define REG_PPE_DFT_CPORT0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248) 286 + #define DFT_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2)) 286 287 287 288 #define REG_PPE_DFT_CPORT1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x24c) 288 289 ··· 691 690 #define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4) 692 691 #define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) 693 692 #define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) 693 + 694 + #define RATE_LIMIT_PARAM_RW_MASK BIT(31) 695 + #define RATE_LIMIT_PARAM_RW_DONE_MASK BIT(30) 696 + #define RATE_LIMIT_PARAM_TYPE_MASK GENMASK(29, 28) 697 + #define RATE_LIMIT_METER_GROUP_MASK GENMASK(27, 26) 698 + #define RATE_LIMIT_PARAM_INDEX_MASK GENMASK(23, 16) 694 699 695 700 #define REG_TXWRR_MODE_CFG 0x1020 696 701 #define TWRR_WEIGHT_SCALE_MASK BIT(31)