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 7 struct lan966x_tc_flower_parse_usage { 8 struct flow_cls_offload *f; 9 struct flow_rule *frule; 10 struct vcap_rule *vrule; 11 unsigned int used_keys; 12 u16 l3_proto; 13 }; 14 15 static int lan966x_tc_flower_handler_ethaddr_usage(struct lan966x_tc_flower_parse_usage *st) 16 { 17 enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; 18 enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; 19 struct flow_match_eth_addrs match; 20 struct vcap_u48_key smac, dmac; 21 int err = 0; 22 23 flow_rule_match_eth_addrs(st->frule, &match); 24 25 if (!is_zero_ether_addr(match.mask->src)) { 26 vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); 27 vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); 28 err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); 29 if (err) 30 goto out; 31 } 32 33 if (!is_zero_ether_addr(match.mask->dst)) { 34 vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); 35 vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); 36 err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); 37 if (err) 38 goto out; 39 } 40 41 st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); 42 43 return err; 44 45 out: 46 NL_SET_ERR_MSG_MOD(st->f->common.extack, "eth_addr parse error"); 47 return err; 48 } 49 50 static int 51 (*lan966x_tc_flower_handlers_usage[])(struct lan966x_tc_flower_parse_usage *st) = { 52 [FLOW_DISSECTOR_KEY_ETH_ADDRS] = lan966x_tc_flower_handler_ethaddr_usage, 53 }; 54 55 static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, 56 struct vcap_admin *admin, 57 struct vcap_rule *vrule, 58 u16 *l3_proto) 59 { 60 struct lan966x_tc_flower_parse_usage state = { 61 .f = f, 62 .vrule = vrule, 63 .l3_proto = ETH_P_ALL, 64 }; 65 int err = 0; 66 67 state.frule = flow_cls_offload_flow_rule(f); 68 for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) { 69 if (!flow_rule_match_key(state.frule, i) || 70 !lan966x_tc_flower_handlers_usage[i]) 71 continue; 72 73 err = lan966x_tc_flower_handlers_usage[i](&state); 74 if (err) 75 return err; 76 } 77 78 if (l3_proto) 79 *l3_proto = state.l3_proto; 80 81 return err; 82 } 83 84 static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, 85 struct flow_cls_offload *fco, 86 struct vcap_admin *admin) 87 { 88 struct flow_rule *rule = flow_cls_offload_flow_rule(fco); 89 struct flow_action_entry *actent, *last_actent = NULL; 90 struct flow_action *act = &rule->action; 91 u64 action_mask = 0; 92 int idx; 93 94 if (!flow_action_has_entries(act)) { 95 NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); 96 return -EINVAL; 97 } 98 99 if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) 100 return -EOPNOTSUPP; 101 102 flow_action_for_each(idx, actent, act) { 103 if (action_mask & BIT(actent->id)) { 104 NL_SET_ERR_MSG_MOD(fco->common.extack, 105 "More actions of the same type"); 106 return -EINVAL; 107 } 108 action_mask |= BIT(actent->id); 109 last_actent = actent; /* Save last action for later check */ 110 } 111 112 /* Check that last action is a goto */ 113 if (last_actent->id != FLOW_ACTION_GOTO) { 114 NL_SET_ERR_MSG_MOD(fco->common.extack, 115 "Last action must be 'goto'"); 116 return -EINVAL; 117 } 118 119 /* Check if the goto chain is in the next lookup */ 120 if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, 121 last_actent->chain_index)) { 122 NL_SET_ERR_MSG_MOD(fco->common.extack, 123 "Invalid goto chain"); 124 return -EINVAL; 125 } 126 127 /* Catch unsupported combinations of actions */ 128 if (action_mask & BIT(FLOW_ACTION_TRAP) && 129 action_mask & BIT(FLOW_ACTION_ACCEPT)) { 130 NL_SET_ERR_MSG_MOD(fco->common.extack, 131 "Cannot combine pass and trap action"); 132 return -EOPNOTSUPP; 133 } 134 135 return 0; 136 } 137 138 static int lan966x_tc_flower_add(struct lan966x_port *port, 139 struct flow_cls_offload *f, 140 struct vcap_admin *admin) 141 { 142 struct flow_action_entry *act; 143 u16 l3_proto = ETH_P_ALL; 144 struct flow_rule *frule; 145 struct vcap_rule *vrule; 146 int err, idx; 147 148 err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, f, 149 admin); 150 if (err) 151 return err; 152 153 vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev, 154 f->common.chain_index, VCAP_USER_TC, 155 f->common.prio, 0); 156 if (IS_ERR(vrule)) 157 return PTR_ERR(vrule); 158 159 vrule->cookie = f->cookie; 160 err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto); 161 if (err) 162 goto out; 163 164 frule = flow_cls_offload_flow_rule(f); 165 166 flow_action_for_each(idx, act, &frule->action) { 167 switch (act->id) { 168 case FLOW_ACTION_TRAP: 169 err = vcap_rule_add_action_bit(vrule, 170 VCAP_AF_CPU_COPY_ENA, 171 VCAP_BIT_1); 172 err |= vcap_rule_add_action_u32(vrule, 173 VCAP_AF_CPU_QUEUE_NUM, 174 0); 175 err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, 176 LAN966X_PMM_REPLACE); 177 err |= vcap_set_rule_set_actionset(vrule, 178 VCAP_AFS_BASE_TYPE); 179 if (err) 180 goto out; 181 182 break; 183 case FLOW_ACTION_GOTO: 184 break; 185 default: 186 NL_SET_ERR_MSG_MOD(f->common.extack, 187 "Unsupported TC action"); 188 err = -EOPNOTSUPP; 189 goto out; 190 } 191 } 192 193 err = vcap_val_rule(vrule, l3_proto); 194 if (err) { 195 vcap_set_tc_exterr(f, vrule); 196 goto out; 197 } 198 199 err = vcap_add_rule(vrule); 200 if (err) 201 NL_SET_ERR_MSG_MOD(f->common.extack, 202 "Could not add the filter"); 203 out: 204 vcap_free_rule(vrule); 205 return err; 206 } 207 208 static int lan966x_tc_flower_del(struct lan966x_port *port, 209 struct flow_cls_offload *f, 210 struct vcap_admin *admin) 211 { 212 struct vcap_control *vctrl; 213 int err = -ENOENT, rule_id; 214 215 vctrl = port->lan966x->vcap_ctrl; 216 while (true) { 217 rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie); 218 if (rule_id <= 0) 219 break; 220 221 err = vcap_del_rule(vctrl, port->dev, rule_id); 222 if (err) { 223 NL_SET_ERR_MSG_MOD(f->common.extack, 224 "Cannot delete rule"); 225 break; 226 } 227 } 228 229 return err; 230 } 231 232 int lan966x_tc_flower(struct lan966x_port *port, 233 struct flow_cls_offload *f) 234 { 235 struct vcap_admin *admin; 236 237 admin = vcap_find_admin(port->lan966x->vcap_ctrl, 238 f->common.chain_index); 239 if (!admin) { 240 NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain"); 241 return -EINVAL; 242 } 243 244 switch (f->command) { 245 case FLOW_CLS_REPLACE: 246 return lan966x_tc_flower_add(port, f, admin); 247 case FLOW_CLS_DESTROY: 248 return lan966x_tc_flower_del(port, f, admin); 249 default: 250 return -EOPNOTSUPP; 251 } 252 253 return 0; 254 } 255