1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include "lan966x_main.h" 4 #include "vcap_api.h" 5 #include "vcap_api_client.h" 6 #include "vcap_tc.h" 7 8 static bool lan966x_tc_is_known_etype(u16 etype) 9 { 10 switch (etype) { 11 case ETH_P_ALL: 12 case ETH_P_ARP: 13 case ETH_P_IP: 14 case ETH_P_IPV6: 15 return true; 16 } 17 18 return false; 19 } 20 21 static int 22 lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st) 23 { 24 struct flow_match_control match; 25 int err = 0; 26 27 flow_rule_match_control(st->frule, &match); 28 if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { 29 if (match.key->flags & FLOW_DIS_IS_FRAGMENT) 30 err = vcap_rule_add_key_bit(st->vrule, 31 VCAP_KF_L3_FRAGMENT, 32 VCAP_BIT_1); 33 else 34 err = vcap_rule_add_key_bit(st->vrule, 35 VCAP_KF_L3_FRAGMENT, 36 VCAP_BIT_0); 37 if (err) 38 goto out; 39 } 40 41 if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { 42 if (match.key->flags & FLOW_DIS_FIRST_FRAG) 43 err = vcap_rule_add_key_bit(st->vrule, 44 VCAP_KF_L3_FRAG_OFS_GT0, 45 VCAP_BIT_0); 46 else 47 err = vcap_rule_add_key_bit(st->vrule, 48 VCAP_KF_L3_FRAG_OFS_GT0, 49 VCAP_BIT_1); 50 if (err) 51 goto out; 52 } 53 54 st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); 55 56 return err; 57 58 out: 59 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); 60 return err; 61 } 62 63 static int 64 lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) 65 { 66 struct flow_match_basic match; 67 int err = 0; 68 69 flow_rule_match_basic(st->frule, &match); 70 if (match.mask->n_proto) { 71 st->l3_proto = be16_to_cpu(match.key->n_proto); 72 if (!lan966x_tc_is_known_etype(st->l3_proto)) { 73 err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, 74 st->l3_proto, ~0); 75 if (err) 76 goto out; 77 } else if (st->l3_proto == ETH_P_IP) { 78 err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, 79 VCAP_BIT_1); 80 if (err) 81 goto out; 82 } 83 } 84 if (match.mask->ip_proto) { 85 st->l4_proto = match.key->ip_proto; 86 87 if (st->l4_proto == IPPROTO_TCP) { 88 err = vcap_rule_add_key_bit(st->vrule, 89 VCAP_KF_TCP_IS, 90 VCAP_BIT_1); 91 if (err) 92 goto out; 93 } else if (st->l4_proto == IPPROTO_UDP) { 94 err = vcap_rule_add_key_bit(st->vrule, 95 VCAP_KF_TCP_IS, 96 VCAP_BIT_0); 97 if (err) 98 goto out; 99 } else { 100 err = vcap_rule_add_key_u32(st->vrule, 101 VCAP_KF_L3_IP_PROTO, 102 st->l4_proto, ~0); 103 if (err) 104 goto out; 105 } 106 } 107 108 st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); 109 return err; 110 out: 111 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error"); 112 return err; 113 } 114 115 static int 116 lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st) 117 { 118 return vcap_tc_flower_handler_vlan_usage(st, 119 VCAP_KF_8021Q_VID_CLS, 120 VCAP_KF_8021Q_PCP_CLS); 121 } 122 123 static int 124 (*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = { 125 [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage, 126 [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage, 127 [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage, 128 [FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage, 129 [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage, 130 [FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage, 131 [FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage, 132 [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage, 133 [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage, 134 [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage, 135 }; 136 137 static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, 138 struct vcap_admin *admin, 139 struct vcap_rule *vrule, 140 u16 *l3_proto) 141 { 142 struct vcap_tc_flower_parse_usage state = { 143 .fco = f, 144 .vrule = vrule, 145 .l3_proto = ETH_P_ALL, 146 }; 147 int err = 0; 148 149 state.frule = flow_cls_offload_flow_rule(f); 150 for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) { 151 if (!flow_rule_match_key(state.frule, i) || 152 !lan966x_tc_flower_handlers_usage[i]) 153 continue; 154 155 err = lan966x_tc_flower_handlers_usage[i](&state); 156 if (err) 157 return err; 158 } 159 160 if (l3_proto) 161 *l3_proto = state.l3_proto; 162 163 return err; 164 } 165 166 static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, 167 struct net_device *dev, 168 struct flow_cls_offload *fco, 169 bool ingress) 170 { 171 struct flow_rule *rule = flow_cls_offload_flow_rule(fco); 172 struct flow_action_entry *actent, *last_actent = NULL; 173 struct flow_action *act = &rule->action; 174 u64 action_mask = 0; 175 int idx; 176 177 if (!flow_action_has_entries(act)) { 178 NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); 179 return -EINVAL; 180 } 181 182 if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) 183 return -EOPNOTSUPP; 184 185 flow_action_for_each(idx, actent, act) { 186 if (action_mask & BIT(actent->id)) { 187 NL_SET_ERR_MSG_MOD(fco->common.extack, 188 "More actions of the same type"); 189 return -EINVAL; 190 } 191 action_mask |= BIT(actent->id); 192 last_actent = actent; /* Save last action for later check */ 193 } 194 195 /* Check that last action is a goto 196 * The last chain/lookup does not need to have goto action 197 */ 198 if (last_actent->id == FLOW_ACTION_GOTO) { 199 /* Check if the destination chain is in one of the VCAPs */ 200 if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, 201 last_actent->chain_index)) { 202 NL_SET_ERR_MSG_MOD(fco->common.extack, 203 "Invalid goto chain"); 204 return -EINVAL; 205 } 206 } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index, 207 ingress)) { 208 NL_SET_ERR_MSG_MOD(fco->common.extack, 209 "Last action must be 'goto'"); 210 return -EINVAL; 211 } 212 213 /* Catch unsupported combinations of actions */ 214 if (action_mask & BIT(FLOW_ACTION_TRAP) && 215 action_mask & BIT(FLOW_ACTION_ACCEPT)) { 216 NL_SET_ERR_MSG_MOD(fco->common.extack, 217 "Cannot combine pass and trap action"); 218 return -EOPNOTSUPP; 219 } 220 221 return 0; 222 } 223 224 static int lan966x_tc_flower_add(struct lan966x_port *port, 225 struct flow_cls_offload *f, 226 struct vcap_admin *admin, 227 bool ingress) 228 { 229 struct flow_action_entry *act; 230 u16 l3_proto = ETH_P_ALL; 231 struct flow_rule *frule; 232 struct vcap_rule *vrule; 233 int err, idx; 234 235 err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, 236 port->dev, f, ingress); 237 if (err) 238 return err; 239 240 vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev, 241 f->common.chain_index, VCAP_USER_TC, 242 f->common.prio, 0); 243 if (IS_ERR(vrule)) 244 return PTR_ERR(vrule); 245 246 vrule->cookie = f->cookie; 247 err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto); 248 if (err) 249 goto out; 250 251 frule = flow_cls_offload_flow_rule(f); 252 253 flow_action_for_each(idx, act, &frule->action) { 254 switch (act->id) { 255 case FLOW_ACTION_TRAP: 256 err = vcap_rule_add_action_bit(vrule, 257 VCAP_AF_CPU_COPY_ENA, 258 VCAP_BIT_1); 259 err |= vcap_rule_add_action_u32(vrule, 260 VCAP_AF_CPU_QUEUE_NUM, 261 0); 262 err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, 263 LAN966X_PMM_REPLACE); 264 if (err) 265 goto out; 266 267 break; 268 case FLOW_ACTION_GOTO: 269 break; 270 default: 271 NL_SET_ERR_MSG_MOD(f->common.extack, 272 "Unsupported TC action"); 273 err = -EOPNOTSUPP; 274 goto out; 275 } 276 } 277 278 err = vcap_val_rule(vrule, l3_proto); 279 if (err) { 280 vcap_set_tc_exterr(f, vrule); 281 goto out; 282 } 283 284 err = vcap_add_rule(vrule); 285 if (err) 286 NL_SET_ERR_MSG_MOD(f->common.extack, 287 "Could not add the filter"); 288 out: 289 vcap_free_rule(vrule); 290 return err; 291 } 292 293 static int lan966x_tc_flower_del(struct lan966x_port *port, 294 struct flow_cls_offload *f, 295 struct vcap_admin *admin) 296 { 297 struct vcap_control *vctrl; 298 int err = -ENOENT, rule_id; 299 300 vctrl = port->lan966x->vcap_ctrl; 301 while (true) { 302 rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie); 303 if (rule_id <= 0) 304 break; 305 306 err = vcap_del_rule(vctrl, port->dev, rule_id); 307 if (err) { 308 NL_SET_ERR_MSG_MOD(f->common.extack, 309 "Cannot delete rule"); 310 break; 311 } 312 } 313 314 return err; 315 } 316 317 static int lan966x_tc_flower_stats(struct lan966x_port *port, 318 struct flow_cls_offload *f, 319 struct vcap_admin *admin) 320 { 321 struct vcap_counter count = {}; 322 int err; 323 324 err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl, 325 &count, f->cookie); 326 if (err) 327 return err; 328 329 flow_stats_update(&f->stats, 0x0, count.value, 0, 0, 330 FLOW_ACTION_HW_STATS_IMMEDIATE); 331 332 return err; 333 } 334 335 int lan966x_tc_flower(struct lan966x_port *port, 336 struct flow_cls_offload *f, 337 bool ingress) 338 { 339 struct vcap_admin *admin; 340 341 admin = vcap_find_admin(port->lan966x->vcap_ctrl, 342 f->common.chain_index); 343 if (!admin) { 344 NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain"); 345 return -EINVAL; 346 } 347 348 switch (f->command) { 349 case FLOW_CLS_REPLACE: 350 return lan966x_tc_flower_add(port, f, admin, ingress); 351 case FLOW_CLS_DESTROY: 352 return lan966x_tc_flower_del(port, f, admin); 353 case FLOW_CLS_STATS: 354 return lan966x_tc_flower_stats(port, f, admin); 355 default: 356 return -EOPNOTSUPP; 357 } 358 359 return 0; 360 } 361