1c9da1ac1SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
2c9da1ac1SSteen Hegelund /* Microchip VCAP API
3c9da1ac1SSteen Hegelund  *
4c9da1ac1SSteen Hegelund  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5c9da1ac1SSteen Hegelund  */
6c9da1ac1SSteen Hegelund 
7c9da1ac1SSteen Hegelund #include <net/tcp.h>
8c9da1ac1SSteen Hegelund 
9c9da1ac1SSteen Hegelund #include "sparx5_tc.h"
10c9da1ac1SSteen Hegelund #include "vcap_api.h"
11c9da1ac1SSteen Hegelund #include "vcap_api_client.h"
12c9da1ac1SSteen Hegelund #include "sparx5_main.h"
13c9da1ac1SSteen Hegelund #include "sparx5_vcap_impl.h"
14c9da1ac1SSteen Hegelund 
15c9da1ac1SSteen Hegelund struct sparx5_tc_flower_parse_usage {
16c9da1ac1SSteen Hegelund 	struct flow_cls_offload *fco;
17c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
18c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
19d6c2964dSSteen Hegelund 	u16 l3_proto;
20d6c2964dSSteen Hegelund 	u8 l4_proto;
21c9da1ac1SSteen Hegelund 	unsigned int used_keys;
22c9da1ac1SSteen Hegelund };
23c9da1ac1SSteen Hegelund 
24d6c2964dSSteen Hegelund /* These protocols have dedicated keysets in IS2 and a TC dissector
25d6c2964dSSteen Hegelund  * ETH_P_ARP does not have a TC dissector
26d6c2964dSSteen Hegelund  */
27d6c2964dSSteen Hegelund static u16 sparx5_tc_known_etypes[] = {
28d6c2964dSSteen Hegelund 	ETH_P_ALL,
29d6c2964dSSteen Hegelund 	ETH_P_IP,
30d6c2964dSSteen Hegelund 	ETH_P_IPV6,
31d6c2964dSSteen Hegelund };
32d6c2964dSSteen Hegelund 
33d6c2964dSSteen Hegelund static bool sparx5_tc_is_known_etype(u16 etype)
34d6c2964dSSteen Hegelund {
35d6c2964dSSteen Hegelund 	int idx;
36d6c2964dSSteen Hegelund 
37d6c2964dSSteen Hegelund 	/* For now this only knows about IS2 traffic classification */
38d6c2964dSSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx)
39d6c2964dSSteen Hegelund 		if (sparx5_tc_known_etypes[idx] == etype)
40d6c2964dSSteen Hegelund 			return true;
41d6c2964dSSteen Hegelund 
42d6c2964dSSteen Hegelund 	return false;
43d6c2964dSSteen Hegelund }
44d6c2964dSSteen Hegelund 
45c9da1ac1SSteen Hegelund static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st)
46c9da1ac1SSteen Hegelund {
47c9da1ac1SSteen Hegelund 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
48c9da1ac1SSteen Hegelund 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
49c9da1ac1SSteen Hegelund 	struct flow_match_eth_addrs match;
50c9da1ac1SSteen Hegelund 	struct vcap_u48_key smac, dmac;
51c9da1ac1SSteen Hegelund 	int err = 0;
52c9da1ac1SSteen Hegelund 
53c9da1ac1SSteen Hegelund 	flow_rule_match_eth_addrs(st->frule, &match);
54c9da1ac1SSteen Hegelund 
55c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->src)) {
56c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
57c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
58c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
59c9da1ac1SSteen Hegelund 		if (err)
60c9da1ac1SSteen Hegelund 			goto out;
61c9da1ac1SSteen Hegelund 	}
62c9da1ac1SSteen Hegelund 
63c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->dst)) {
64c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
65c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
66c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
67c9da1ac1SSteen Hegelund 		if (err)
68c9da1ac1SSteen Hegelund 			goto out;
69c9da1ac1SSteen Hegelund 	}
70c9da1ac1SSteen Hegelund 
71c9da1ac1SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
72c9da1ac1SSteen Hegelund 
73c9da1ac1SSteen Hegelund 	return err;
74c9da1ac1SSteen Hegelund 
75c9da1ac1SSteen Hegelund out:
76c9da1ac1SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error");
77c9da1ac1SSteen Hegelund 	return err;
78c9da1ac1SSteen Hegelund }
79c9da1ac1SSteen Hegelund 
80d6c2964dSSteen Hegelund static int
81d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st)
82d6c2964dSSteen Hegelund {
83d6c2964dSSteen Hegelund 	int err = 0;
84d6c2964dSSteen Hegelund 
85d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IP) {
86d6c2964dSSteen Hegelund 		struct flow_match_ipv4_addrs mt;
87d6c2964dSSteen Hegelund 
88d6c2964dSSteen Hegelund 		flow_rule_match_ipv4_addrs(st->frule, &mt);
89d6c2964dSSteen Hegelund 		if (mt.mask->src) {
90d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
91d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_SIP,
92d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->src),
93d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->src));
94d6c2964dSSteen Hegelund 			if (err)
95d6c2964dSSteen Hegelund 				goto out;
96d6c2964dSSteen Hegelund 		}
97d6c2964dSSteen Hegelund 		if (mt.mask->dst) {
98d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
99d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_DIP,
100d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->dst),
101d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->dst));
102d6c2964dSSteen Hegelund 			if (err)
103d6c2964dSSteen Hegelund 				goto out;
104d6c2964dSSteen Hegelund 		}
105d6c2964dSSteen Hegelund 	}
106d6c2964dSSteen Hegelund 
107d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
108d6c2964dSSteen Hegelund 
109d6c2964dSSteen Hegelund 	return err;
110d6c2964dSSteen Hegelund 
111d6c2964dSSteen Hegelund out:
112d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error");
113d6c2964dSSteen Hegelund 	return err;
114d6c2964dSSteen Hegelund }
115d6c2964dSSteen Hegelund 
116d6c2964dSSteen Hegelund static int
117d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st)
118d6c2964dSSteen Hegelund {
119d6c2964dSSteen Hegelund 	int err = 0;
120d6c2964dSSteen Hegelund 
121d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IPV6) {
122d6c2964dSSteen Hegelund 		struct flow_match_ipv6_addrs mt;
123d6c2964dSSteen Hegelund 		struct vcap_u128_key sip;
124d6c2964dSSteen Hegelund 		struct vcap_u128_key dip;
125d6c2964dSSteen Hegelund 
126d6c2964dSSteen Hegelund 		flow_rule_match_ipv6_addrs(st->frule, &mt);
127d6c2964dSSteen Hegelund 		/* Check if address masks are non-zero */
128d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->src)) {
129d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16);
130d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16);
131d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
132d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_SIP, &sip);
133d6c2964dSSteen Hegelund 			if (err)
134d6c2964dSSteen Hegelund 				goto out;
135d6c2964dSSteen Hegelund 		}
136d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->dst)) {
137d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16);
138d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16);
139d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
140d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_DIP, &dip);
141d6c2964dSSteen Hegelund 			if (err)
142d6c2964dSSteen Hegelund 				goto out;
143d6c2964dSSteen Hegelund 		}
144d6c2964dSSteen Hegelund 	}
145d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
146d6c2964dSSteen Hegelund 	return err;
147d6c2964dSSteen Hegelund out:
148d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error");
149d6c2964dSSteen Hegelund 	return err;
150d6c2964dSSteen Hegelund }
151d6c2964dSSteen Hegelund 
152d6c2964dSSteen Hegelund static int
153d6c2964dSSteen Hegelund sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st)
154d6c2964dSSteen Hegelund {
155d6c2964dSSteen Hegelund 	struct flow_match_control mt;
156d6c2964dSSteen Hegelund 	u32 value, mask;
157d6c2964dSSteen Hegelund 	int err = 0;
158d6c2964dSSteen Hegelund 
159d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
160d6c2964dSSteen Hegelund 
161d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
162d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
163d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
164d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
165d6c2964dSSteen Hegelund 				mask = 0x3;
166d6c2964dSSteen Hegelund 			} else {
167d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
168d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
169d6c2964dSSteen Hegelund 					mask = 0x3;
170d6c2964dSSteen Hegelund 				} else {
171d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
172d6c2964dSSteen Hegelund 					mask = 0x3;
173d6c2964dSSteen Hegelund 				}
174d6c2964dSSteen Hegelund 			}
175d6c2964dSSteen Hegelund 		} else {
176d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
177d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
178d6c2964dSSteen Hegelund 				mask = 0x3;
179d6c2964dSSteen Hegelund 			} else {
180d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
181d6c2964dSSteen Hegelund 				mask = 0x3;
182d6c2964dSSteen Hegelund 			}
183d6c2964dSSteen Hegelund 		}
184d6c2964dSSteen Hegelund 
185d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
186d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
187d6c2964dSSteen Hegelund 					    value, mask);
188d6c2964dSSteen Hegelund 		if (err)
189d6c2964dSSteen Hegelund 			goto out;
190d6c2964dSSteen Hegelund 	}
191d6c2964dSSteen Hegelund 
192d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
193d6c2964dSSteen Hegelund 
194d6c2964dSSteen Hegelund 	return err;
195d6c2964dSSteen Hegelund 
196d6c2964dSSteen Hegelund out:
197d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
198d6c2964dSSteen Hegelund 	return err;
199d6c2964dSSteen Hegelund }
200d6c2964dSSteen Hegelund 
201d6c2964dSSteen Hegelund static int
202d6c2964dSSteen Hegelund sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st)
203d6c2964dSSteen Hegelund {
204d6c2964dSSteen Hegelund 	struct flow_match_ports mt;
205d6c2964dSSteen Hegelund 	u16 value, mask;
206d6c2964dSSteen Hegelund 	int err = 0;
207d6c2964dSSteen Hegelund 
208d6c2964dSSteen Hegelund 	flow_rule_match_ports(st->frule, &mt);
209d6c2964dSSteen Hegelund 
210d6c2964dSSteen Hegelund 	if (mt.mask->src) {
211d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->src);
212d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->src);
213d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value,
214d6c2964dSSteen Hegelund 					    mask);
215d6c2964dSSteen Hegelund 		if (err)
216d6c2964dSSteen Hegelund 			goto out;
217d6c2964dSSteen Hegelund 	}
218d6c2964dSSteen Hegelund 
219d6c2964dSSteen Hegelund 	if (mt.mask->dst) {
220d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->dst);
221d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->dst);
222d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value,
223d6c2964dSSteen Hegelund 					    mask);
224d6c2964dSSteen Hegelund 		if (err)
225d6c2964dSSteen Hegelund 			goto out;
226d6c2964dSSteen Hegelund 	}
227d6c2964dSSteen Hegelund 
228d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS);
229d6c2964dSSteen Hegelund 
230d6c2964dSSteen Hegelund 	return err;
231d6c2964dSSteen Hegelund 
232d6c2964dSSteen Hegelund out:
233d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error");
234d6c2964dSSteen Hegelund 	return err;
235d6c2964dSSteen Hegelund }
236d6c2964dSSteen Hegelund 
237d6c2964dSSteen Hegelund static int
238d6c2964dSSteen Hegelund sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st)
239d6c2964dSSteen Hegelund {
240d6c2964dSSteen Hegelund 	struct flow_match_basic mt;
241d6c2964dSSteen Hegelund 	int err = 0;
242d6c2964dSSteen Hegelund 
243d6c2964dSSteen Hegelund 	flow_rule_match_basic(st->frule, &mt);
244d6c2964dSSteen Hegelund 
245d6c2964dSSteen Hegelund 	if (mt.mask->n_proto) {
246d6c2964dSSteen Hegelund 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
247d6c2964dSSteen Hegelund 		if (!sparx5_tc_is_known_etype(st->l3_proto)) {
248d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
249d6c2964dSSteen Hegelund 						    st->l3_proto, ~0);
250d6c2964dSSteen Hegelund 			if (err)
251d6c2964dSSteen Hegelund 				goto out;
252d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IP) {
253d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
254d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
255d6c2964dSSteen Hegelund 			if (err)
256d6c2964dSSteen Hegelund 				goto out;
257d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IPV6) {
258d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
259d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
260d6c2964dSSteen Hegelund 			if (err)
261d6c2964dSSteen Hegelund 				goto out;
262d6c2964dSSteen Hegelund 		}
263d6c2964dSSteen Hegelund 	}
264d6c2964dSSteen Hegelund 
265d6c2964dSSteen Hegelund 	if (mt.mask->ip_proto) {
266d6c2964dSSteen Hegelund 		st->l4_proto = mt.key->ip_proto;
267d6c2964dSSteen Hegelund 		if (st->l4_proto == IPPROTO_TCP) {
268d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
269d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
270d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
271d6c2964dSSteen Hegelund 			if (err)
272d6c2964dSSteen Hegelund 				goto out;
273d6c2964dSSteen Hegelund 		} else if (st->l4_proto == IPPROTO_UDP) {
274d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
275d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
276d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
277d6c2964dSSteen Hegelund 			if (err)
278d6c2964dSSteen Hegelund 				goto out;
279d6c2964dSSteen Hegelund 		} else {
280d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
281d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP_PROTO,
282d6c2964dSSteen Hegelund 						    st->l4_proto, ~0);
283d6c2964dSSteen Hegelund 			if (err)
284d6c2964dSSteen Hegelund 				goto out;
285d6c2964dSSteen Hegelund 		}
286d6c2964dSSteen Hegelund 	}
287d6c2964dSSteen Hegelund 
288d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
289d6c2964dSSteen Hegelund 
290d6c2964dSSteen Hegelund 	return err;
291d6c2964dSSteen Hegelund 
292d6c2964dSSteen Hegelund out:
293d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
294d6c2964dSSteen Hegelund 	return err;
295d6c2964dSSteen Hegelund }
296d6c2964dSSteen Hegelund 
297d6c2964dSSteen Hegelund static int
298d6c2964dSSteen Hegelund sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st)
299d6c2964dSSteen Hegelund {
300d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
301d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
302d6c2964dSSteen Hegelund 	struct flow_match_vlan mt;
303d6c2964dSSteen Hegelund 	int err;
304d6c2964dSSteen Hegelund 
305d6c2964dSSteen Hegelund 	flow_rule_match_vlan(st->frule, &mt);
306d6c2964dSSteen Hegelund 
307d6c2964dSSteen Hegelund 	if (mt.mask->vlan_id) {
308d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, vid_key,
309d6c2964dSSteen Hegelund 					    mt.key->vlan_id,
310d6c2964dSSteen Hegelund 					    mt.mask->vlan_id);
311d6c2964dSSteen Hegelund 		if (err)
312d6c2964dSSteen Hegelund 			goto out;
313d6c2964dSSteen Hegelund 	}
314d6c2964dSSteen Hegelund 
315d6c2964dSSteen Hegelund 	if (mt.mask->vlan_priority) {
316d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, pcp_key,
317d6c2964dSSteen Hegelund 					    mt.key->vlan_priority,
318d6c2964dSSteen Hegelund 					    mt.mask->vlan_priority);
319d6c2964dSSteen Hegelund 		if (err)
320d6c2964dSSteen Hegelund 			goto out;
321d6c2964dSSteen Hegelund 	}
322d6c2964dSSteen Hegelund 
323d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
324d6c2964dSSteen Hegelund 
325d6c2964dSSteen Hegelund 	return err;
326d6c2964dSSteen Hegelund out:
327d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error");
328d6c2964dSSteen Hegelund 	return err;
329d6c2964dSSteen Hegelund }
330d6c2964dSSteen Hegelund 
331d6c2964dSSteen Hegelund static int
332d6c2964dSSteen Hegelund sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st)
333d6c2964dSSteen Hegelund {
334d6c2964dSSteen Hegelund 	struct flow_match_tcp mt;
335d6c2964dSSteen Hegelund 	u16 tcp_flags_mask;
336d6c2964dSSteen Hegelund 	u16 tcp_flags_key;
337d6c2964dSSteen Hegelund 	enum vcap_bit val;
338d6c2964dSSteen Hegelund 	int err = 0;
339d6c2964dSSteen Hegelund 
340d6c2964dSSteen Hegelund 	flow_rule_match_tcp(st->frule, &mt);
341d6c2964dSSteen Hegelund 	tcp_flags_key = be16_to_cpu(mt.key->flags);
342d6c2964dSSteen Hegelund 	tcp_flags_mask = be16_to_cpu(mt.mask->flags);
343d6c2964dSSteen Hegelund 
344d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_FIN) {
345d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
346d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_FIN)
347d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
348d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val);
349d6c2964dSSteen Hegelund 		if (err)
350d6c2964dSSteen Hegelund 			goto out;
351d6c2964dSSteen Hegelund 	}
352d6c2964dSSteen Hegelund 
353d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_SYN) {
354d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
355d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_SYN)
356d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
357d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val);
358d6c2964dSSteen Hegelund 		if (err)
359d6c2964dSSteen Hegelund 			goto out;
360d6c2964dSSteen Hegelund 	}
361d6c2964dSSteen Hegelund 
362d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_RST) {
363d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
364d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_RST)
365d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
366d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val);
367d6c2964dSSteen Hegelund 		if (err)
368d6c2964dSSteen Hegelund 			goto out;
369d6c2964dSSteen Hegelund 	}
370d6c2964dSSteen Hegelund 
371d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_PSH) {
372d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
373d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_PSH)
374d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
375d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val);
376d6c2964dSSteen Hegelund 		if (err)
377d6c2964dSSteen Hegelund 			goto out;
378d6c2964dSSteen Hegelund 	}
379d6c2964dSSteen Hegelund 
380d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_ACK) {
381d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
382d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_ACK)
383d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
384d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val);
385d6c2964dSSteen Hegelund 		if (err)
386d6c2964dSSteen Hegelund 			goto out;
387d6c2964dSSteen Hegelund 	}
388d6c2964dSSteen Hegelund 
389d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_URG) {
390d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
391d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_URG)
392d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
393d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val);
394d6c2964dSSteen Hegelund 		if (err)
395d6c2964dSSteen Hegelund 			goto out;
396d6c2964dSSteen Hegelund 	}
397d6c2964dSSteen Hegelund 
398d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP);
399d6c2964dSSteen Hegelund 
400d6c2964dSSteen Hegelund 	return err;
401d6c2964dSSteen Hegelund 
402d6c2964dSSteen Hegelund out:
403d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error");
404d6c2964dSSteen Hegelund 	return err;
405d6c2964dSSteen Hegelund }
406d6c2964dSSteen Hegelund 
407d6c2964dSSteen Hegelund static int
408d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
409d6c2964dSSteen Hegelund {
410d6c2964dSSteen Hegelund 	struct flow_match_ip mt;
411d6c2964dSSteen Hegelund 	int err = 0;
412d6c2964dSSteen Hegelund 
413d6c2964dSSteen Hegelund 	flow_rule_match_ip(st->frule, &mt);
414d6c2964dSSteen Hegelund 
415d6c2964dSSteen Hegelund 	if (mt.mask->tos) {
416d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS,
417d6c2964dSSteen Hegelund 					    mt.key->tos,
418d6c2964dSSteen Hegelund 					    mt.mask->tos);
419d6c2964dSSteen Hegelund 		if (err)
420d6c2964dSSteen Hegelund 			goto out;
421d6c2964dSSteen Hegelund 	}
422d6c2964dSSteen Hegelund 
423d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP);
424d6c2964dSSteen Hegelund 
425d6c2964dSSteen Hegelund 	return err;
426d6c2964dSSteen Hegelund 
427d6c2964dSSteen Hegelund out:
428d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error");
429d6c2964dSSteen Hegelund 	return err;
430d6c2964dSSteen Hegelund }
431d6c2964dSSteen Hegelund 
432c9da1ac1SSteen Hegelund static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = {
433c9da1ac1SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage,
434d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage,
435d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage,
436d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
437d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage,
438d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
439d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
440d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage,
441d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage,
442c9da1ac1SSteen Hegelund };
443c9da1ac1SSteen Hegelund 
444c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
445c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
446c9da1ac1SSteen Hegelund 				    struct vcap_rule *vrule)
447c9da1ac1SSteen Hegelund {
448c9da1ac1SSteen Hegelund 	struct sparx5_tc_flower_parse_usage state = {
449c9da1ac1SSteen Hegelund 		.fco = fco,
450c9da1ac1SSteen Hegelund 		.vrule = vrule,
451c9da1ac1SSteen Hegelund 	};
452c9da1ac1SSteen Hegelund 	int idx, err = 0;
453c9da1ac1SSteen Hegelund 
454c9da1ac1SSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
455c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
456c9da1ac1SSteen Hegelund 		if (!flow_rule_match_key(state.frule, idx))
457c9da1ac1SSteen Hegelund 			continue;
458c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
459c9da1ac1SSteen Hegelund 			continue;
460c9da1ac1SSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](&state);
461c9da1ac1SSteen Hegelund 		if (err)
462c9da1ac1SSteen Hegelund 			return err;
463c9da1ac1SSteen Hegelund 	}
464c9da1ac1SSteen Hegelund 	return err;
465c9da1ac1SSteen Hegelund }
466c9da1ac1SSteen Hegelund 
467*392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
468*392d0ab0SSteen Hegelund 					 struct flow_cls_offload *fco,
469*392d0ab0SSteen Hegelund 					 struct vcap_admin *admin)
470*392d0ab0SSteen Hegelund {
471*392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
472*392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
473*392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
474*392d0ab0SSteen Hegelund 	u64 action_mask = 0;
475*392d0ab0SSteen Hegelund 	int idx;
476*392d0ab0SSteen Hegelund 
477*392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
478*392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
479*392d0ab0SSteen Hegelund 		return -EINVAL;
480*392d0ab0SSteen Hegelund 	}
481*392d0ab0SSteen Hegelund 
482*392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
483*392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
484*392d0ab0SSteen Hegelund 
485*392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
486*392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
487*392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
488*392d0ab0SSteen Hegelund 					   "More actions of the same type");
489*392d0ab0SSteen Hegelund 			return -EINVAL;
490*392d0ab0SSteen Hegelund 		}
491*392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
492*392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
493*392d0ab0SSteen Hegelund 	}
494*392d0ab0SSteen Hegelund 
495*392d0ab0SSteen Hegelund 	/* Check that last action is a goto */
496*392d0ab0SSteen Hegelund 	if (last_actent->id != FLOW_ACTION_GOTO) {
497*392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
498*392d0ab0SSteen Hegelund 				   "Last action must be 'goto'");
499*392d0ab0SSteen Hegelund 		return -EINVAL;
500*392d0ab0SSteen Hegelund 	}
501*392d0ab0SSteen Hegelund 
502*392d0ab0SSteen Hegelund 	/* Check if the goto chain is in the next lookup */
503*392d0ab0SSteen Hegelund 	if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
504*392d0ab0SSteen Hegelund 				 last_actent->chain_index)) {
505*392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
506*392d0ab0SSteen Hegelund 				   "Invalid goto chain");
507*392d0ab0SSteen Hegelund 		return -EINVAL;
508*392d0ab0SSteen Hegelund 	}
509*392d0ab0SSteen Hegelund 
510*392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
511*392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
512*392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
513*392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
514*392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
515*392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
516*392d0ab0SSteen Hegelund 	}
517*392d0ab0SSteen Hegelund 
518*392d0ab0SSteen Hegelund 	return 0;
519*392d0ab0SSteen Hegelund }
520*392d0ab0SSteen Hegelund 
521c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
522c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
523c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
524c9da1ac1SSteen Hegelund {
525c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
526c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
527c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
528c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
529c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
530c9da1ac1SSteen Hegelund 	int err, idx;
531c9da1ac1SSteen Hegelund 
532c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
533*392d0ab0SSteen Hegelund 
534*392d0ab0SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, fco, admin);
535*392d0ab0SSteen Hegelund 	if (err)
536*392d0ab0SSteen Hegelund 		return err;
537*392d0ab0SSteen Hegelund 
538c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
539c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
540c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
541c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
542c9da1ac1SSteen Hegelund 
543c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
544c9da1ac1SSteen Hegelund 	sparx5_tc_use_dissectors(fco, admin, vrule);
545*392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
546c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
547c9da1ac1SSteen Hegelund 		switch (act->id) {
548c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
549c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_bit(vrule,
550c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_COPY_ENA,
551c9da1ac1SSteen Hegelund 						       VCAP_BIT_1);
552c9da1ac1SSteen Hegelund 			if (err)
553c9da1ac1SSteen Hegelund 				goto out;
554c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule,
555c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_QUEUE_NUM, 0);
556c9da1ac1SSteen Hegelund 			if (err)
557c9da1ac1SSteen Hegelund 				goto out;
558c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
559c9da1ac1SSteen Hegelund 						       SPX5_PMM_REPLACE_ALL);
560c9da1ac1SSteen Hegelund 			if (err)
561c9da1ac1SSteen Hegelund 				goto out;
562c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
563c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
564c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
565c9da1ac1SSteen Hegelund 			if (err)
566c9da1ac1SSteen Hegelund 				goto out;
567c9da1ac1SSteen Hegelund 			break;
568c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
569c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
570c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
571c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
572c9da1ac1SSteen Hegelund 			if (err)
573c9da1ac1SSteen Hegelund 				goto out;
574c9da1ac1SSteen Hegelund 			break;
575*392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
576*392d0ab0SSteen Hegelund 			/* Links between VCAPs will be added later */
577*392d0ab0SSteen Hegelund 			break;
578c9da1ac1SSteen Hegelund 		default:
579c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
580c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
581c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
582c9da1ac1SSteen Hegelund 			goto out;
583c9da1ac1SSteen Hegelund 		}
584c9da1ac1SSteen Hegelund 	}
585c9da1ac1SSteen Hegelund 	/* For now the keyset is hardcoded */
586c9da1ac1SSteen Hegelund 	err = vcap_set_rule_set_keyset(vrule, VCAP_KFS_MAC_ETYPE);
587c9da1ac1SSteen Hegelund 	if (err) {
588c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
589c9da1ac1SSteen Hegelund 				   "No matching port keyset for filter protocol and keys");
590c9da1ac1SSteen Hegelund 		goto out;
591c9da1ac1SSteen Hegelund 	}
592c9da1ac1SSteen Hegelund 	err = vcap_val_rule(vrule, ETH_P_ALL);
593c9da1ac1SSteen Hegelund 	if (err) {
594c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
595c9da1ac1SSteen Hegelund 		goto out;
596c9da1ac1SSteen Hegelund 	}
597c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
598c9da1ac1SSteen Hegelund 	if (err)
599c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
600c9da1ac1SSteen Hegelund 				   "Could not add the filter");
601c9da1ac1SSteen Hegelund out:
602c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
603c9da1ac1SSteen Hegelund 	return err;
604c9da1ac1SSteen Hegelund }
605c9da1ac1SSteen Hegelund 
606c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
607c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
608c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
609c9da1ac1SSteen Hegelund {
610c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
611c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
612c9da1ac1SSteen Hegelund 	int err = -ENOENT, rule_id;
613c9da1ac1SSteen Hegelund 
614c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
615c9da1ac1SSteen Hegelund 	while (true) {
616c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
617c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
618c9da1ac1SSteen Hegelund 			break;
619c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
620c9da1ac1SSteen Hegelund 		if (err) {
621c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
622c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
623c9da1ac1SSteen Hegelund 			break;
624c9da1ac1SSteen Hegelund 		}
625c9da1ac1SSteen Hegelund 	}
626c9da1ac1SSteen Hegelund 	return err;
627c9da1ac1SSteen Hegelund }
628c9da1ac1SSteen Hegelund 
629c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
630c9da1ac1SSteen Hegelund 		     bool ingress)
631c9da1ac1SSteen Hegelund {
632c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
633c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
634c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
635c9da1ac1SSteen Hegelund 	int err = -EINVAL;
636c9da1ac1SSteen Hegelund 
637c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
638c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
639c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
640c9da1ac1SSteen Hegelund 	if (!admin) {
641c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
642c9da1ac1SSteen Hegelund 		return err;
643c9da1ac1SSteen Hegelund 	}
644c9da1ac1SSteen Hegelund 
645c9da1ac1SSteen Hegelund 	switch (fco->command) {
646c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
647c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin);
648c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
649c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
650c9da1ac1SSteen Hegelund 	default:
651c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
652c9da1ac1SSteen Hegelund 	}
653c9da1ac1SSteen Hegelund }
654