1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2018 Netronome Systems, Inc. */ 3 4 #include <linux/bitfield.h> 5 #include <net/pkt_cls.h> 6 7 #include "../nfpcore/nfp_cpp.h" 8 #include "../nfp_app.h" 9 #include "../nfp_net_repr.h" 10 #include "main.h" 11 12 struct nfp_abm_u32_match { 13 u32 handle; 14 u32 band; 15 u8 mask; 16 u8 val; 17 struct list_head list; 18 }; 19 20 static bool 21 nfp_abm_u32_check_knode(struct nfp_abm *abm, struct tc_cls_u32_knode *knode, 22 __be16 proto, struct netlink_ext_ack *extack) 23 { 24 struct tc_u32_key *k; 25 unsigned int tos_off; 26 27 if (knode->exts && tcf_exts_has_actions(knode->exts)) { 28 NL_SET_ERR_MSG_MOD(extack, "action offload not supported"); 29 return false; 30 } 31 if (knode->link_handle) { 32 NL_SET_ERR_MSG_MOD(extack, "linking not supported"); 33 return false; 34 } 35 if (knode->sel->flags != TC_U32_TERMINAL) { 36 NL_SET_ERR_MSG_MOD(extack, 37 "flags must be equal to TC_U32_TERMINAL"); 38 return false; 39 } 40 if (knode->sel->off || knode->sel->offshift || knode->sel->offmask || 41 knode->sel->offoff || knode->fshift) { 42 NL_SET_ERR_MSG_MOD(extack, "variable offsetting not supported"); 43 return false; 44 } 45 if (knode->sel->hoff || knode->sel->hmask) { 46 NL_SET_ERR_MSG_MOD(extack, "hashing not supported"); 47 return false; 48 } 49 if (knode->val || knode->mask) { 50 NL_SET_ERR_MSG_MOD(extack, "matching on mark not supported"); 51 return false; 52 } 53 if (knode->res && knode->res->class) { 54 NL_SET_ERR_MSG_MOD(extack, "setting non-0 class not supported"); 55 return false; 56 } 57 if (knode->res && knode->res->classid >= abm->num_bands) { 58 NL_SET_ERR_MSG_MOD(extack, 59 "classid higher than number of bands"); 60 return false; 61 } 62 if (knode->sel->nkeys != 1) { 63 NL_SET_ERR_MSG_MOD(extack, "exactly one key required"); 64 return false; 65 } 66 67 switch (proto) { 68 case htons(ETH_P_IP): 69 tos_off = 16; 70 break; 71 case htons(ETH_P_IPV6): 72 tos_off = 20; 73 break; 74 default: 75 NL_SET_ERR_MSG_MOD(extack, "only IP and IPv6 supported as filter protocol"); 76 return false; 77 } 78 79 k = &knode->sel->keys[0]; 80 if (k->offmask) { 81 NL_SET_ERR_MSG_MOD(extack, "offset mask - variable offsetting not supported"); 82 return false; 83 } 84 if (k->off) { 85 NL_SET_ERR_MSG_MOD(extack, "only DSCP fields can be matched"); 86 return false; 87 } 88 if (k->val & ~k->mask) { 89 NL_SET_ERR_MSG_MOD(extack, "mask does not cover the key"); 90 return false; 91 } 92 if (be32_to_cpu(k->mask) >> tos_off & ~abm->dscp_mask) { 93 NL_SET_ERR_MSG_MOD(extack, "only high DSCP class selector bits can be used"); 94 nfp_err(abm->app->cpp, 95 "u32 offload: requested mask %x FW can support only %x\n", 96 be32_to_cpu(k->mask) >> tos_off, abm->dscp_mask); 97 return false; 98 } 99 100 return true; 101 } 102 103 /* This filter list -> map conversion is O(n * m), we expect single digit or 104 * low double digit number of prios and likewise for the filters. Also u32 105 * doesn't report stats, so it's really only setup time cost. 106 */ 107 static unsigned int 108 nfp_abm_find_band_for_prio(struct nfp_abm_link *alink, unsigned int prio) 109 { 110 struct nfp_abm_u32_match *iter; 111 112 list_for_each_entry(iter, &alink->dscp_map, list) 113 if ((prio & iter->mask) == iter->val) 114 return iter->band; 115 116 return alink->def_band; 117 } 118 119 static int nfp_abm_update_band_map(struct nfp_abm_link *alink) 120 { 121 unsigned int i, bits_per_prio, prios_per_word, base_shift; 122 struct nfp_abm *abm = alink->abm; 123 u32 field_mask; 124 125 alink->has_prio = !list_empty(&alink->dscp_map); 126 127 bits_per_prio = roundup_pow_of_two(order_base_2(abm->num_bands)); 128 field_mask = (1 << bits_per_prio) - 1; 129 prios_per_word = sizeof(u32) * BITS_PER_BYTE / bits_per_prio; 130 131 /* FW mask applies from top bits */ 132 base_shift = 8 - order_base_2(abm->num_prios); 133 134 for (i = 0; i < abm->num_prios; i++) { 135 unsigned int offset; 136 u32 *word; 137 u8 band; 138 139 word = &alink->prio_map[i / prios_per_word]; 140 offset = (i % prios_per_word) * bits_per_prio; 141 142 band = nfp_abm_find_band_for_prio(alink, i << base_shift); 143 144 *word &= ~(field_mask << offset); 145 *word |= band << offset; 146 } 147 148 /* Qdisc offload status may change if has_prio changed */ 149 nfp_abm_qdisc_offload_update(alink); 150 151 return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map); 152 } 153 154 static void 155 nfp_abm_u32_knode_delete(struct nfp_abm_link *alink, 156 struct tc_cls_u32_knode *knode) 157 { 158 struct nfp_abm_u32_match *iter; 159 160 list_for_each_entry(iter, &alink->dscp_map, list) 161 if (iter->handle == knode->handle) { 162 list_del(&iter->list); 163 kfree(iter); 164 nfp_abm_update_band_map(alink); 165 return; 166 } 167 } 168 169 static int 170 nfp_abm_u32_knode_replace(struct nfp_abm_link *alink, 171 struct tc_cls_u32_knode *knode, 172 __be16 proto, struct netlink_ext_ack *extack) 173 { 174 struct nfp_abm_u32_match *match = NULL, *iter; 175 unsigned int tos_off; 176 u8 mask, val; 177 int err; 178 179 if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack)) 180 goto err_delete; 181 182 tos_off = proto == htons(ETH_P_IP) ? 16 : 20; 183 184 /* Extract the DSCP Class Selector bits */ 185 val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff; 186 mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff; 187 188 /* Check if there is no conflicting mapping and find match by handle */ 189 list_for_each_entry(iter, &alink->dscp_map, list) { 190 u32 cmask; 191 192 if (iter->handle == knode->handle) { 193 match = iter; 194 continue; 195 } 196 197 cmask = iter->mask & mask; 198 if ((iter->val & cmask) == (val & cmask) && 199 iter->band != knode->res->classid) { 200 NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter"); 201 goto err_delete; 202 } 203 } 204 205 if (!match) { 206 match = kzalloc(sizeof(*match), GFP_KERNEL); 207 if (!match) 208 return -ENOMEM; 209 list_add(&match->list, &alink->dscp_map); 210 } 211 match->handle = knode->handle; 212 match->band = knode->res->classid; 213 match->mask = mask; 214 match->val = val; 215 216 err = nfp_abm_update_band_map(alink); 217 if (err) 218 goto err_delete; 219 220 return 0; 221 222 err_delete: 223 nfp_abm_u32_knode_delete(alink, knode); 224 return -EOPNOTSUPP; 225 } 226 227 static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type, 228 void *type_data, void *cb_priv) 229 { 230 struct tc_cls_u32_offload *cls_u32 = type_data; 231 struct nfp_repr *repr = cb_priv; 232 struct nfp_abm_link *alink; 233 234 alink = repr->app_priv; 235 236 if (type != TC_SETUP_CLSU32) { 237 NL_SET_ERR_MSG_MOD(cls_u32->common.extack, 238 "only offload of u32 classifier supported"); 239 return -EOPNOTSUPP; 240 } 241 if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common)) 242 return -EOPNOTSUPP; 243 244 if (cls_u32->common.protocol != htons(ETH_P_IP) && 245 cls_u32->common.protocol != htons(ETH_P_IPV6)) { 246 NL_SET_ERR_MSG_MOD(cls_u32->common.extack, 247 "only IP and IPv6 supported as filter protocol"); 248 return -EOPNOTSUPP; 249 } 250 251 switch (cls_u32->command) { 252 case TC_CLSU32_NEW_KNODE: 253 case TC_CLSU32_REPLACE_KNODE: 254 return nfp_abm_u32_knode_replace(alink, &cls_u32->knode, 255 cls_u32->common.protocol, 256 cls_u32->common.extack); 257 case TC_CLSU32_DELETE_KNODE: 258 nfp_abm_u32_knode_delete(alink, &cls_u32->knode); 259 return 0; 260 default: 261 return -EOPNOTSUPP; 262 } 263 } 264 265 static LIST_HEAD(nfp_abm_block_cb_list); 266 267 int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr, 268 struct flow_block_offload *f) 269 { 270 return flow_block_cb_setup_simple(f, &nfp_abm_block_cb_list, 271 nfp_abm_setup_tc_block_cb, 272 repr, repr, true); 273 } 274