1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip VCAP API 3 * 4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include <net/tcp.h> 8 9 #include "sparx5_tc.h" 10 #include "vcap_api.h" 11 #include "vcap_api_client.h" 12 #include "sparx5_main.h" 13 #include "sparx5_vcap_impl.h" 14 15 struct sparx5_tc_flower_parse_usage { 16 struct flow_cls_offload *fco; 17 struct flow_rule *frule; 18 struct vcap_rule *vrule; 19 unsigned int used_keys; 20 }; 21 22 static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st) 23 { 24 enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; 25 enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; 26 struct flow_match_eth_addrs match; 27 struct vcap_u48_key smac, dmac; 28 int err = 0; 29 30 flow_rule_match_eth_addrs(st->frule, &match); 31 32 if (!is_zero_ether_addr(match.mask->src)) { 33 vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); 34 vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); 35 err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); 36 if (err) 37 goto out; 38 } 39 40 if (!is_zero_ether_addr(match.mask->dst)) { 41 vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); 42 vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); 43 err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); 44 if (err) 45 goto out; 46 } 47 48 st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); 49 50 return err; 51 52 out: 53 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error"); 54 return err; 55 } 56 57 static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = { 58 /* More dissector handlers will be added here later */ 59 [FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage, 60 }; 61 62 static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, 63 struct vcap_admin *admin, 64 struct vcap_rule *vrule) 65 { 66 struct sparx5_tc_flower_parse_usage state = { 67 .fco = fco, 68 .vrule = vrule, 69 }; 70 int idx, err = 0; 71 72 state.frule = flow_cls_offload_flow_rule(fco); 73 for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { 74 if (!flow_rule_match_key(state.frule, idx)) 75 continue; 76 if (!sparx5_tc_flower_usage_handlers[idx]) 77 continue; 78 err = sparx5_tc_flower_usage_handlers[idx](&state); 79 if (err) 80 return err; 81 } 82 return err; 83 } 84 85 static int sparx5_tc_flower_replace(struct net_device *ndev, 86 struct flow_cls_offload *fco, 87 struct vcap_admin *admin) 88 { 89 struct sparx5_port *port = netdev_priv(ndev); 90 struct flow_action_entry *act; 91 struct vcap_control *vctrl; 92 struct flow_rule *frule; 93 struct vcap_rule *vrule; 94 int err, idx; 95 96 frule = flow_cls_offload_flow_rule(fco); 97 if (!flow_action_has_entries(&frule->action)) { 98 NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); 99 return -EINVAL; 100 } 101 102 if (!flow_action_basic_hw_stats_check(&frule->action, fco->common.extack)) 103 return -EOPNOTSUPP; 104 105 vctrl = port->sparx5->vcap_ctrl; 106 vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC, 107 fco->common.prio, 0); 108 if (IS_ERR(vrule)) 109 return PTR_ERR(vrule); 110 111 vrule->cookie = fco->cookie; 112 sparx5_tc_use_dissectors(fco, admin, vrule); 113 flow_action_for_each(idx, act, &frule->action) { 114 switch (act->id) { 115 case FLOW_ACTION_TRAP: 116 err = vcap_rule_add_action_bit(vrule, 117 VCAP_AF_CPU_COPY_ENA, 118 VCAP_BIT_1); 119 if (err) 120 goto out; 121 err = vcap_rule_add_action_u32(vrule, 122 VCAP_AF_CPU_QUEUE_NUM, 0); 123 if (err) 124 goto out; 125 err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, 126 SPX5_PMM_REPLACE_ALL); 127 if (err) 128 goto out; 129 /* For now the actionset is hardcoded */ 130 err = vcap_set_rule_set_actionset(vrule, 131 VCAP_AFS_BASE_TYPE); 132 if (err) 133 goto out; 134 break; 135 case FLOW_ACTION_ACCEPT: 136 /* For now the actionset is hardcoded */ 137 err = vcap_set_rule_set_actionset(vrule, 138 VCAP_AFS_BASE_TYPE); 139 if (err) 140 goto out; 141 break; 142 default: 143 NL_SET_ERR_MSG_MOD(fco->common.extack, 144 "Unsupported TC action"); 145 err = -EOPNOTSUPP; 146 goto out; 147 } 148 } 149 /* For now the keyset is hardcoded */ 150 err = vcap_set_rule_set_keyset(vrule, VCAP_KFS_MAC_ETYPE); 151 if (err) { 152 NL_SET_ERR_MSG_MOD(fco->common.extack, 153 "No matching port keyset for filter protocol and keys"); 154 goto out; 155 } 156 err = vcap_val_rule(vrule, ETH_P_ALL); 157 if (err) { 158 vcap_set_tc_exterr(fco, vrule); 159 goto out; 160 } 161 err = vcap_add_rule(vrule); 162 if (err) 163 NL_SET_ERR_MSG_MOD(fco->common.extack, 164 "Could not add the filter"); 165 out: 166 vcap_free_rule(vrule); 167 return err; 168 } 169 170 static int sparx5_tc_flower_destroy(struct net_device *ndev, 171 struct flow_cls_offload *fco, 172 struct vcap_admin *admin) 173 { 174 struct sparx5_port *port = netdev_priv(ndev); 175 struct vcap_control *vctrl; 176 int err = -ENOENT, rule_id; 177 178 vctrl = port->sparx5->vcap_ctrl; 179 while (true) { 180 rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie); 181 if (rule_id <= 0) 182 break; 183 err = vcap_del_rule(vctrl, ndev, rule_id); 184 if (err) { 185 pr_err("%s:%d: could not delete rule %d\n", 186 __func__, __LINE__, rule_id); 187 break; 188 } 189 } 190 return err; 191 } 192 193 int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, 194 bool ingress) 195 { 196 struct sparx5_port *port = netdev_priv(ndev); 197 struct vcap_control *vctrl; 198 struct vcap_admin *admin; 199 int err = -EINVAL; 200 201 /* Get vcap instance from the chain id */ 202 vctrl = port->sparx5->vcap_ctrl; 203 admin = vcap_find_admin(vctrl, fco->common.chain_index); 204 if (!admin) { 205 NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain"); 206 return err; 207 } 208 209 switch (fco->command) { 210 case FLOW_CLS_REPLACE: 211 return sparx5_tc_flower_replace(ndev, fco, admin); 212 case FLOW_CLS_DESTROY: 213 return sparx5_tc_flower_destroy(ndev, fco, admin); 214 default: 215 return -EOPNOTSUPP; 216 } 217 } 218