brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · a5d2f84 Raw
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