]>
Commit | Line | Data |
---|---|---|
8d0bcb01 DB |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Copyright (C) 2014-2017 aQuantia Corporation. */ | |
3 | ||
4 | /* File aq_filters.c: RX filters related functions. */ | |
5 | ||
6 | #include "aq_filters.h" | |
7 | ||
8 | static bool __must_check | |
9 | aq_rule_is_approve(struct ethtool_rx_flow_spec *fsp) | |
10 | { | |
11 | if (fsp->flow_type & FLOW_MAC_EXT) | |
12 | return false; | |
13 | ||
14 | switch (fsp->flow_type & ~FLOW_EXT) { | |
15 | case ETHER_FLOW: | |
16 | case TCP_V4_FLOW: | |
17 | case UDP_V4_FLOW: | |
18 | case SCTP_V4_FLOW: | |
19 | case TCP_V6_FLOW: | |
20 | case UDP_V6_FLOW: | |
21 | case SCTP_V6_FLOW: | |
22 | case IPV4_FLOW: | |
23 | case IPV6_FLOW: | |
24 | return true; | |
25 | case IP_USER_FLOW: | |
26 | switch (fsp->h_u.usr_ip4_spec.proto) { | |
27 | case IPPROTO_TCP: | |
28 | case IPPROTO_UDP: | |
29 | case IPPROTO_SCTP: | |
30 | case IPPROTO_IP: | |
31 | return true; | |
32 | default: | |
33 | return false; | |
34 | } | |
35 | case IPV6_USER_FLOW: | |
36 | switch (fsp->h_u.usr_ip6_spec.l4_proto) { | |
37 | case IPPROTO_TCP: | |
38 | case IPPROTO_UDP: | |
39 | case IPPROTO_SCTP: | |
40 | case IPPROTO_IP: | |
41 | return true; | |
42 | default: | |
43 | return false; | |
44 | } | |
45 | default: | |
46 | return false; | |
47 | } | |
48 | ||
49 | return false; | |
50 | } | |
51 | ||
52 | static bool __must_check | |
53 | aq_match_filter(struct ethtool_rx_flow_spec *fsp1, | |
54 | struct ethtool_rx_flow_spec *fsp2) | |
55 | { | |
56 | if (fsp1->flow_type != fsp2->flow_type || | |
57 | memcmp(&fsp1->h_u, &fsp2->h_u, sizeof(fsp2->h_u)) || | |
58 | memcmp(&fsp1->h_ext, &fsp2->h_ext, sizeof(fsp2->h_ext)) || | |
59 | memcmp(&fsp1->m_u, &fsp2->m_u, sizeof(fsp2->m_u)) || | |
60 | memcmp(&fsp1->m_ext, &fsp2->m_ext, sizeof(fsp2->m_ext))) | |
61 | return false; | |
62 | ||
63 | return true; | |
64 | } | |
65 | ||
66 | static bool __must_check | |
67 | aq_rule_already_exists(struct aq_nic_s *aq_nic, | |
68 | struct ethtool_rx_flow_spec *fsp) | |
69 | { | |
70 | struct aq_rx_filter *rule; | |
71 | struct hlist_node *aq_node2; | |
72 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
73 | ||
74 | hlist_for_each_entry_safe(rule, aq_node2, | |
75 | &rx_fltrs->filter_list, aq_node) { | |
76 | if (rule->aq_fsp.location == fsp->location) | |
77 | continue; | |
78 | if (aq_match_filter(&rule->aq_fsp, fsp)) { | |
79 | netdev_err(aq_nic->ndev, | |
80 | "ethtool: This filter is already set\n"); | |
81 | return true; | |
82 | } | |
83 | } | |
84 | ||
85 | return false; | |
86 | } | |
87 | ||
a6ed6f22 DB |
88 | static int aq_check_approve_fl3l4(struct aq_nic_s *aq_nic, |
89 | struct aq_hw_rx_fltrs_s *rx_fltrs, | |
90 | struct ethtool_rx_flow_spec *fsp) | |
91 | { | |
92 | if (fsp->location < AQ_RX_FIRST_LOC_FL3L4 || | |
93 | fsp->location > AQ_RX_LAST_LOC_FL3L4) { | |
94 | netdev_err(aq_nic->ndev, | |
95 | "ethtool: location must be in range [%d, %d]", | |
96 | AQ_RX_FIRST_LOC_FL3L4, | |
97 | AQ_RX_LAST_LOC_FL3L4); | |
98 | return -EINVAL; | |
99 | } | |
100 | if (rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv4) { | |
101 | rx_fltrs->fl3l4.is_ipv6 = false; | |
102 | netdev_err(aq_nic->ndev, | |
103 | "ethtool: mixing ipv4 and ipv6 is not allowed"); | |
104 | return -EINVAL; | |
105 | } else if (!rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv6) { | |
106 | rx_fltrs->fl3l4.is_ipv6 = true; | |
107 | netdev_err(aq_nic->ndev, | |
108 | "ethtool: mixing ipv4 and ipv6 is not allowed"); | |
109 | return -EINVAL; | |
110 | } else if (rx_fltrs->fl3l4.is_ipv6 && | |
111 | fsp->location != AQ_RX_FIRST_LOC_FL3L4 + 4 && | |
112 | fsp->location != AQ_RX_FIRST_LOC_FL3L4) { | |
113 | netdev_err(aq_nic->ndev, | |
114 | "ethtool: The specified location for ipv6 must be %d or %d", | |
115 | AQ_RX_FIRST_LOC_FL3L4, AQ_RX_FIRST_LOC_FL3L4 + 4); | |
116 | return -EINVAL; | |
117 | } | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
9a8cac4b DB |
122 | static int __must_check |
123 | aq_check_approve_fl2(struct aq_nic_s *aq_nic, | |
124 | struct aq_hw_rx_fltrs_s *rx_fltrs, | |
125 | struct ethtool_rx_flow_spec *fsp) | |
126 | { | |
127 | if (fsp->location < AQ_RX_FIRST_LOC_FETHERT || | |
128 | fsp->location > AQ_RX_LAST_LOC_FETHERT) { | |
129 | netdev_err(aq_nic->ndev, | |
130 | "ethtool: location must be in range [%d, %d]", | |
131 | AQ_RX_FIRST_LOC_FETHERT, | |
132 | AQ_RX_LAST_LOC_FETHERT); | |
133 | return -EINVAL; | |
134 | } | |
135 | ||
136 | if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK && | |
137 | fsp->m_u.ether_spec.h_proto == 0U) { | |
138 | netdev_err(aq_nic->ndev, | |
7c460cf9 | 139 | "ethtool: proto (ether_type) parameter must be specified"); |
9a8cac4b DB |
140 | return -EINVAL; |
141 | } | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
54bcb3d1 DB |
146 | static int __must_check |
147 | aq_check_approve_fvlan(struct aq_nic_s *aq_nic, | |
148 | struct aq_hw_rx_fltrs_s *rx_fltrs, | |
149 | struct ethtool_rx_flow_spec *fsp) | |
150 | { | |
151 | if (fsp->location < AQ_RX_FIRST_LOC_FVLANID || | |
152 | fsp->location > AQ_RX_LAST_LOC_FVLANID) { | |
153 | netdev_err(aq_nic->ndev, | |
154 | "ethtool: location must be in range [%d, %d]", | |
155 | AQ_RX_FIRST_LOC_FVLANID, | |
156 | AQ_RX_LAST_LOC_FVLANID); | |
157 | return -EINVAL; | |
158 | } | |
159 | ||
7975d2af DB |
160 | if ((aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) && |
161 | (!test_bit(be16_to_cpu(fsp->h_ext.vlan_tci), | |
162 | aq_nic->active_vlans))) { | |
163 | netdev_err(aq_nic->ndev, | |
164 | "ethtool: unknown vlan-id specified"); | |
165 | return -EINVAL; | |
166 | } | |
167 | ||
54bcb3d1 DB |
168 | if (fsp->ring_cookie > aq_nic->aq_nic_cfg.num_rss_queues) { |
169 | netdev_err(aq_nic->ndev, | |
170 | "ethtool: queue number must be in range [0, %d]", | |
171 | aq_nic->aq_nic_cfg.num_rss_queues - 1); | |
172 | return -EINVAL; | |
173 | } | |
174 | return 0; | |
175 | } | |
176 | ||
8d0bcb01 DB |
177 | static int __must_check |
178 | aq_check_filter(struct aq_nic_s *aq_nic, | |
179 | struct ethtool_rx_flow_spec *fsp) | |
180 | { | |
181 | int err = 0; | |
a6ed6f22 | 182 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); |
8d0bcb01 DB |
183 | |
184 | if (fsp->flow_type & FLOW_EXT) { | |
54bcb3d1 DB |
185 | if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_VID_MASK) { |
186 | err = aq_check_approve_fvlan(aq_nic, rx_fltrs, fsp); | |
9a8cac4b DB |
187 | } else if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK) { |
188 | err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp); | |
54bcb3d1 DB |
189 | } else { |
190 | netdev_err(aq_nic->ndev, | |
191 | "ethtool: invalid vlan mask 0x%x specified", | |
192 | be16_to_cpu(fsp->m_ext.vlan_tci)); | |
193 | err = -EINVAL; | |
194 | } | |
8d0bcb01 DB |
195 | } else { |
196 | switch (fsp->flow_type & ~FLOW_EXT) { | |
197 | case ETHER_FLOW: | |
9a8cac4b | 198 | err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp); |
8d0bcb01 DB |
199 | break; |
200 | case TCP_V4_FLOW: | |
201 | case UDP_V4_FLOW: | |
202 | case SCTP_V4_FLOW: | |
203 | case IPV4_FLOW: | |
204 | case IP_USER_FLOW: | |
a6ed6f22 DB |
205 | rx_fltrs->fl3l4.is_ipv6 = false; |
206 | err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp); | |
8d0bcb01 DB |
207 | break; |
208 | case TCP_V6_FLOW: | |
209 | case UDP_V6_FLOW: | |
210 | case SCTP_V6_FLOW: | |
211 | case IPV6_FLOW: | |
212 | case IPV6_USER_FLOW: | |
a6ed6f22 DB |
213 | rx_fltrs->fl3l4.is_ipv6 = true; |
214 | err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp); | |
8d0bcb01 DB |
215 | break; |
216 | default: | |
217 | netdev_err(aq_nic->ndev, | |
218 | "ethtool: unknown flow-type specified"); | |
219 | err = -EINVAL; | |
220 | } | |
221 | } | |
222 | ||
223 | return err; | |
224 | } | |
225 | ||
226 | static bool __must_check | |
227 | aq_rule_is_not_support(struct aq_nic_s *aq_nic, | |
228 | struct ethtool_rx_flow_spec *fsp) | |
229 | { | |
230 | bool rule_is_not_support = false; | |
231 | ||
232 | if (!(aq_nic->ndev->features & NETIF_F_NTUPLE)) { | |
233 | netdev_err(aq_nic->ndev, | |
234 | "ethtool: Please, to enable the RX flow control:\n" | |
235 | "ethtool -K %s ntuple on\n", aq_nic->ndev->name); | |
236 | rule_is_not_support = true; | |
237 | } else if (!aq_rule_is_approve(fsp)) { | |
238 | netdev_err(aq_nic->ndev, | |
239 | "ethtool: The specified flow type is not supported\n"); | |
240 | rule_is_not_support = true; | |
241 | } else if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW && | |
242 | (fsp->h_u.tcp_ip4_spec.tos || | |
243 | fsp->h_u.tcp_ip6_spec.tclass)) { | |
244 | netdev_err(aq_nic->ndev, | |
245 | "ethtool: The specified tos tclass are not supported\n"); | |
246 | rule_is_not_support = true; | |
9a8cac4b DB |
247 | } else if (fsp->flow_type & FLOW_MAC_EXT) { |
248 | netdev_err(aq_nic->ndev, | |
249 | "ethtool: MAC_EXT is not supported"); | |
250 | rule_is_not_support = true; | |
8d0bcb01 DB |
251 | } |
252 | ||
253 | return rule_is_not_support; | |
254 | } | |
255 | ||
256 | static bool __must_check | |
257 | aq_rule_is_not_correct(struct aq_nic_s *aq_nic, | |
258 | struct ethtool_rx_flow_spec *fsp) | |
259 | { | |
260 | bool rule_is_not_correct = false; | |
261 | ||
262 | if (!aq_nic) { | |
263 | rule_is_not_correct = true; | |
a6ed6f22 DB |
264 | } else if (fsp->location > AQ_RX_MAX_RXNFC_LOC) { |
265 | netdev_err(aq_nic->ndev, | |
266 | "ethtool: The specified number %u rule is invalid\n", | |
267 | fsp->location); | |
268 | rule_is_not_correct = true; | |
8d0bcb01 DB |
269 | } else if (aq_check_filter(aq_nic, fsp)) { |
270 | rule_is_not_correct = true; | |
271 | } else if (fsp->ring_cookie != RX_CLS_FLOW_DISC) { | |
272 | if (fsp->ring_cookie >= aq_nic->aq_nic_cfg.num_rss_queues) { | |
273 | netdev_err(aq_nic->ndev, | |
274 | "ethtool: The specified action is invalid.\n" | |
275 | "Maximum allowable value action is %u.\n", | |
276 | aq_nic->aq_nic_cfg.num_rss_queues - 1); | |
277 | rule_is_not_correct = true; | |
278 | } | |
279 | } | |
280 | ||
281 | return rule_is_not_correct; | |
282 | } | |
283 | ||
284 | static int __must_check | |
285 | aq_check_rule(struct aq_nic_s *aq_nic, | |
286 | struct ethtool_rx_flow_spec *fsp) | |
287 | { | |
288 | int err = 0; | |
289 | ||
290 | if (aq_rule_is_not_correct(aq_nic, fsp)) | |
291 | err = -EINVAL; | |
292 | else if (aq_rule_is_not_support(aq_nic, fsp)) | |
293 | err = -EOPNOTSUPP; | |
294 | else if (aq_rule_already_exists(aq_nic, fsp)) | |
295 | err = -EEXIST; | |
296 | ||
297 | return err; | |
298 | } | |
299 | ||
9a8cac4b DB |
300 | static void aq_set_data_fl2(struct aq_nic_s *aq_nic, |
301 | struct aq_rx_filter *aq_rx_fltr, | |
302 | struct aq_rx_filter_l2 *data, bool add) | |
303 | { | |
304 | const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp; | |
305 | ||
306 | memset(data, 0, sizeof(*data)); | |
307 | ||
308 | data->location = fsp->location - AQ_RX_FIRST_LOC_FETHERT; | |
309 | ||
310 | if (fsp->ring_cookie != RX_CLS_FLOW_DISC) | |
311 | data->queue = fsp->ring_cookie; | |
312 | else | |
313 | data->queue = -1; | |
314 | ||
315 | data->ethertype = be16_to_cpu(fsp->h_u.ether_spec.h_proto); | |
316 | data->user_priority_en = be16_to_cpu(fsp->m_ext.vlan_tci) | |
317 | == VLAN_PRIO_MASK; | |
318 | data->user_priority = (be16_to_cpu(fsp->h_ext.vlan_tci) | |
319 | & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | |
320 | } | |
321 | ||
322 | static int aq_add_del_fether(struct aq_nic_s *aq_nic, | |
323 | struct aq_rx_filter *aq_rx_fltr, bool add) | |
324 | { | |
325 | struct aq_rx_filter_l2 data; | |
326 | struct aq_hw_s *aq_hw = aq_nic->aq_hw; | |
327 | const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops; | |
328 | ||
329 | aq_set_data_fl2(aq_nic, aq_rx_fltr, &data, add); | |
330 | ||
331 | if (unlikely(!aq_hw_ops->hw_filter_l2_set)) | |
332 | return -EOPNOTSUPP; | |
333 | if (unlikely(!aq_hw_ops->hw_filter_l2_clear)) | |
334 | return -EOPNOTSUPP; | |
335 | ||
336 | if (add) | |
337 | return aq_hw_ops->hw_filter_l2_set(aq_hw, &data); | |
338 | else | |
339 | return aq_hw_ops->hw_filter_l2_clear(aq_hw, &data); | |
340 | } | |
341 | ||
7975d2af DB |
342 | static bool aq_fvlan_is_busy(struct aq_rx_filter_vlan *aq_vlans, int vlan) |
343 | { | |
344 | int i; | |
345 | ||
346 | for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) { | |
347 | if (aq_vlans[i].enable && | |
348 | aq_vlans[i].queue != AQ_RX_QUEUE_NOT_ASSIGNED && | |
349 | aq_vlans[i].vlan_id == vlan) { | |
350 | return true; | |
351 | } | |
352 | } | |
353 | ||
354 | return false; | |
355 | } | |
356 | ||
357 | /* Function rebuilds array of vlan filters so that filters with assigned | |
358 | * queue have a precedence over just vlans on the interface. | |
359 | */ | |
360 | static void aq_fvlan_rebuild(struct aq_nic_s *aq_nic, | |
361 | unsigned long *active_vlans, | |
362 | struct aq_rx_filter_vlan *aq_vlans) | |
363 | { | |
364 | bool vlan_busy = false; | |
365 | int vlan = -1; | |
366 | int i; | |
367 | ||
368 | for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) { | |
369 | if (aq_vlans[i].enable && | |
370 | aq_vlans[i].queue != AQ_RX_QUEUE_NOT_ASSIGNED) | |
371 | continue; | |
372 | do { | |
373 | vlan = find_next_bit(active_vlans, | |
374 | VLAN_N_VID, | |
375 | vlan + 1); | |
376 | if (vlan == VLAN_N_VID) { | |
377 | aq_vlans[i].enable = 0U; | |
378 | aq_vlans[i].queue = AQ_RX_QUEUE_NOT_ASSIGNED; | |
379 | aq_vlans[i].vlan_id = 0; | |
380 | continue; | |
381 | } | |
382 | ||
383 | vlan_busy = aq_fvlan_is_busy(aq_vlans, vlan); | |
384 | if (!vlan_busy) { | |
385 | aq_vlans[i].enable = 1U; | |
386 | aq_vlans[i].queue = AQ_RX_QUEUE_NOT_ASSIGNED; | |
387 | aq_vlans[i].vlan_id = vlan; | |
388 | } | |
389 | } while (vlan_busy && vlan != VLAN_N_VID); | |
390 | } | |
391 | } | |
392 | ||
54bcb3d1 DB |
393 | static int aq_set_data_fvlan(struct aq_nic_s *aq_nic, |
394 | struct aq_rx_filter *aq_rx_fltr, | |
395 | struct aq_rx_filter_vlan *aq_vlans, bool add) | |
396 | { | |
397 | const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp; | |
398 | int location = fsp->location - AQ_RX_FIRST_LOC_FVLANID; | |
7975d2af | 399 | int i; |
54bcb3d1 DB |
400 | |
401 | memset(&aq_vlans[location], 0, sizeof(aq_vlans[location])); | |
402 | ||
403 | if (!add) | |
404 | return 0; | |
405 | ||
7975d2af DB |
406 | /* remove vlan if it was in table without queue assignment */ |
407 | for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) { | |
408 | if (aq_vlans[i].vlan_id == | |
409 | (be16_to_cpu(fsp->h_ext.vlan_tci) & VLAN_VID_MASK)) { | |
410 | aq_vlans[i].enable = false; | |
411 | } | |
412 | } | |
413 | ||
54bcb3d1 DB |
414 | aq_vlans[location].location = location; |
415 | aq_vlans[location].vlan_id = be16_to_cpu(fsp->h_ext.vlan_tci) | |
416 | & VLAN_VID_MASK; | |
417 | aq_vlans[location].queue = fsp->ring_cookie & 0x1FU; | |
418 | aq_vlans[location].enable = 1U; | |
7975d2af | 419 | |
54bcb3d1 DB |
420 | return 0; |
421 | } | |
422 | ||
7975d2af DB |
423 | int aq_del_fvlan_by_vlan(struct aq_nic_s *aq_nic, u16 vlan_id) |
424 | { | |
425 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
426 | struct aq_rx_filter *rule = NULL; | |
427 | struct hlist_node *aq_node2; | |
428 | ||
429 | hlist_for_each_entry_safe(rule, aq_node2, | |
430 | &rx_fltrs->filter_list, aq_node) { | |
431 | if (be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) | |
432 | break; | |
433 | } | |
434 | if (rule && be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) { | |
435 | struct ethtool_rxnfc cmd; | |
436 | ||
437 | cmd.fs.location = rule->aq_fsp.location; | |
438 | return aq_del_rxnfc_rule(aq_nic, &cmd); | |
439 | } | |
440 | ||
441 | return -ENOENT; | |
442 | } | |
443 | ||
54bcb3d1 DB |
444 | static int aq_add_del_fvlan(struct aq_nic_s *aq_nic, |
445 | struct aq_rx_filter *aq_rx_fltr, bool add) | |
446 | { | |
447 | const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops; | |
448 | ||
449 | if (unlikely(!aq_hw_ops->hw_filter_vlan_set)) | |
450 | return -EOPNOTSUPP; | |
451 | ||
452 | aq_set_data_fvlan(aq_nic, | |
453 | aq_rx_fltr, | |
454 | aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans, | |
455 | add); | |
456 | ||
457 | return aq_filters_vlans_update(aq_nic); | |
458 | } | |
459 | ||
a6ed6f22 DB |
460 | static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic, |
461 | struct aq_rx_filter *aq_rx_fltr, | |
462 | struct aq_rx_filter_l3l4 *data, bool add) | |
463 | { | |
464 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
465 | const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp; | |
466 | ||
467 | memset(data, 0, sizeof(*data)); | |
468 | ||
469 | data->is_ipv6 = rx_fltrs->fl3l4.is_ipv6; | |
470 | data->location = HW_ATL_GET_REG_LOCATION_FL3L4(fsp->location); | |
471 | ||
472 | if (!add) { | |
473 | if (!data->is_ipv6) | |
474 | rx_fltrs->fl3l4.active_ipv4 &= ~BIT(data->location); | |
475 | else | |
476 | rx_fltrs->fl3l4.active_ipv6 &= | |
477 | ~BIT((data->location) / 4); | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
482 | data->cmd |= HW_ATL_RX_ENABLE_FLTR_L3L4; | |
483 | ||
484 | switch (fsp->flow_type) { | |
485 | case TCP_V4_FLOW: | |
486 | case TCP_V6_FLOW: | |
487 | data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4; | |
488 | break; | |
489 | case UDP_V4_FLOW: | |
490 | case UDP_V6_FLOW: | |
491 | data->cmd |= HW_ATL_RX_UDP; | |
492 | data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4; | |
493 | break; | |
494 | case SCTP_V4_FLOW: | |
495 | case SCTP_V6_FLOW: | |
496 | data->cmd |= HW_ATL_RX_SCTP; | |
497 | data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4; | |
498 | break; | |
499 | default: | |
500 | break; | |
501 | } | |
502 | ||
503 | if (!data->is_ipv6) { | |
504 | data->ip_src[0] = | |
505 | ntohl(fsp->h_u.tcp_ip4_spec.ip4src); | |
506 | data->ip_dst[0] = | |
507 | ntohl(fsp->h_u.tcp_ip4_spec.ip4dst); | |
508 | rx_fltrs->fl3l4.active_ipv4 |= BIT(data->location); | |
509 | } else { | |
510 | int i; | |
511 | ||
512 | rx_fltrs->fl3l4.active_ipv6 |= BIT((data->location) / 4); | |
513 | for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) { | |
514 | data->ip_dst[i] = | |
515 | ntohl(fsp->h_u.tcp_ip6_spec.ip6dst[i]); | |
516 | data->ip_src[i] = | |
517 | ntohl(fsp->h_u.tcp_ip6_spec.ip6src[i]); | |
518 | } | |
519 | data->cmd |= HW_ATL_RX_ENABLE_L3_IPV6; | |
520 | } | |
521 | if (fsp->flow_type != IP_USER_FLOW && | |
522 | fsp->flow_type != IPV6_USER_FLOW) { | |
523 | if (!data->is_ipv6) { | |
524 | data->p_dst = | |
525 | ntohs(fsp->h_u.tcp_ip4_spec.pdst); | |
526 | data->p_src = | |
527 | ntohs(fsp->h_u.tcp_ip4_spec.psrc); | |
528 | } else { | |
529 | data->p_dst = | |
530 | ntohs(fsp->h_u.tcp_ip6_spec.pdst); | |
531 | data->p_src = | |
532 | ntohs(fsp->h_u.tcp_ip6_spec.psrc); | |
533 | } | |
534 | } | |
535 | if (data->ip_src[0] && !data->is_ipv6) | |
536 | data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3; | |
537 | if (data->ip_dst[0] && !data->is_ipv6) | |
538 | data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3; | |
539 | if (data->p_dst) | |
540 | data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4; | |
541 | if (data->p_src) | |
542 | data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4; | |
543 | if (fsp->ring_cookie != RX_CLS_FLOW_DISC) { | |
544 | data->cmd |= HW_ATL_RX_HOST << HW_ATL_RX_ACTION_FL3F4_SHIFT; | |
545 | data->cmd |= fsp->ring_cookie << HW_ATL_RX_QUEUE_FL3L4_SHIFT; | |
546 | data->cmd |= HW_ATL_RX_ENABLE_QUEUE_L3L4; | |
547 | } else { | |
548 | data->cmd |= HW_ATL_RX_DISCARD << HW_ATL_RX_ACTION_FL3F4_SHIFT; | |
549 | } | |
550 | ||
551 | return 0; | |
552 | } | |
553 | ||
554 | static int aq_set_fl3l4(struct aq_hw_s *aq_hw, | |
555 | const struct aq_hw_ops *aq_hw_ops, | |
556 | struct aq_rx_filter_l3l4 *data) | |
557 | { | |
558 | if (unlikely(!aq_hw_ops->hw_filter_l3l4_set)) | |
559 | return -EOPNOTSUPP; | |
560 | ||
561 | return aq_hw_ops->hw_filter_l3l4_set(aq_hw, data); | |
562 | } | |
563 | ||
564 | static int aq_add_del_fl3l4(struct aq_nic_s *aq_nic, | |
565 | struct aq_rx_filter *aq_rx_fltr, bool add) | |
566 | { | |
567 | const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops; | |
568 | struct aq_hw_s *aq_hw = aq_nic->aq_hw; | |
569 | struct aq_rx_filter_l3l4 data; | |
570 | ||
571 | if (unlikely(aq_rx_fltr->aq_fsp.location < AQ_RX_FIRST_LOC_FL3L4 || | |
572 | aq_rx_fltr->aq_fsp.location > AQ_RX_LAST_LOC_FL3L4 || | |
573 | aq_set_data_fl3l4(aq_nic, aq_rx_fltr, &data, add))) | |
574 | return -EINVAL; | |
575 | ||
576 | return aq_set_fl3l4(aq_hw, aq_hw_ops, &data); | |
577 | } | |
578 | ||
8d0bcb01 DB |
579 | static int aq_add_del_rule(struct aq_nic_s *aq_nic, |
580 | struct aq_rx_filter *aq_rx_fltr, bool add) | |
581 | { | |
582 | int err = -EINVAL; | |
583 | ||
584 | if (aq_rx_fltr->aq_fsp.flow_type & FLOW_EXT) { | |
54bcb3d1 DB |
585 | if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci) |
586 | == VLAN_VID_MASK) { | |
587 | aq_rx_fltr->type = aq_rx_filter_vlan; | |
588 | err = aq_add_del_fvlan(aq_nic, aq_rx_fltr, add); | |
9a8cac4b DB |
589 | } else if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci) |
590 | == VLAN_PRIO_MASK) { | |
591 | aq_rx_fltr->type = aq_rx_filter_ethertype; | |
592 | err = aq_add_del_fether(aq_nic, aq_rx_fltr, add); | |
54bcb3d1 | 593 | } |
8d0bcb01 DB |
594 | } else { |
595 | switch (aq_rx_fltr->aq_fsp.flow_type & ~FLOW_EXT) { | |
596 | case ETHER_FLOW: | |
9a8cac4b DB |
597 | aq_rx_fltr->type = aq_rx_filter_ethertype; |
598 | err = aq_add_del_fether(aq_nic, aq_rx_fltr, add); | |
8d0bcb01 DB |
599 | break; |
600 | case TCP_V4_FLOW: | |
601 | case UDP_V4_FLOW: | |
602 | case SCTP_V4_FLOW: | |
603 | case IP_USER_FLOW: | |
604 | case TCP_V6_FLOW: | |
605 | case UDP_V6_FLOW: | |
606 | case SCTP_V6_FLOW: | |
607 | case IPV6_USER_FLOW: | |
a6ed6f22 DB |
608 | aq_rx_fltr->type = aq_rx_filter_l3l4; |
609 | err = aq_add_del_fl3l4(aq_nic, aq_rx_fltr, add); | |
8d0bcb01 DB |
610 | break; |
611 | default: | |
612 | err = -EINVAL; | |
613 | break; | |
614 | } | |
615 | } | |
616 | ||
617 | return err; | |
618 | } | |
619 | ||
620 | static int aq_update_table_filters(struct aq_nic_s *aq_nic, | |
621 | struct aq_rx_filter *aq_rx_fltr, u16 index, | |
622 | struct ethtool_rxnfc *cmd) | |
623 | { | |
624 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
625 | struct aq_rx_filter *rule = NULL, *parent = NULL; | |
626 | struct hlist_node *aq_node2; | |
627 | int err = -EINVAL; | |
628 | ||
629 | hlist_for_each_entry_safe(rule, aq_node2, | |
630 | &rx_fltrs->filter_list, aq_node) { | |
631 | if (rule->aq_fsp.location >= index) | |
632 | break; | |
633 | parent = rule; | |
634 | } | |
635 | ||
636 | if (rule && rule->aq_fsp.location == index) { | |
637 | err = aq_add_del_rule(aq_nic, rule, false); | |
638 | hlist_del(&rule->aq_node); | |
639 | kfree(rule); | |
640 | --rx_fltrs->active_filters; | |
641 | } | |
642 | ||
643 | if (unlikely(!aq_rx_fltr)) | |
644 | return err; | |
645 | ||
646 | INIT_HLIST_NODE(&aq_rx_fltr->aq_node); | |
647 | ||
648 | if (parent) | |
649 | hlist_add_behind(&aq_rx_fltr->aq_node, &parent->aq_node); | |
650 | else | |
651 | hlist_add_head(&aq_rx_fltr->aq_node, &rx_fltrs->filter_list); | |
652 | ||
653 | ++rx_fltrs->active_filters; | |
654 | ||
655 | return 0; | |
656 | } | |
657 | ||
658 | u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic) | |
659 | { | |
660 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
661 | ||
662 | return rx_fltrs->active_filters; | |
663 | } | |
664 | ||
665 | struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic) | |
666 | { | |
667 | return &aq_nic->aq_hw_rx_fltrs; | |
668 | } | |
669 | ||
670 | int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd) | |
671 | { | |
672 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
673 | struct ethtool_rx_flow_spec *fsp = | |
674 | (struct ethtool_rx_flow_spec *)&cmd->fs; | |
675 | struct aq_rx_filter *aq_rx_fltr; | |
676 | int err = 0; | |
677 | ||
678 | err = aq_check_rule(aq_nic, fsp); | |
679 | if (err) | |
680 | goto err_exit; | |
681 | ||
682 | aq_rx_fltr = kzalloc(sizeof(*aq_rx_fltr), GFP_KERNEL); | |
683 | if (unlikely(!aq_rx_fltr)) { | |
684 | err = -ENOMEM; | |
685 | goto err_exit; | |
686 | } | |
687 | ||
688 | memcpy(&aq_rx_fltr->aq_fsp, fsp, sizeof(*fsp)); | |
689 | ||
690 | err = aq_update_table_filters(aq_nic, aq_rx_fltr, fsp->location, NULL); | |
691 | if (unlikely(err)) | |
692 | goto err_free; | |
693 | ||
694 | err = aq_add_del_rule(aq_nic, aq_rx_fltr, true); | |
695 | if (unlikely(err)) { | |
696 | hlist_del(&aq_rx_fltr->aq_node); | |
697 | --rx_fltrs->active_filters; | |
698 | goto err_free; | |
699 | } | |
700 | ||
701 | return 0; | |
702 | ||
703 | err_free: | |
704 | kfree(aq_rx_fltr); | |
705 | err_exit: | |
706 | return err; | |
707 | } | |
708 | ||
709 | int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd) | |
710 | { | |
711 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
712 | struct aq_rx_filter *rule = NULL; | |
713 | struct hlist_node *aq_node2; | |
714 | int err = -EINVAL; | |
715 | ||
716 | hlist_for_each_entry_safe(rule, aq_node2, | |
717 | &rx_fltrs->filter_list, aq_node) { | |
718 | if (rule->aq_fsp.location == cmd->fs.location) | |
719 | break; | |
720 | } | |
721 | ||
722 | if (rule && rule->aq_fsp.location == cmd->fs.location) { | |
723 | err = aq_add_del_rule(aq_nic, rule, false); | |
724 | hlist_del(&rule->aq_node); | |
725 | kfree(rule); | |
726 | --rx_fltrs->active_filters; | |
727 | } | |
728 | return err; | |
729 | } | |
730 | ||
731 | int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd) | |
732 | { | |
733 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
734 | struct ethtool_rx_flow_spec *fsp = | |
735 | (struct ethtool_rx_flow_spec *)&cmd->fs; | |
736 | struct aq_rx_filter *rule = NULL; | |
737 | struct hlist_node *aq_node2; | |
738 | ||
739 | hlist_for_each_entry_safe(rule, aq_node2, | |
740 | &rx_fltrs->filter_list, aq_node) | |
741 | if (fsp->location <= rule->aq_fsp.location) | |
742 | break; | |
743 | ||
744 | if (unlikely(!rule || fsp->location != rule->aq_fsp.location)) | |
745 | return -EINVAL; | |
746 | ||
747 | memcpy(fsp, &rule->aq_fsp, sizeof(*fsp)); | |
748 | ||
749 | return 0; | |
750 | } | |
751 | ||
752 | int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd, | |
753 | u32 *rule_locs) | |
754 | { | |
755 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
756 | struct hlist_node *aq_node2; | |
757 | struct aq_rx_filter *rule; | |
758 | int count = 0; | |
759 | ||
760 | cmd->data = aq_get_rxnfc_count_all_rules(aq_nic); | |
761 | ||
762 | hlist_for_each_entry_safe(rule, aq_node2, | |
763 | &rx_fltrs->filter_list, aq_node) { | |
764 | if (unlikely(count == cmd->rule_cnt)) | |
765 | return -EMSGSIZE; | |
766 | ||
767 | rule_locs[count++] = rule->aq_fsp.location; | |
768 | } | |
769 | ||
770 | cmd->rule_cnt = count; | |
771 | ||
772 | return 0; | |
773 | } | |
774 | ||
775 | int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic) | |
776 | { | |
777 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
778 | struct hlist_node *aq_node2; | |
779 | struct aq_rx_filter *rule; | |
780 | int err = 0; | |
781 | ||
782 | hlist_for_each_entry_safe(rule, aq_node2, | |
783 | &rx_fltrs->filter_list, aq_node) { | |
784 | err = aq_add_del_rule(aq_nic, rule, false); | |
785 | if (err) | |
786 | goto err_exit; | |
787 | hlist_del(&rule->aq_node); | |
788 | kfree(rule); | |
789 | --rx_fltrs->active_filters; | |
790 | } | |
791 | ||
792 | err_exit: | |
793 | return err; | |
794 | } | |
795 | ||
796 | int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic) | |
797 | { | |
798 | struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); | |
799 | struct hlist_node *aq_node2; | |
800 | struct aq_rx_filter *rule; | |
801 | int err = 0; | |
802 | ||
803 | hlist_for_each_entry_safe(rule, aq_node2, | |
804 | &rx_fltrs->filter_list, aq_node) { | |
805 | err = aq_add_del_rule(aq_nic, rule, true); | |
806 | if (err) | |
807 | goto err_exit; | |
808 | } | |
809 | ||
810 | err_exit: | |
811 | return err; | |
812 | } | |
54bcb3d1 DB |
813 | |
814 | int aq_filters_vlans_update(struct aq_nic_s *aq_nic) | |
815 | { | |
816 | const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops; | |
817 | struct aq_hw_s *aq_hw = aq_nic->aq_hw; | |
7975d2af | 818 | int hweight = 0; |
54bcb3d1 | 819 | int err = 0; |
7975d2af | 820 | int i; |
54bcb3d1 DB |
821 | |
822 | if (unlikely(!aq_hw_ops->hw_filter_vlan_set)) | |
823 | return -EOPNOTSUPP; | |
7975d2af DB |
824 | if (unlikely(!aq_hw_ops->hw_filter_vlan_ctrl)) |
825 | return -EOPNOTSUPP; | |
826 | ||
827 | aq_fvlan_rebuild(aq_nic, aq_nic->active_vlans, | |
828 | aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans); | |
829 | ||
830 | if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) { | |
831 | for (i = 0; i < BITS_TO_LONGS(VLAN_N_VID); i++) | |
832 | hweight += hweight_long(aq_nic->active_vlans[i]); | |
833 | ||
834 | err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, false); | |
835 | if (err) | |
836 | return err; | |
837 | } | |
54bcb3d1 DB |
838 | |
839 | err = aq_hw_ops->hw_filter_vlan_set(aq_hw, | |
840 | aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans | |
841 | ); | |
7975d2af DB |
842 | if (err) |
843 | return err; | |
844 | ||
845 | if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) { | |
846 | if (hweight < AQ_VLAN_MAX_FILTERS) | |
847 | err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, true); | |
848 | /* otherwise left in promiscue mode */ | |
849 | } | |
850 | ||
851 | return err; | |
852 | } | |
54bcb3d1 | 853 | |
7975d2af DB |
854 | int aq_filters_vlan_offload_off(struct aq_nic_s *aq_nic) |
855 | { | |
856 | const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops; | |
857 | struct aq_hw_s *aq_hw = aq_nic->aq_hw; | |
858 | int err = 0; | |
859 | ||
860 | memset(aq_nic->active_vlans, 0, sizeof(aq_nic->active_vlans)); | |
861 | aq_fvlan_rebuild(aq_nic, aq_nic->active_vlans, | |
862 | aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans); | |
863 | ||
864 | if (unlikely(!aq_hw_ops->hw_filter_vlan_set)) | |
865 | return -EOPNOTSUPP; | |
866 | if (unlikely(!aq_hw_ops->hw_filter_vlan_ctrl)) | |
867 | return -EOPNOTSUPP; | |
868 | ||
869 | err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, false); | |
870 | if (err) | |
871 | return err; | |
872 | err = aq_hw_ops->hw_filter_vlan_set(aq_hw, | |
873 | aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans | |
874 | ); | |
54bcb3d1 DB |
875 | return err; |
876 | } |