1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) 2018-2020, Intel Corporation. */ |
3 | |
4 | #include "ice.h" |
5 | #include "ice_fltr.h" |
6 | |
7 | /** |
8 | * ice_fltr_free_list - free filter lists helper |
9 | * @dev: pointer to the device struct |
10 | * @h: pointer to the list head to be freed |
11 | * |
12 | * Helper function to free filter lists previously created using |
13 | * ice_fltr_add_mac_to_list |
14 | */ |
15 | void ice_fltr_free_list(struct device *dev, struct list_head *h) |
16 | { |
17 | struct ice_fltr_list_entry *e, *tmp; |
18 | |
19 | list_for_each_entry_safe(e, tmp, h, list_entry) { |
20 | list_del(entry: &e->list_entry); |
21 | devm_kfree(dev, p: e); |
22 | } |
23 | } |
24 | |
25 | /** |
26 | * ice_fltr_add_entry_to_list - allocate and add filter entry to list |
27 | * @dev: pointer to device needed by alloc function |
28 | * @info: filter info struct that gets added to the passed in list |
29 | * @list: pointer to the list which contains MAC filters entry |
30 | */ |
31 | static int |
32 | ice_fltr_add_entry_to_list(struct device *dev, struct ice_fltr_info *info, |
33 | struct list_head *list) |
34 | { |
35 | struct ice_fltr_list_entry *entry; |
36 | |
37 | entry = devm_kzalloc(dev, size: sizeof(*entry), GFP_ATOMIC); |
38 | if (!entry) |
39 | return -ENOMEM; |
40 | |
41 | entry->fltr_info = *info; |
42 | |
43 | INIT_LIST_HEAD(list: &entry->list_entry); |
44 | list_add(new: &entry->list_entry, head: list); |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | /** |
50 | * ice_fltr_set_vlan_vsi_promisc |
51 | * @hw: pointer to the hardware structure |
52 | * @vsi: the VSI being configured |
53 | * @promisc_mask: mask of promiscuous config bits |
54 | * |
55 | * Set VSI with all associated VLANs to given promiscuous mode(s) |
56 | */ |
57 | int |
58 | ice_fltr_set_vlan_vsi_promisc(struct ice_hw *hw, struct ice_vsi *vsi, |
59 | u8 promisc_mask) |
60 | { |
61 | struct ice_pf *pf = hw->back; |
62 | int result; |
63 | |
64 | result = ice_set_vlan_vsi_promisc(hw, vsi_handle: vsi->idx, promisc_mask, rm_vlan_promisc: false); |
65 | if (result && result != -EEXIST) |
66 | dev_err(ice_pf_to_dev(pf), |
67 | "Error setting promisc mode on VSI %i (rc=%d)\n" , |
68 | vsi->vsi_num, result); |
69 | |
70 | return result; |
71 | } |
72 | |
73 | /** |
74 | * ice_fltr_clear_vlan_vsi_promisc |
75 | * @hw: pointer to the hardware structure |
76 | * @vsi: the VSI being configured |
77 | * @promisc_mask: mask of promiscuous config bits |
78 | * |
79 | * Clear VSI with all associated VLANs to given promiscuous mode(s) |
80 | */ |
81 | int |
82 | ice_fltr_clear_vlan_vsi_promisc(struct ice_hw *hw, struct ice_vsi *vsi, |
83 | u8 promisc_mask) |
84 | { |
85 | struct ice_pf *pf = hw->back; |
86 | int result; |
87 | |
88 | result = ice_set_vlan_vsi_promisc(hw, vsi_handle: vsi->idx, promisc_mask, rm_vlan_promisc: true); |
89 | if (result && result != -EEXIST) |
90 | dev_err(ice_pf_to_dev(pf), |
91 | "Error clearing promisc mode on VSI %i (rc=%d)\n" , |
92 | vsi->vsi_num, result); |
93 | |
94 | return result; |
95 | } |
96 | |
97 | /** |
98 | * ice_fltr_clear_vsi_promisc - clear specified promiscuous mode(s) |
99 | * @hw: pointer to the hardware structure |
100 | * @vsi_handle: VSI handle to clear mode |
101 | * @promisc_mask: mask of promiscuous config bits to clear |
102 | * @vid: VLAN ID to clear VLAN promiscuous |
103 | */ |
104 | int |
105 | ice_fltr_clear_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask, |
106 | u16 vid) |
107 | { |
108 | struct ice_pf *pf = hw->back; |
109 | int result; |
110 | |
111 | result = ice_clear_vsi_promisc(hw, vsi_handle, promisc_mask, vid); |
112 | if (result && result != -EEXIST) |
113 | dev_err(ice_pf_to_dev(pf), |
114 | "Error clearing promisc mode on VSI %i for VID %u (rc=%d)\n" , |
115 | ice_get_hw_vsi_num(hw, vsi_handle), vid, result); |
116 | |
117 | return result; |
118 | } |
119 | |
120 | /** |
121 | * ice_fltr_set_vsi_promisc - set given VSI to given promiscuous mode(s) |
122 | * @hw: pointer to the hardware structure |
123 | * @vsi_handle: VSI handle to configure |
124 | * @promisc_mask: mask of promiscuous config bits |
125 | * @vid: VLAN ID to set VLAN promiscuous |
126 | */ |
127 | int |
128 | ice_fltr_set_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask, |
129 | u16 vid) |
130 | { |
131 | struct ice_pf *pf = hw->back; |
132 | int result; |
133 | |
134 | result = ice_set_vsi_promisc(hw, vsi_handle, promisc_mask, vid); |
135 | if (result && result != -EEXIST) |
136 | dev_err(ice_pf_to_dev(pf), |
137 | "Error setting promisc mode on VSI %i for VID %u (rc=%d)\n" , |
138 | ice_get_hw_vsi_num(hw, vsi_handle), vid, result); |
139 | |
140 | return result; |
141 | } |
142 | |
143 | /** |
144 | * ice_fltr_add_mac_list - add list of MAC filters |
145 | * @vsi: pointer to VSI struct |
146 | * @list: list of filters |
147 | */ |
148 | int ice_fltr_add_mac_list(struct ice_vsi *vsi, struct list_head *list) |
149 | { |
150 | return ice_add_mac(hw: &vsi->back->hw, m_lst: list); |
151 | } |
152 | |
153 | /** |
154 | * ice_fltr_remove_mac_list - remove list of MAC filters |
155 | * @vsi: pointer to VSI struct |
156 | * @list: list of filters |
157 | */ |
158 | int ice_fltr_remove_mac_list(struct ice_vsi *vsi, struct list_head *list) |
159 | { |
160 | return ice_remove_mac(hw: &vsi->back->hw, m_lst: list); |
161 | } |
162 | |
163 | /** |
164 | * ice_fltr_add_vlan_list - add list of VLAN filters |
165 | * @vsi: pointer to VSI struct |
166 | * @list: list of filters |
167 | */ |
168 | static int ice_fltr_add_vlan_list(struct ice_vsi *vsi, struct list_head *list) |
169 | { |
170 | return ice_add_vlan(hw: &vsi->back->hw, m_list: list); |
171 | } |
172 | |
173 | /** |
174 | * ice_fltr_remove_vlan_list - remove list of VLAN filters |
175 | * @vsi: pointer to VSI struct |
176 | * @list: list of filters |
177 | */ |
178 | static int |
179 | ice_fltr_remove_vlan_list(struct ice_vsi *vsi, struct list_head *list) |
180 | { |
181 | return ice_remove_vlan(hw: &vsi->back->hw, v_list: list); |
182 | } |
183 | |
184 | /** |
185 | * ice_fltr_add_eth_list - add list of ethertype filters |
186 | * @vsi: pointer to VSI struct |
187 | * @list: list of filters |
188 | */ |
189 | static int ice_fltr_add_eth_list(struct ice_vsi *vsi, struct list_head *list) |
190 | { |
191 | return ice_add_eth_mac(hw: &vsi->back->hw, em_list: list); |
192 | } |
193 | |
194 | /** |
195 | * ice_fltr_remove_eth_list - remove list of ethertype filters |
196 | * @vsi: pointer to VSI struct |
197 | * @list: list of filters |
198 | */ |
199 | static int ice_fltr_remove_eth_list(struct ice_vsi *vsi, struct list_head *list) |
200 | { |
201 | return ice_remove_eth_mac(hw: &vsi->back->hw, em_list: list); |
202 | } |
203 | |
204 | /** |
205 | * ice_fltr_remove_all - remove all filters associated with VSI |
206 | * @vsi: pointer to VSI struct |
207 | */ |
208 | void ice_fltr_remove_all(struct ice_vsi *vsi) |
209 | { |
210 | ice_remove_vsi_fltr(hw: &vsi->back->hw, vsi_handle: vsi->idx); |
211 | /* sync netdev filters if exist */ |
212 | if (vsi->netdev) { |
213 | __dev_uc_unsync(dev: vsi->netdev, NULL); |
214 | __dev_mc_unsync(dev: vsi->netdev, NULL); |
215 | } |
216 | } |
217 | |
218 | /** |
219 | * ice_fltr_add_mac_to_list - add MAC filter info to exsisting list |
220 | * @vsi: pointer to VSI struct |
221 | * @list: list to add filter info to |
222 | * @mac: MAC address to add |
223 | * @action: filter action |
224 | */ |
225 | int |
226 | ice_fltr_add_mac_to_list(struct ice_vsi *vsi, struct list_head *list, |
227 | const u8 *mac, enum ice_sw_fwd_act_type action) |
228 | { |
229 | struct ice_fltr_info info = { 0 }; |
230 | |
231 | info.flag = ICE_FLTR_TX; |
232 | info.src_id = ICE_SRC_ID_VSI; |
233 | info.lkup_type = ICE_SW_LKUP_MAC; |
234 | info.fltr_act = action; |
235 | info.vsi_handle = vsi->idx; |
236 | |
237 | ether_addr_copy(dst: info.l_data.mac.mac_addr, src: mac); |
238 | |
239 | return ice_fltr_add_entry_to_list(ice_pf_to_dev(vsi->back), info: &info, |
240 | list); |
241 | } |
242 | |
243 | /** |
244 | * ice_fltr_add_vlan_to_list - add VLAN filter info to exsisting list |
245 | * @vsi: pointer to VSI struct |
246 | * @list: list to add filter info to |
247 | * @vlan: VLAN filter details |
248 | */ |
249 | static int |
250 | ice_fltr_add_vlan_to_list(struct ice_vsi *vsi, struct list_head *list, |
251 | struct ice_vlan *vlan) |
252 | { |
253 | struct ice_fltr_info info = { 0 }; |
254 | |
255 | info.flag = ICE_FLTR_TX; |
256 | info.src_id = ICE_SRC_ID_VSI; |
257 | info.lkup_type = ICE_SW_LKUP_VLAN; |
258 | info.fltr_act = ICE_FWD_TO_VSI; |
259 | info.vsi_handle = vsi->idx; |
260 | info.l_data.vlan.vlan_id = vlan->vid; |
261 | info.l_data.vlan.tpid = vlan->tpid; |
262 | info.l_data.vlan.tpid_valid = true; |
263 | |
264 | return ice_fltr_add_entry_to_list(ice_pf_to_dev(vsi->back), info: &info, |
265 | list); |
266 | } |
267 | |
268 | /** |
269 | * ice_fltr_add_eth_to_list - add ethertype filter info to exsisting list |
270 | * @vsi: pointer to VSI struct |
271 | * @list: list to add filter info to |
272 | * @ethertype: ethertype of packet that matches filter |
273 | * @flag: filter direction, Tx or Rx |
274 | * @action: filter action |
275 | */ |
276 | static int |
277 | ice_fltr_add_eth_to_list(struct ice_vsi *vsi, struct list_head *list, |
278 | u16 ethertype, u16 flag, |
279 | enum ice_sw_fwd_act_type action) |
280 | { |
281 | struct ice_fltr_info info = { 0 }; |
282 | |
283 | info.flag = flag; |
284 | info.lkup_type = ICE_SW_LKUP_ETHERTYPE; |
285 | info.fltr_act = action; |
286 | info.vsi_handle = vsi->idx; |
287 | info.l_data.ethertype_mac.ethertype = ethertype; |
288 | |
289 | if (flag == ICE_FLTR_TX) |
290 | info.src_id = ICE_SRC_ID_VSI; |
291 | else |
292 | info.src_id = ICE_SRC_ID_LPORT; |
293 | |
294 | return ice_fltr_add_entry_to_list(ice_pf_to_dev(vsi->back), info: &info, |
295 | list); |
296 | } |
297 | |
298 | /** |
299 | * ice_fltr_prepare_mac - add or remove MAC rule |
300 | * @vsi: pointer to VSI struct |
301 | * @mac: MAC address to add |
302 | * @action: action to be performed on filter match |
303 | * @mac_action: pointer to add or remove MAC function |
304 | */ |
305 | static int |
306 | ice_fltr_prepare_mac(struct ice_vsi *vsi, const u8 *mac, |
307 | enum ice_sw_fwd_act_type action, |
308 | int (*mac_action)(struct ice_vsi *, struct list_head *)) |
309 | { |
310 | LIST_HEAD(tmp_list); |
311 | int result; |
312 | |
313 | if (ice_fltr_add_mac_to_list(vsi, list: &tmp_list, mac, action)) { |
314 | ice_fltr_free_list(ice_pf_to_dev(vsi->back), h: &tmp_list); |
315 | return -ENOMEM; |
316 | } |
317 | |
318 | result = mac_action(vsi, &tmp_list); |
319 | ice_fltr_free_list(ice_pf_to_dev(vsi->back), h: &tmp_list); |
320 | return result; |
321 | } |
322 | |
323 | /** |
324 | * ice_fltr_prepare_mac_and_broadcast - add or remove MAC and broadcast filter |
325 | * @vsi: pointer to VSI struct |
326 | * @mac: MAC address to add |
327 | * @action: action to be performed on filter match |
328 | * @mac_action: pointer to add or remove MAC function |
329 | */ |
330 | static int |
331 | ice_fltr_prepare_mac_and_broadcast(struct ice_vsi *vsi, const u8 *mac, |
332 | enum ice_sw_fwd_act_type action, |
333 | int(*mac_action) |
334 | (struct ice_vsi *, struct list_head *)) |
335 | { |
336 | u8 broadcast[ETH_ALEN]; |
337 | LIST_HEAD(tmp_list); |
338 | int result; |
339 | |
340 | eth_broadcast_addr(addr: broadcast); |
341 | if (ice_fltr_add_mac_to_list(vsi, list: &tmp_list, mac, action) || |
342 | ice_fltr_add_mac_to_list(vsi, list: &tmp_list, mac: broadcast, action)) { |
343 | ice_fltr_free_list(ice_pf_to_dev(vsi->back), h: &tmp_list); |
344 | return -ENOMEM; |
345 | } |
346 | |
347 | result = mac_action(vsi, &tmp_list); |
348 | ice_fltr_free_list(ice_pf_to_dev(vsi->back), h: &tmp_list); |
349 | return result; |
350 | } |
351 | |
352 | /** |
353 | * ice_fltr_prepare_vlan - add or remove VLAN filter |
354 | * @vsi: pointer to VSI struct |
355 | * @vlan: VLAN filter details |
356 | * @vlan_action: pointer to add or remove VLAN function |
357 | */ |
358 | static int |
359 | ice_fltr_prepare_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan, |
360 | int (*vlan_action)(struct ice_vsi *, struct list_head *)) |
361 | { |
362 | LIST_HEAD(tmp_list); |
363 | int result; |
364 | |
365 | if (ice_fltr_add_vlan_to_list(vsi, list: &tmp_list, vlan)) |
366 | return -ENOMEM; |
367 | |
368 | result = vlan_action(vsi, &tmp_list); |
369 | ice_fltr_free_list(ice_pf_to_dev(vsi->back), h: &tmp_list); |
370 | return result; |
371 | } |
372 | |
373 | /** |
374 | * ice_fltr_prepare_eth - add or remove ethertype filter |
375 | * @vsi: pointer to VSI struct |
376 | * @ethertype: ethertype of packet to be filtered |
377 | * @flag: direction of packet, Tx or Rx |
378 | * @action: action to be performed on filter match |
379 | * @eth_action: pointer to add or remove ethertype function |
380 | */ |
381 | static int |
382 | ice_fltr_prepare_eth(struct ice_vsi *vsi, u16 ethertype, u16 flag, |
383 | enum ice_sw_fwd_act_type action, |
384 | int (*eth_action)(struct ice_vsi *, struct list_head *)) |
385 | { |
386 | LIST_HEAD(tmp_list); |
387 | int result; |
388 | |
389 | if (ice_fltr_add_eth_to_list(vsi, list: &tmp_list, ethertype, flag, action)) |
390 | return -ENOMEM; |
391 | |
392 | result = eth_action(vsi, &tmp_list); |
393 | ice_fltr_free_list(ice_pf_to_dev(vsi->back), h: &tmp_list); |
394 | return result; |
395 | } |
396 | |
397 | /** |
398 | * ice_fltr_add_mac - add single MAC filter |
399 | * @vsi: pointer to VSI struct |
400 | * @mac: MAC to add |
401 | * @action: action to be performed on filter match |
402 | */ |
403 | int ice_fltr_add_mac(struct ice_vsi *vsi, const u8 *mac, |
404 | enum ice_sw_fwd_act_type action) |
405 | { |
406 | return ice_fltr_prepare_mac(vsi, mac, action, mac_action: ice_fltr_add_mac_list); |
407 | } |
408 | |
409 | /** |
410 | * ice_fltr_add_mac_and_broadcast - add single MAC and broadcast |
411 | * @vsi: pointer to VSI struct |
412 | * @mac: MAC to add |
413 | * @action: action to be performed on filter match |
414 | */ |
415 | int |
416 | ice_fltr_add_mac_and_broadcast(struct ice_vsi *vsi, const u8 *mac, |
417 | enum ice_sw_fwd_act_type action) |
418 | { |
419 | return ice_fltr_prepare_mac_and_broadcast(vsi, mac, action, |
420 | mac_action: ice_fltr_add_mac_list); |
421 | } |
422 | |
423 | /** |
424 | * ice_fltr_remove_mac - remove MAC filter |
425 | * @vsi: pointer to VSI struct |
426 | * @mac: filter MAC to remove |
427 | * @action: action to remove |
428 | */ |
429 | int ice_fltr_remove_mac(struct ice_vsi *vsi, const u8 *mac, |
430 | enum ice_sw_fwd_act_type action) |
431 | { |
432 | return ice_fltr_prepare_mac(vsi, mac, action, mac_action: ice_fltr_remove_mac_list); |
433 | } |
434 | |
435 | /** |
436 | * ice_fltr_add_vlan - add single VLAN filter |
437 | * @vsi: pointer to VSI struct |
438 | * @vlan: VLAN filter details |
439 | */ |
440 | int ice_fltr_add_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) |
441 | { |
442 | return ice_fltr_prepare_vlan(vsi, vlan, vlan_action: ice_fltr_add_vlan_list); |
443 | } |
444 | |
445 | /** |
446 | * ice_fltr_remove_vlan - remove VLAN filter |
447 | * @vsi: pointer to VSI struct |
448 | * @vlan: VLAN filter details |
449 | */ |
450 | int ice_fltr_remove_vlan(struct ice_vsi *vsi, struct ice_vlan *vlan) |
451 | { |
452 | return ice_fltr_prepare_vlan(vsi, vlan, vlan_action: ice_fltr_remove_vlan_list); |
453 | } |
454 | |
455 | /** |
456 | * ice_fltr_add_eth - add specyfic ethertype filter |
457 | * @vsi: pointer to VSI struct |
458 | * @ethertype: ethertype of filter |
459 | * @flag: direction of packet to be filtered, Tx or Rx |
460 | * @action: action to be performed on filter match |
461 | */ |
462 | int ice_fltr_add_eth(struct ice_vsi *vsi, u16 ethertype, u16 flag, |
463 | enum ice_sw_fwd_act_type action) |
464 | { |
465 | return ice_fltr_prepare_eth(vsi, ethertype, flag, action, |
466 | eth_action: ice_fltr_add_eth_list); |
467 | } |
468 | |
469 | /** |
470 | * ice_fltr_remove_eth - remove ethertype filter |
471 | * @vsi: pointer to VSI struct |
472 | * @ethertype: ethertype of filter |
473 | * @flag: direction of filter |
474 | * @action: action to remove |
475 | */ |
476 | int ice_fltr_remove_eth(struct ice_vsi *vsi, u16 ethertype, u16 flag, |
477 | enum ice_sw_fwd_act_type action) |
478 | { |
479 | return ice_fltr_prepare_eth(vsi, ethertype, flag, action, |
480 | eth_action: ice_fltr_remove_eth_list); |
481 | } |
482 | |