533 lines · c
1/*2 * This file is part of the Chelsio T4 Ethernet driver for Linux.3 *4 * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.5 *6 * This software is available to you under a choice of one of two7 * licenses. You may choose to be licensed under the terms of the GNU8 * General Public License (GPL) Version 2, available from the file9 * COPYING in the main directory of this source tree, or the10 * OpenIB.org BSD license below:11 *12 * Redistribution and use in source and binary forms, with or13 * without modification, are permitted provided that the following14 * conditions are met:15 *16 * - Redistributions of source code must retain the above17 * copyright notice, this list of conditions and the following18 * disclaimer.19 *20 * - Redistributions in binary form must reproduce the above21 * copyright notice, this list of conditions and the following22 * disclaimer in the documentation and/or other materials23 * provided with the distribution.24 *25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE32 * SOFTWARE.33 */34 35#include <net/tc_act/tc_gact.h>36#include <net/tc_act/tc_mirred.h>37 38#include "cxgb4.h"39#include "cxgb4_filter.h"40#include "cxgb4_tc_u32_parse.h"41#include "cxgb4_tc_u32.h"42 43/* Fill ch_filter_specification with parsed match value/mask pair. */44static int fill_match_fields(struct adapter *adap,45 struct ch_filter_specification *fs,46 struct tc_cls_u32_offload *cls,47 const struct cxgb4_match_field *entry,48 bool next_header)49{50 unsigned int i, j;51 __be32 val, mask;52 int off, err;53 bool found;54 55 for (i = 0; i < cls->knode.sel->nkeys; i++) {56 off = cls->knode.sel->keys[i].off;57 val = cls->knode.sel->keys[i].val;58 mask = cls->knode.sel->keys[i].mask;59 60 if (next_header) {61 /* For next headers, parse only keys with offmask */62 if (!cls->knode.sel->keys[i].offmask)63 continue;64 } else {65 /* For the remaining, parse only keys without offmask */66 if (cls->knode.sel->keys[i].offmask)67 continue;68 }69 70 found = false;71 72 for (j = 0; entry[j].val; j++) {73 if (off == entry[j].off) {74 found = true;75 err = entry[j].val(fs, val, mask);76 if (err)77 return err;78 break;79 }80 }81 82 if (!found)83 return -EINVAL;84 }85 86 return 0;87}88 89/* Fill ch_filter_specification with parsed action. */90static int fill_action_fields(struct adapter *adap,91 struct ch_filter_specification *fs,92 struct tc_cls_u32_offload *cls)93{94 unsigned int num_actions = 0;95 const struct tc_action *a;96 struct tcf_exts *exts;97 int i;98 99 exts = cls->knode.exts;100 if (!tcf_exts_has_actions(exts))101 return -EINVAL;102 103 tcf_exts_for_each_action(i, a, exts) {104 /* Don't allow more than one action per rule. */105 if (num_actions)106 return -EINVAL;107 108 /* Drop in hardware. */109 if (is_tcf_gact_shot(a)) {110 fs->action = FILTER_DROP;111 num_actions++;112 continue;113 }114 115 /* Re-direct to specified port in hardware. */116 if (is_tcf_mirred_egress_redirect(a)) {117 struct net_device *n_dev, *target_dev;118 bool found = false;119 unsigned int i;120 121 target_dev = tcf_mirred_dev(a);122 for_each_port(adap, i) {123 n_dev = adap->port[i];124 if (target_dev == n_dev) {125 fs->action = FILTER_SWITCH;126 fs->eport = i;127 found = true;128 break;129 }130 }131 132 /* Interface doesn't belong to any port of133 * the underlying hardware.134 */135 if (!found)136 return -EINVAL;137 138 num_actions++;139 continue;140 }141 142 /* Un-supported action. */143 return -EINVAL;144 }145 146 return 0;147}148 149int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)150{151 const struct cxgb4_match_field *start, *link_start = NULL;152 struct netlink_ext_ack *extack = cls->common.extack;153 struct adapter *adapter = netdev2adap(dev);154 __be16 protocol = cls->common.protocol;155 struct ch_filter_specification fs;156 struct cxgb4_tc_u32_table *t;157 struct cxgb4_link *link;158 u32 uhtid, link_uhtid;159 bool is_ipv6 = false;160 u8 inet_family;161 int filter_id;162 int ret;163 164 if (!can_tc_u32_offload(dev))165 return -EOPNOTSUPP;166 167 if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6))168 return -EOPNOTSUPP;169 170 inet_family = (protocol == htons(ETH_P_IPV6)) ? PF_INET6 : PF_INET;171 172 /* Get a free filter entry TID, where we can insert this new173 * rule. Only insert rule if its prio doesn't conflict with174 * existing rules.175 */176 filter_id = cxgb4_get_free_ftid(dev, inet_family, false,177 TC_U32_NODE(cls->knode.handle));178 if (filter_id < 0) {179 NL_SET_ERR_MSG_MOD(extack,180 "No free LETCAM index available");181 return -ENOMEM;182 }183 184 t = adapter->tc_u32;185 uhtid = TC_U32_USERHTID(cls->knode.handle);186 link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);187 188 /* Ensure that uhtid is either root u32 (i.e. 0x800)189 * or a a valid linked bucket.190 */191 if (uhtid != 0x800 && uhtid >= t->size)192 return -EINVAL;193 194 /* Ensure link handle uhtid is sane, if specified. */195 if (link_uhtid >= t->size)196 return -EINVAL;197 198 memset(&fs, 0, sizeof(fs));199 200 if (filter_id < adapter->tids.nhpftids)201 fs.prio = 1;202 fs.tc_prio = cls->common.prio;203 fs.tc_cookie = cls->knode.handle;204 205 if (protocol == htons(ETH_P_IPV6)) {206 start = cxgb4_ipv6_fields;207 is_ipv6 = true;208 } else {209 start = cxgb4_ipv4_fields;210 is_ipv6 = false;211 }212 213 if (uhtid != 0x800) {214 /* Link must exist from root node before insertion. */215 if (!t->table[uhtid - 1].link_handle)216 return -EINVAL;217 218 /* Link must have a valid supported next header. */219 link_start = t->table[uhtid - 1].match_field;220 if (!link_start)221 return -EINVAL;222 }223 224 /* Parse links and record them for subsequent jumps to valid225 * next headers.226 */227 if (link_uhtid) {228 const struct cxgb4_next_header *next;229 bool found = false;230 unsigned int i, j;231 __be32 val, mask;232 int off;233 234 if (t->table[link_uhtid - 1].link_handle) {235 dev_err(adapter->pdev_dev,236 "Link handle exists for: 0x%x\n",237 link_uhtid);238 return -EINVAL;239 }240 241 next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;242 243 /* Try to find matches that allow jumps to next header. */244 for (i = 0; next[i].jump; i++) {245 if (next[i].sel.offoff != cls->knode.sel->offoff ||246 next[i].sel.offshift != cls->knode.sel->offshift ||247 next[i].sel.offmask != cls->knode.sel->offmask ||248 next[i].sel.off != cls->knode.sel->off)249 continue;250 251 /* Found a possible candidate. Find a key that252 * matches the corresponding offset, value, and253 * mask to jump to next header.254 */255 for (j = 0; j < cls->knode.sel->nkeys; j++) {256 off = cls->knode.sel->keys[j].off;257 val = cls->knode.sel->keys[j].val;258 mask = cls->knode.sel->keys[j].mask;259 260 if (next[i].key.off == off &&261 next[i].key.val == val &&262 next[i].key.mask == mask) {263 found = true;264 break;265 }266 }267 268 if (!found)269 continue; /* Try next candidate. */270 271 /* Candidate to jump to next header found.272 * Translate all keys to internal specification273 * and store them in jump table. This spec is copied274 * later to set the actual filters.275 */276 ret = fill_match_fields(adapter, &fs, cls,277 start, false);278 if (ret)279 goto out;280 281 link = &t->table[link_uhtid - 1];282 link->match_field = next[i].jump;283 link->link_handle = cls->knode.handle;284 memcpy(&link->fs, &fs, sizeof(fs));285 break;286 }287 288 /* No candidate found to jump to next header. */289 if (!found)290 return -EINVAL;291 292 return 0;293 }294 295 /* Fill ch_filter_specification match fields to be shipped to hardware.296 * Copy the linked spec (if any) first. And then update the spec as297 * needed.298 */299 if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) {300 /* Copy linked ch_filter_specification */301 memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));302 ret = fill_match_fields(adapter, &fs, cls,303 link_start, true);304 if (ret)305 goto out;306 }307 308 ret = fill_match_fields(adapter, &fs, cls, start, false);309 if (ret)310 goto out;311 312 /* Fill ch_filter_specification action fields to be shipped to313 * hardware.314 */315 ret = fill_action_fields(adapter, &fs, cls);316 if (ret)317 goto out;318 319 /* The filter spec has been completely built from the info320 * provided from u32. We now set some default fields in the321 * spec for sanity.322 */323 324 /* Match only packets coming from the ingress port where this325 * filter will be created.326 */327 fs.val.iport = netdev2pinfo(dev)->port_id;328 fs.mask.iport = ~0;329 330 /* Enable filter hit counts. */331 fs.hitcnts = 1;332 333 /* Set type of filter - IPv6 or IPv4 */334 fs.type = is_ipv6 ? 1 : 0;335 336 /* Set the filter */337 ret = cxgb4_set_filter(dev, filter_id, &fs);338 if (ret)339 goto out;340 341 /* If this is a linked bucket, then set the corresponding342 * entry in the bitmap to mark it as belonging to this linked343 * bucket.344 */345 if (uhtid != 0x800 && t->table[uhtid - 1].link_handle)346 set_bit(filter_id, t->table[uhtid - 1].tid_map);347 348out:349 return ret;350}351 352int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)353{354 struct adapter *adapter = netdev2adap(dev);355 unsigned int filter_id, max_tids, i, j;356 struct cxgb4_link *link = NULL;357 struct cxgb4_tc_u32_table *t;358 struct filter_entry *f;359 bool found = false;360 u32 handle, uhtid;361 u8 nslots;362 int ret;363 364 if (!can_tc_u32_offload(dev))365 return -EOPNOTSUPP;366 367 /* Fetch the location to delete the filter. */368 max_tids = adapter->tids.nhpftids + adapter->tids.nftids;369 370 spin_lock_bh(&adapter->tids.ftid_lock);371 filter_id = 0;372 while (filter_id < max_tids) {373 if (filter_id < adapter->tids.nhpftids) {374 i = filter_id;375 f = &adapter->tids.hpftid_tab[i];376 if (f->valid && f->fs.tc_cookie == cls->knode.handle) {377 found = true;378 break;379 }380 381 i = find_next_bit(adapter->tids.hpftid_bmap,382 adapter->tids.nhpftids, i + 1);383 if (i >= adapter->tids.nhpftids) {384 filter_id = adapter->tids.nhpftids;385 continue;386 }387 388 filter_id = i;389 } else {390 i = filter_id - adapter->tids.nhpftids;391 f = &adapter->tids.ftid_tab[i];392 if (f->valid && f->fs.tc_cookie == cls->knode.handle) {393 found = true;394 break;395 }396 397 i = find_next_bit(adapter->tids.ftid_bmap,398 adapter->tids.nftids, i + 1);399 if (i >= adapter->tids.nftids)400 break;401 402 filter_id = i + adapter->tids.nhpftids;403 }404 405 nslots = 0;406 if (f->fs.type) {407 nslots++;408 if (CHELSIO_CHIP_VERSION(adapter->params.chip) <409 CHELSIO_T6)410 nslots += 2;411 }412 413 filter_id += nslots;414 }415 spin_unlock_bh(&adapter->tids.ftid_lock);416 417 if (!found)418 return -ERANGE;419 420 t = adapter->tc_u32;421 handle = cls->knode.handle;422 uhtid = TC_U32_USERHTID(cls->knode.handle);423 424 /* Ensure that uhtid is either root u32 (i.e. 0x800)425 * or a a valid linked bucket.426 */427 if (uhtid != 0x800 && uhtid >= t->size)428 return -EINVAL;429 430 /* Delete the specified filter */431 if (uhtid != 0x800) {432 link = &t->table[uhtid - 1];433 if (!link->link_handle)434 return -EINVAL;435 436 if (!test_bit(filter_id, link->tid_map))437 return -EINVAL;438 }439 440 ret = cxgb4_del_filter(dev, filter_id, NULL);441 if (ret)442 goto out;443 444 if (link)445 clear_bit(filter_id, link->tid_map);446 447 /* If a link is being deleted, then delete all filters448 * associated with the link.449 */450 for (i = 0; i < t->size; i++) {451 link = &t->table[i];452 453 if (link->link_handle == handle) {454 for (j = 0; j < max_tids; j++) {455 if (!test_bit(j, link->tid_map))456 continue;457 458 ret = __cxgb4_del_filter(dev, j, NULL, NULL);459 if (ret)460 goto out;461 462 clear_bit(j, link->tid_map);463 }464 465 /* Clear the link state */466 link->match_field = NULL;467 link->link_handle = 0;468 memset(&link->fs, 0, sizeof(link->fs));469 break;470 }471 }472 473out:474 return ret;475}476 477void cxgb4_cleanup_tc_u32(struct adapter *adap)478{479 struct cxgb4_tc_u32_table *t;480 unsigned int i;481 482 if (!adap->tc_u32)483 return;484 485 /* Free up all allocated memory. */486 t = adap->tc_u32;487 for (i = 0; i < t->size; i++) {488 struct cxgb4_link *link = &t->table[i];489 490 kvfree(link->tid_map);491 }492 kvfree(adap->tc_u32);493}494 495struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap)496{497 unsigned int max_tids = adap->tids.nftids + adap->tids.nhpftids;498 struct cxgb4_tc_u32_table *t;499 unsigned int i;500 501 if (!max_tids)502 return NULL;503 504 t = kvzalloc(struct_size(t, table, max_tids), GFP_KERNEL);505 if (!t)506 return NULL;507 508 t->size = max_tids;509 510 for (i = 0; i < t->size; i++) {511 struct cxgb4_link *link = &t->table[i];512 unsigned int bmap_size;513 514 bmap_size = BITS_TO_LONGS(max_tids);515 link->tid_map = kvcalloc(bmap_size, sizeof(unsigned long),516 GFP_KERNEL);517 if (!link->tid_map)518 goto out_no_mem;519 bitmap_zero(link->tid_map, max_tids);520 }521 522 return t;523 524out_no_mem:525 for (i = 0; i < t->size; i++) {526 struct cxgb4_link *link = &t->table[i];527 kvfree(link->tid_map);528 }529 kvfree(t);530 531 return NULL;532}533