ocelot_flower.c 7.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch driver
 * Copyright (c) 2019 Microsemi Corporation
 */

#include <net/pkt_cls.h>
#include <net/tc_act/tc_gact.h>

#include "ocelot_ace.h"

11
static int ocelot_flower_parse_action(struct flow_cls_offload *f,
12
				      struct ocelot_ace_rule *ace)
13 14
{
	const struct flow_action_entry *a;
15 16
	s64 burst;
	u64 rate;
17 18
	int i;

19
	if (!flow_offload_has_one_action(&f->rule->action))
20 21
		return -EOPNOTSUPP;

22 23
	if (!flow_action_basic_hw_stats_check(&f->rule->action,
					      f->common.extack))
24 25
		return -EOPNOTSUPP;

26 27 28
	flow_action_for_each(i, a, &f->rule->action) {
		switch (a->id) {
		case FLOW_ACTION_DROP:
29
			ace->action = OCELOT_ACL_ACTION_DROP;
30 31
			break;
		case FLOW_ACTION_TRAP:
32
			ace->action = OCELOT_ACL_ACTION_TRAP;
33
			break;
34 35 36 37 38 39 40
		case FLOW_ACTION_POLICE:
			ace->action = OCELOT_ACL_ACTION_POLICE;
			rate = a->police.rate_bytes_ps;
			ace->pol.rate = div_u64(rate, 1000) * 8;
			burst = rate * PSCHED_NS2TICKS(a->police.burst);
			ace->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC);
			break;
41 42 43 44 45 46 47 48
		default:
			return -EOPNOTSUPP;
		}
	}

	return 0;
}

49
static int ocelot_flower_parse(struct flow_cls_offload *f,
50
			       struct ocelot_ace_rule *ace)
51
{
52
	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
53
	struct flow_dissector *dissector = rule->match.dissector;
54 55
	u16 proto = ntohs(f->common.protocol);
	bool match_protocol = true;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

	if (dissector->used_keys &
	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
		return -EOPNOTSUPP;
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
		struct flow_match_control match;

		flow_rule_match_control(rule, &match);
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
		struct flow_match_eth_addrs match;

		/* The hw support mac matches only for MAC_ETYPE key,
		 * therefore if other matches(port, tcp flags, etc) are added
		 * then just bail out
		 */
		if ((dissector->used_keys &
		    (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
		     BIT(FLOW_DISSECTOR_KEY_BASIC) |
		     BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
		    (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
		     BIT(FLOW_DISSECTOR_KEY_BASIC) |
		     BIT(FLOW_DISSECTOR_KEY_CONTROL)))
			return -EOPNOTSUPP;

		flow_rule_match_eth_addrs(rule, &match);
91 92
		ace->type = OCELOT_ACE_TYPE_ETYPE;
		ether_addr_copy(ace->frame.etype.dmac.value,
93
				match.key->dst);
94
		ether_addr_copy(ace->frame.etype.smac.value,
95
				match.key->src);
96
		ether_addr_copy(ace->frame.etype.dmac.mask,
97
				match.mask->dst);
98
		ether_addr_copy(ace->frame.etype.smac.mask,
99 100 101 102 103 104 105 106 107
				match.mask->src);
		goto finished_key_parsing;
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
		struct flow_match_basic match;

		flow_rule_match_basic(rule, &match);
		if (ntohs(match.key->n_proto) == ETH_P_IP) {
108 109
			ace->type = OCELOT_ACE_TYPE_IPV4;
			ace->frame.ipv4.proto.value[0] =
110
				match.key->ip_proto;
111
			ace->frame.ipv4.proto.mask[0] =
112
				match.mask->ip_proto;
113
			match_protocol = false;
114 115
		}
		if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
116 117
			ace->type = OCELOT_ACE_TYPE_IPV6;
			ace->frame.ipv6.proto.value[0] =
118
				match.key->ip_proto;
119
			ace->frame.ipv6.proto.mask[0] =
120
				match.mask->ip_proto;
121
			match_protocol = false;
122 123 124 125
		}
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
126
	    proto == ETH_P_IP) {
127 128 129 130
		struct flow_match_ipv4_addrs match;
		u8 *tmp;

		flow_rule_match_ipv4_addrs(rule, &match);
131
		tmp = &ace->frame.ipv4.sip.value.addr[0];
132 133
		memcpy(tmp, &match.key->src, 4);

134
		tmp = &ace->frame.ipv4.sip.mask.addr[0];
135 136
		memcpy(tmp, &match.mask->src, 4);

137
		tmp = &ace->frame.ipv4.dip.value.addr[0];
138 139
		memcpy(tmp, &match.key->dst, 4);

140
		tmp = &ace->frame.ipv4.dip.mask.addr[0];
141
		memcpy(tmp, &match.mask->dst, 4);
142
		match_protocol = false;
143 144 145
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
146
	    proto == ETH_P_IPV6) {
147 148 149 150 151 152 153
		return -EOPNOTSUPP;
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
		struct flow_match_ports match;

		flow_rule_match_ports(rule, &match);
154 155 156 157
		ace->frame.ipv4.sport.value = ntohs(match.key->src);
		ace->frame.ipv4.sport.mask = ntohs(match.mask->src);
		ace->frame.ipv4.dport.value = ntohs(match.key->dst);
		ace->frame.ipv4.dport.mask = ntohs(match.mask->dst);
158
		match_protocol = false;
159 160 161 162 163 164
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
		struct flow_match_vlan match;

		flow_rule_match_vlan(rule, &match);
165 166 167 168 169
		ace->type = OCELOT_ACE_TYPE_ANY;
		ace->vlan.vid.value = match.key->vlan_id;
		ace->vlan.vid.mask = match.mask->vlan_id;
		ace->vlan.pcp.value[0] = match.key->vlan_priority;
		ace->vlan.pcp.mask[0] = match.mask->vlan_priority;
170
		match_protocol = false;
171 172 173
	}

finished_key_parsing:
174 175 176 177 178 179 180 181 182 183
	if (match_protocol && proto != ETH_P_ALL) {
		/* TODO: support SNAP, LLC etc */
		if (proto < ETH_P_802_3_MIN)
			return -EOPNOTSUPP;
		ace->type = OCELOT_ACE_TYPE_ETYPE;
		*(u16 *)ace->frame.etype.etype.value = htons(proto);
		*(u16 *)ace->frame.etype.etype.mask = 0xffff;
	}
	/* else, a rule of type OCELOT_ACE_TYPE_ANY is implicitly added */

184 185 186
	ace->prio = f->common.prio;
	ace->id = f->cookie;
	return ocelot_flower_parse_action(f, ace);
187 188 189
}

static
190 191
struct ocelot_ace_rule *ocelot_ace_rule_create(struct ocelot *ocelot, int port,
					       struct flow_cls_offload *f)
192
{
193
	struct ocelot_ace_rule *ace;
194

195 196
	ace = kzalloc(sizeof(*ace), GFP_KERNEL);
	if (!ace)
197 198
		return NULL;

199 200
	ace->ingress_port_mask = BIT(port);
	return ace;
201 202
}

203 204
int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
			      struct flow_cls_offload *f, bool ingress)
