brintos

brintos / linux-shallow public Read only

0
0
Text · 7.0 KiB · 23ebddf Raw
274 lines · c
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 12struct nfp_abm_u32_match {13	u32 handle;14	u32 band;15	u8 mask;16	u8 val;17	struct list_head list;18};19 20static bool21nfp_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 or104 * low double digit number of prios and likewise for the filters.  Also u32105 * doesn't report stats, so it's really only setup time cost.106 */107static unsigned int108nfp_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 119static 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 154static void155nfp_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 169static int170nfp_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 222err_delete:223	nfp_abm_u32_knode_delete(alink, knode);224	return -EOPNOTSUPP;225}226 227static 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 265static LIST_HEAD(nfp_abm_block_cb_list);266 267int 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