205
{
206
	struct ocelot_ace_rule *ace;
207 208
	int ret;

209 210
	ace = ocelot_ace_rule_create(ocelot, port, f);
	if (!ace)
211 212
		return -ENOMEM;

213
	ret = ocelot_flower_parse(f, ace);
214
	if (ret) {
215
		kfree(ace);
216 217 218
		return ret;
	}

219
	return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack);
220
}
221
EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
222

223 224
int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
			      struct flow_cls_offload *f, bool ingress)
225
{
226
	struct ocelot_ace_rule ace;
227

228 229
	ace.prio = f->common.prio;
	ace.id = f->cookie;
230

231
	return ocelot_ace_rule_offload_del(ocelot, &ace);
232
}
233
EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
234

235 236
int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
			    struct flow_cls_offload *f, bool ingress)
237
{
238
	struct ocelot_ace_rule ace;
239 240
	int ret;

241 242 243
	ace.prio = f->common.prio;
	ace.id = f->cookie;
	ret = ocelot_ace_rule_stats_update(ocelot, &ace);
244 245 246
	if (ret)
		return ret;

247 248
	flow_stats_update(&f->stats, 0x0, ace.stats.pkts, 0x0,
			  FLOW_ACTION_HW_STATS_IMMEDIATE);
249 250
	return 0;
}
251
EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);
252

253 254 255
int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
			       struct flow_cls_offload *f,
			       bool ingress)
256
{
257 258 259 260 261 262
	struct ocelot *ocelot = priv->port.ocelot;
	int port = priv->chip_port;

	if (!ingress)
		return -EOPNOTSUPP;

263
	switch (f->command) {
264
	case FLOW_CLS_REPLACE:
265
		return ocelot_cls_flower_replace(ocelot, port, f, ingress);
266
	case FLOW_CLS_DESTROY:
267
		return ocelot_cls_flower_destroy(ocelot, port, f, ingress);
268
	case FLOW_CLS_STATS:
269
		return ocelot_cls_flower_stats(ocelot, port, f, ingress);
270 271 272 273
	default:
		return -EOPNOTSUPP;
	}
}