1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019, Intel Corporation. */ |
3 | |
4 | #include "ice_common.h" |
5 | #include "ice_flex_pipe.h" |
6 | #include "ice_flow.h" |
7 | #include "ice.h" |
8 | |
9 | static const u32 ice_sect_lkup[ICE_BLK_COUNT][ICE_SECT_COUNT] = { |
10 | /* SWITCH */ |
11 | { |
12 | ICE_SID_XLT0_SW, |
13 | ICE_SID_XLT_KEY_BUILDER_SW, |
14 | ICE_SID_XLT1_SW, |
15 | ICE_SID_XLT2_SW, |
16 | ICE_SID_PROFID_TCAM_SW, |
17 | ICE_SID_PROFID_REDIR_SW, |
18 | ICE_SID_FLD_VEC_SW, |
19 | ICE_SID_CDID_KEY_BUILDER_SW, |
20 | ICE_SID_CDID_REDIR_SW |
21 | }, |
22 | |
23 | /* ACL */ |
24 | { |
25 | ICE_SID_XLT0_ACL, |
26 | ICE_SID_XLT_KEY_BUILDER_ACL, |
27 | ICE_SID_XLT1_ACL, |
28 | ICE_SID_XLT2_ACL, |
29 | ICE_SID_PROFID_TCAM_ACL, |
30 | ICE_SID_PROFID_REDIR_ACL, |
31 | ICE_SID_FLD_VEC_ACL, |
32 | ICE_SID_CDID_KEY_BUILDER_ACL, |
33 | ICE_SID_CDID_REDIR_ACL |
34 | }, |
35 | |
36 | /* FD */ |
37 | { |
38 | ICE_SID_XLT0_FD, |
39 | ICE_SID_XLT_KEY_BUILDER_FD, |
40 | ICE_SID_XLT1_FD, |
41 | ICE_SID_XLT2_FD, |
42 | ICE_SID_PROFID_TCAM_FD, |
43 | ICE_SID_PROFID_REDIR_FD, |
44 | ICE_SID_FLD_VEC_FD, |
45 | ICE_SID_CDID_KEY_BUILDER_FD, |
46 | ICE_SID_CDID_REDIR_FD |
47 | }, |
48 | |
49 | /* RSS */ |
50 | { |
51 | ICE_SID_XLT0_RSS, |
52 | ICE_SID_XLT_KEY_BUILDER_RSS, |
53 | ICE_SID_XLT1_RSS, |
54 | ICE_SID_XLT2_RSS, |
55 | ICE_SID_PROFID_TCAM_RSS, |
56 | ICE_SID_PROFID_REDIR_RSS, |
57 | ICE_SID_FLD_VEC_RSS, |
58 | ICE_SID_CDID_KEY_BUILDER_RSS, |
59 | ICE_SID_CDID_REDIR_RSS |
60 | }, |
61 | |
62 | /* PE */ |
63 | { |
64 | ICE_SID_XLT0_PE, |
65 | ICE_SID_XLT_KEY_BUILDER_PE, |
66 | ICE_SID_XLT1_PE, |
67 | ICE_SID_XLT2_PE, |
68 | ICE_SID_PROFID_TCAM_PE, |
69 | ICE_SID_PROFID_REDIR_PE, |
70 | ICE_SID_FLD_VEC_PE, |
71 | ICE_SID_CDID_KEY_BUILDER_PE, |
72 | ICE_SID_CDID_REDIR_PE |
73 | } |
74 | }; |
75 | |
76 | /** |
77 | * ice_sect_id - returns section ID |
78 | * @blk: block type |
79 | * @sect: section type |
80 | * |
81 | * This helper function returns the proper section ID given a block type and a |
82 | * section type. |
83 | */ |
84 | static u32 ice_sect_id(enum ice_block blk, enum ice_sect sect) |
85 | { |
86 | return ice_sect_lkup[blk][sect]; |
87 | } |
88 | |
89 | /** |
90 | * ice_hw_ptype_ena - check if the PTYPE is enabled or not |
91 | * @hw: pointer to the HW structure |
92 | * @ptype: the hardware PTYPE |
93 | */ |
94 | bool ice_hw_ptype_ena(struct ice_hw *hw, u16 ptype) |
95 | { |
96 | return ptype < ICE_FLOW_PTYPE_MAX && |
97 | test_bit(ptype, hw->hw_ptype); |
98 | } |
99 | |
100 | /* Key creation */ |
101 | |
102 | #define ICE_DC_KEY 0x1 /* don't care */ |
103 | #define ICE_DC_KEYINV 0x1 |
104 | #define ICE_NM_KEY 0x0 /* never match */ |
105 | #define ICE_NM_KEYINV 0x0 |
106 | #define ICE_0_KEY 0x1 /* match 0 */ |
107 | #define ICE_0_KEYINV 0x0 |
108 | #define ICE_1_KEY 0x0 /* match 1 */ |
109 | #define ICE_1_KEYINV 0x1 |
110 | |
111 | /** |
112 | * ice_gen_key_word - generate 16-bits of a key/mask word |
113 | * @val: the value |
114 | * @valid: valid bits mask (change only the valid bits) |
115 | * @dont_care: don't care mask |
116 | * @nvr_mtch: never match mask |
117 | * @key: pointer to an array of where the resulting key portion |
118 | * @key_inv: pointer to an array of where the resulting key invert portion |
119 | * |
120 | * This function generates 16-bits from a 8-bit value, an 8-bit don't care mask |
121 | * and an 8-bit never match mask. The 16-bits of output are divided into 8 bits |
122 | * of key and 8 bits of key invert. |
123 | * |
124 | * '0' = b01, always match a 0 bit |
125 | * '1' = b10, always match a 1 bit |
126 | * '?' = b11, don't care bit (always matches) |
127 | * '~' = b00, never match bit |
128 | * |
129 | * Input: |
130 | * val: b0 1 0 1 0 1 |
131 | * dont_care: b0 0 1 1 0 0 |
132 | * never_mtch: b0 0 0 0 1 1 |
133 | * ------------------------------ |
134 | * Result: key: b01 10 11 11 00 00 |
135 | */ |
136 | static int |
137 | ice_gen_key_word(u8 val, u8 valid, u8 dont_care, u8 nvr_mtch, u8 *key, |
138 | u8 *key_inv) |
139 | { |
140 | u8 in_key = *key, in_key_inv = *key_inv; |
141 | u8 i; |
142 | |
143 | /* 'dont_care' and 'nvr_mtch' masks cannot overlap */ |
144 | if ((dont_care ^ nvr_mtch) != (dont_care | nvr_mtch)) |
145 | return -EIO; |
146 | |
147 | *key = 0; |
148 | *key_inv = 0; |
149 | |
150 | /* encode the 8 bits into 8-bit key and 8-bit key invert */ |
151 | for (i = 0; i < 8; i++) { |
152 | *key >>= 1; |
153 | *key_inv >>= 1; |
154 | |
155 | if (!(valid & 0x1)) { /* change only valid bits */ |
156 | *key |= (in_key & 0x1) << 7; |
157 | *key_inv |= (in_key_inv & 0x1) << 7; |
158 | } else if (dont_care & 0x1) { /* don't care bit */ |
159 | *key |= ICE_DC_KEY << 7; |
160 | *key_inv |= ICE_DC_KEYINV << 7; |
161 | } else if (nvr_mtch & 0x1) { /* never match bit */ |
162 | *key |= ICE_NM_KEY << 7; |
163 | *key_inv |= ICE_NM_KEYINV << 7; |
164 | } else if (val & 0x01) { /* exact 1 match */ |
165 | *key |= ICE_1_KEY << 7; |
166 | *key_inv |= ICE_1_KEYINV << 7; |
167 | } else { /* exact 0 match */ |
168 | *key |= ICE_0_KEY << 7; |
169 | *key_inv |= ICE_0_KEYINV << 7; |
170 | } |
171 | |
172 | dont_care >>= 1; |
173 | nvr_mtch >>= 1; |
174 | valid >>= 1; |
175 | val >>= 1; |
176 | in_key >>= 1; |
177 | in_key_inv >>= 1; |
178 | } |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | /** |
184 | * ice_bits_max_set - determine if the number of bits set is within a maximum |
185 | * @mask: pointer to the byte array which is the mask |
186 | * @size: the number of bytes in the mask |
187 | * @max: the max number of set bits |
188 | * |
189 | * This function determines if there are at most 'max' number of bits set in an |
190 | * array. Returns true if the number for bits set is <= max or will return false |
191 | * otherwise. |
192 | */ |
193 | static bool ice_bits_max_set(const u8 *mask, u16 size, u16 max) |
194 | { |
195 | u16 count = 0; |
196 | u16 i; |
197 | |
198 | /* check each byte */ |
199 | for (i = 0; i < size; i++) { |
200 | /* if 0, go to next byte */ |
201 | if (!mask[i]) |
202 | continue; |
203 | |
204 | /* We know there is at least one set bit in this byte because of |
205 | * the above check; if we already have found 'max' number of |
206 | * bits set, then we can return failure now. |
207 | */ |
208 | if (count == max) |
209 | return false; |
210 | |
211 | /* count the bits in this byte, checking threshold */ |
212 | count += hweight8(mask[i]); |
213 | if (count > max) |
214 | return false; |
215 | } |
216 | |
217 | return true; |
218 | } |
219 | |
220 | /** |
221 | * ice_set_key - generate a variable sized key with multiples of 16-bits |
222 | * @key: pointer to where the key will be stored |
223 | * @size: the size of the complete key in bytes (must be even) |
224 | * @val: array of 8-bit values that makes up the value portion of the key |
225 | * @upd: array of 8-bit masks that determine what key portion to update |
226 | * @dc: array of 8-bit masks that make up the don't care mask |
227 | * @nm: array of 8-bit masks that make up the never match mask |
228 | * @off: the offset of the first byte in the key to update |
229 | * @len: the number of bytes in the key update |
230 | * |
231 | * This function generates a key from a value, a don't care mask and a never |
232 | * match mask. |
233 | * upd, dc, and nm are optional parameters, and can be NULL: |
234 | * upd == NULL --> upd mask is all 1's (update all bits) |
235 | * dc == NULL --> dc mask is all 0's (no don't care bits) |
236 | * nm == NULL --> nm mask is all 0's (no never match bits) |
237 | */ |
238 | static int |
239 | ice_set_key(u8 *key, u16 size, u8 *val, u8 *upd, u8 *dc, u8 *nm, u16 off, |
240 | u16 len) |
241 | { |
242 | u16 half_size; |
243 | u16 i; |
244 | |
245 | /* size must be a multiple of 2 bytes. */ |
246 | if (size % 2) |
247 | return -EIO; |
248 | |
249 | half_size = size / 2; |
250 | if (off + len > half_size) |
251 | return -EIO; |
252 | |
253 | /* Make sure at most one bit is set in the never match mask. Having more |
254 | * than one never match mask bit set will cause HW to consume excessive |
255 | * power otherwise; this is a power management efficiency check. |
256 | */ |
257 | #define ICE_NVR_MTCH_BITS_MAX 1 |
258 | if (nm && !ice_bits_max_set(mask: nm, size: len, ICE_NVR_MTCH_BITS_MAX)) |
259 | return -EIO; |
260 | |
261 | for (i = 0; i < len; i++) |
262 | if (ice_gen_key_word(val: val[i], valid: upd ? upd[i] : 0xff, |
263 | dont_care: dc ? dc[i] : 0, nvr_mtch: nm ? nm[i] : 0, |
264 | key: key + off + i, key_inv: key + half_size + off + i)) |
265 | return -EIO; |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | /** |
271 | * ice_acquire_change_lock |
272 | * @hw: pointer to the HW structure |
273 | * @access: access type (read or write) |
274 | * |
275 | * This function will request ownership of the change lock. |
276 | */ |
277 | int |
278 | ice_acquire_change_lock(struct ice_hw *hw, enum ice_aq_res_access_type access) |
279 | { |
280 | return ice_acquire_res(hw, res: ICE_CHANGE_LOCK_RES_ID, access, |
281 | ICE_CHANGE_LOCK_TIMEOUT); |
282 | } |
283 | |
284 | /** |
285 | * ice_release_change_lock |
286 | * @hw: pointer to the HW structure |
287 | * |
288 | * This function will release the change lock using the proper Admin Command. |
289 | */ |
290 | void ice_release_change_lock(struct ice_hw *hw) |
291 | { |
292 | ice_release_res(hw, res: ICE_CHANGE_LOCK_RES_ID); |
293 | } |
294 | |
295 | /** |
296 | * ice_get_open_tunnel_port - retrieve an open tunnel port |
297 | * @hw: pointer to the HW structure |
298 | * @port: returns open port |
299 | * @type: type of tunnel, can be TNL_LAST if it doesn't matter |
300 | */ |
301 | bool |
302 | ice_get_open_tunnel_port(struct ice_hw *hw, u16 *port, |
303 | enum ice_tunnel_type type) |
304 | { |
305 | bool res = false; |
306 | u16 i; |
307 | |
308 | mutex_lock(&hw->tnl_lock); |
309 | |
310 | for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++) |
311 | if (hw->tnl.tbl[i].valid && hw->tnl.tbl[i].port && |
312 | (type == TNL_LAST || type == hw->tnl.tbl[i].type)) { |
313 | *port = hw->tnl.tbl[i].port; |
314 | res = true; |
315 | break; |
316 | } |
317 | |
318 | mutex_unlock(lock: &hw->tnl_lock); |
319 | |
320 | return res; |
321 | } |
322 | |
323 | /** |
324 | * ice_upd_dvm_boost_entry |
325 | * @hw: pointer to the HW structure |
326 | * @entry: pointer to double vlan boost entry info |
327 | */ |
328 | static int |
329 | ice_upd_dvm_boost_entry(struct ice_hw *hw, struct ice_dvm_entry *entry) |
330 | { |
331 | struct ice_boost_tcam_section *sect_rx, *sect_tx; |
332 | int status = -ENOSPC; |
333 | struct ice_buf_build *bld; |
334 | u8 val, dc, nm; |
335 | |
336 | bld = ice_pkg_buf_alloc(hw); |
337 | if (!bld) |
338 | return -ENOMEM; |
339 | |
340 | /* allocate 2 sections, one for Rx parser, one for Tx parser */ |
341 | if (ice_pkg_buf_reserve_section(bld, count: 2)) |
342 | goto ice_upd_dvm_boost_entry_err; |
343 | |
344 | sect_rx = ice_pkg_buf_alloc_section(bld, ICE_SID_RXPARSER_BOOST_TCAM, |
345 | struct_size(sect_rx, tcam, 1)); |
346 | if (!sect_rx) |
347 | goto ice_upd_dvm_boost_entry_err; |
348 | sect_rx->count = cpu_to_le16(1); |
349 | |
350 | sect_tx = ice_pkg_buf_alloc_section(bld, ICE_SID_TXPARSER_BOOST_TCAM, |
351 | struct_size(sect_tx, tcam, 1)); |
352 | if (!sect_tx) |
353 | goto ice_upd_dvm_boost_entry_err; |
354 | sect_tx->count = cpu_to_le16(1); |
355 | |
356 | /* copy original boost entry to update package buffer */ |
357 | memcpy(sect_rx->tcam, entry->boost_entry, sizeof(*sect_rx->tcam)); |
358 | |
359 | /* re-write the don't care and never match bits accordingly */ |
360 | if (entry->enable) { |
361 | /* all bits are don't care */ |
362 | val = 0x00; |
363 | dc = 0xFF; |
364 | nm = 0x00; |
365 | } else { |
366 | /* disable, one never match bit, the rest are don't care */ |
367 | val = 0x00; |
368 | dc = 0xF7; |
369 | nm = 0x08; |
370 | } |
371 | |
372 | ice_set_key(key: (u8 *)§_rx->tcam[0].key, size: sizeof(sect_rx->tcam[0].key), |
373 | val: &val, NULL, dc: &dc, nm: &nm, off: 0, len: sizeof(u8)); |
374 | |
375 | /* exact copy of entry to Tx section entry */ |
376 | memcpy(sect_tx->tcam, sect_rx->tcam, sizeof(*sect_tx->tcam)); |
377 | |
378 | status = ice_update_pkg_no_lock(hw, bufs: ice_pkg_buf(bld), count: 1); |
379 | |
380 | ice_upd_dvm_boost_entry_err: |
381 | ice_pkg_buf_free(hw, bld); |
382 | |
383 | return status; |
384 | } |
385 | |
386 | /** |
387 | * ice_set_dvm_boost_entries |
388 | * @hw: pointer to the HW structure |
389 | * |
390 | * Enable double vlan by updating the appropriate boost tcam entries. |
391 | */ |
392 | int ice_set_dvm_boost_entries(struct ice_hw *hw) |
393 | { |
394 | u16 i; |
395 | |
396 | for (i = 0; i < hw->dvm_upd.count; i++) { |
397 | int status; |
398 | |
399 | status = ice_upd_dvm_boost_entry(hw, entry: &hw->dvm_upd.tbl[i]); |
400 | if (status) |
401 | return status; |
402 | } |
403 | |
404 | return 0; |
405 | } |
406 | |
407 | /** |
408 | * ice_tunnel_idx_to_entry - convert linear index to the sparse one |
409 | * @hw: pointer to the HW structure |
410 | * @type: type of tunnel |
411 | * @idx: linear index |
412 | * |
413 | * Stack assumes we have 2 linear tables with indexes [0, count_valid), |
414 | * but really the port table may be sprase, and types are mixed, so convert |
415 | * the stack index into the device index. |
416 | */ |
417 | static u16 ice_tunnel_idx_to_entry(struct ice_hw *hw, enum ice_tunnel_type type, |
418 | u16 idx) |
419 | { |
420 | u16 i; |
421 | |
422 | for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++) |
423 | if (hw->tnl.tbl[i].valid && |
424 | hw->tnl.tbl[i].type == type && |
425 | idx-- == 0) |
426 | return i; |
427 | |
428 | WARN_ON_ONCE(1); |
429 | return 0; |
430 | } |
431 | |
432 | /** |
433 | * ice_create_tunnel |
434 | * @hw: pointer to the HW structure |
435 | * @index: device table entry |
436 | * @type: type of tunnel |
437 | * @port: port of tunnel to create |
438 | * |
439 | * Create a tunnel by updating the parse graph in the parser. We do that by |
440 | * creating a package buffer with the tunnel info and issuing an update package |
441 | * command. |
442 | */ |
443 | static int |
444 | ice_create_tunnel(struct ice_hw *hw, u16 index, |
445 | enum ice_tunnel_type type, u16 port) |
446 | { |
447 | struct ice_boost_tcam_section *sect_rx, *sect_tx; |
448 | struct ice_buf_build *bld; |
449 | int status = -ENOSPC; |
450 | |
451 | mutex_lock(&hw->tnl_lock); |
452 | |
453 | bld = ice_pkg_buf_alloc(hw); |
454 | if (!bld) { |
455 | status = -ENOMEM; |
456 | goto ice_create_tunnel_end; |
457 | } |
458 | |
459 | /* allocate 2 sections, one for Rx parser, one for Tx parser */ |
460 | if (ice_pkg_buf_reserve_section(bld, count: 2)) |
461 | goto ice_create_tunnel_err; |
462 | |
463 | sect_rx = ice_pkg_buf_alloc_section(bld, ICE_SID_RXPARSER_BOOST_TCAM, |
464 | struct_size(sect_rx, tcam, 1)); |
465 | if (!sect_rx) |
466 | goto ice_create_tunnel_err; |
467 | sect_rx->count = cpu_to_le16(1); |
468 | |
469 | sect_tx = ice_pkg_buf_alloc_section(bld, ICE_SID_TXPARSER_BOOST_TCAM, |
470 | struct_size(sect_tx, tcam, 1)); |
471 | if (!sect_tx) |
472 | goto ice_create_tunnel_err; |
473 | sect_tx->count = cpu_to_le16(1); |
474 | |
475 | /* copy original boost entry to update package buffer */ |
476 | memcpy(sect_rx->tcam, hw->tnl.tbl[index].boost_entry, |
477 | sizeof(*sect_rx->tcam)); |
478 | |
479 | /* over-write the never-match dest port key bits with the encoded port |
480 | * bits |
481 | */ |
482 | ice_set_key(key: (u8 *)§_rx->tcam[0].key, size: sizeof(sect_rx->tcam[0].key), |
483 | val: (u8 *)&port, NULL, NULL, NULL, |
484 | off: (u16)offsetof(struct ice_boost_key_value, hv_dst_port_key), |
485 | len: sizeof(sect_rx->tcam[0].key.key.hv_dst_port_key)); |
486 | |
487 | /* exact copy of entry to Tx section entry */ |
488 | memcpy(sect_tx->tcam, sect_rx->tcam, sizeof(*sect_tx->tcam)); |
489 | |
490 | status = ice_update_pkg(hw, bufs: ice_pkg_buf(bld), count: 1); |
491 | if (!status) |
492 | hw->tnl.tbl[index].port = port; |
493 | |
494 | ice_create_tunnel_err: |
495 | ice_pkg_buf_free(hw, bld); |
496 | |
497 | ice_create_tunnel_end: |
498 | mutex_unlock(lock: &hw->tnl_lock); |
499 | |
500 | return status; |
501 | } |
502 | |
503 | /** |
504 | * ice_destroy_tunnel |
505 | * @hw: pointer to the HW structure |
506 | * @index: device table entry |
507 | * @type: type of tunnel |
508 | * @port: port of tunnel to destroy (ignored if the all parameter is true) |
509 | * |
510 | * Destroys a tunnel or all tunnels by creating an update package buffer |
511 | * targeting the specific updates requested and then performing an update |
512 | * package. |
513 | */ |
514 | static int |
515 | ice_destroy_tunnel(struct ice_hw *hw, u16 index, enum ice_tunnel_type type, |
516 | u16 port) |
517 | { |
518 | struct ice_boost_tcam_section *sect_rx, *sect_tx; |
519 | struct ice_buf_build *bld; |
520 | int status = -ENOSPC; |
521 | |
522 | mutex_lock(&hw->tnl_lock); |
523 | |
524 | if (WARN_ON(!hw->tnl.tbl[index].valid || |
525 | hw->tnl.tbl[index].type != type || |
526 | hw->tnl.tbl[index].port != port)) { |
527 | status = -EIO; |
528 | goto ice_destroy_tunnel_end; |
529 | } |
530 | |
531 | bld = ice_pkg_buf_alloc(hw); |
532 | if (!bld) { |
533 | status = -ENOMEM; |
534 | goto ice_destroy_tunnel_end; |
535 | } |
536 | |
537 | /* allocate 2 sections, one for Rx parser, one for Tx parser */ |
538 | if (ice_pkg_buf_reserve_section(bld, count: 2)) |
539 | goto ice_destroy_tunnel_err; |
540 | |
541 | sect_rx = ice_pkg_buf_alloc_section(bld, ICE_SID_RXPARSER_BOOST_TCAM, |
542 | struct_size(sect_rx, tcam, 1)); |
543 | if (!sect_rx) |
544 | goto ice_destroy_tunnel_err; |
545 | sect_rx->count = cpu_to_le16(1); |
546 | |
547 | sect_tx = ice_pkg_buf_alloc_section(bld, ICE_SID_TXPARSER_BOOST_TCAM, |
548 | struct_size(sect_tx, tcam, 1)); |
549 | if (!sect_tx) |
550 | goto ice_destroy_tunnel_err; |
551 | sect_tx->count = cpu_to_le16(1); |
552 | |
553 | /* copy original boost entry to update package buffer, one copy to Rx |
554 | * section, another copy to the Tx section |
555 | */ |
556 | memcpy(sect_rx->tcam, hw->tnl.tbl[index].boost_entry, |
557 | sizeof(*sect_rx->tcam)); |
558 | memcpy(sect_tx->tcam, hw->tnl.tbl[index].boost_entry, |
559 | sizeof(*sect_tx->tcam)); |
560 | |
561 | status = ice_update_pkg(hw, bufs: ice_pkg_buf(bld), count: 1); |
562 | if (!status) |
563 | hw->tnl.tbl[index].port = 0; |
564 | |
565 | ice_destroy_tunnel_err: |
566 | ice_pkg_buf_free(hw, bld); |
567 | |
568 | ice_destroy_tunnel_end: |
569 | mutex_unlock(lock: &hw->tnl_lock); |
570 | |
571 | return status; |
572 | } |
573 | |
574 | int ice_udp_tunnel_set_port(struct net_device *netdev, unsigned int table, |
575 | unsigned int idx, struct udp_tunnel_info *ti) |
576 | { |
577 | struct ice_netdev_priv *np = netdev_priv(dev: netdev); |
578 | struct ice_vsi *vsi = np->vsi; |
579 | struct ice_pf *pf = vsi->back; |
580 | enum ice_tunnel_type tnl_type; |
581 | int status; |
582 | u16 index; |
583 | |
584 | tnl_type = ti->type == UDP_TUNNEL_TYPE_VXLAN ? TNL_VXLAN : TNL_GENEVE; |
585 | index = ice_tunnel_idx_to_entry(hw: &pf->hw, type: tnl_type, idx); |
586 | |
587 | status = ice_create_tunnel(hw: &pf->hw, index, type: tnl_type, ntohs(ti->port)); |
588 | if (status) { |
589 | netdev_err(dev: netdev, format: "Error adding UDP tunnel - %d\n" , |
590 | status); |
591 | return -EIO; |
592 | } |
593 | |
594 | udp_tunnel_nic_set_port_priv(dev: netdev, table, idx, priv: index); |
595 | return 0; |
596 | } |
597 | |
598 | int ice_udp_tunnel_unset_port(struct net_device *netdev, unsigned int table, |
599 | unsigned int idx, struct udp_tunnel_info *ti) |
600 | { |
601 | struct ice_netdev_priv *np = netdev_priv(dev: netdev); |
602 | struct ice_vsi *vsi = np->vsi; |
603 | struct ice_pf *pf = vsi->back; |
604 | enum ice_tunnel_type tnl_type; |
605 | int status; |
606 | |
607 | tnl_type = ti->type == UDP_TUNNEL_TYPE_VXLAN ? TNL_VXLAN : TNL_GENEVE; |
608 | |
609 | status = ice_destroy_tunnel(hw: &pf->hw, index: ti->hw_priv, type: tnl_type, |
610 | ntohs(ti->port)); |
611 | if (status) { |
612 | netdev_err(dev: netdev, format: "Error removing UDP tunnel - %d\n" , |
613 | status); |
614 | return -EIO; |
615 | } |
616 | |
617 | return 0; |
618 | } |
619 | |
620 | /** |
621 | * ice_find_prot_off - find prot ID and offset pair, based on prof and FV index |
622 | * @hw: pointer to the hardware structure |
623 | * @blk: hardware block |
624 | * @prof: profile ID |
625 | * @fv_idx: field vector word index |
626 | * @prot: variable to receive the protocol ID |
627 | * @off: variable to receive the protocol offset |
628 | */ |
629 | int |
630 | ice_find_prot_off(struct ice_hw *hw, enum ice_block blk, u8 prof, u16 fv_idx, |
631 | u8 *prot, u16 *off) |
632 | { |
633 | struct ice_fv_word *fv_ext; |
634 | |
635 | if (prof >= hw->blk[blk].es.count) |
636 | return -EINVAL; |
637 | |
638 | if (fv_idx >= hw->blk[blk].es.fvw) |
639 | return -EINVAL; |
640 | |
641 | fv_ext = hw->blk[blk].es.t + (prof * hw->blk[blk].es.fvw); |
642 | |
643 | *prot = fv_ext[fv_idx].prot_id; |
644 | *off = fv_ext[fv_idx].off; |
645 | |
646 | return 0; |
647 | } |
648 | |
649 | /* PTG Management */ |
650 | |
651 | /** |
652 | * ice_ptg_find_ptype - Search for packet type group using packet type (ptype) |
653 | * @hw: pointer to the hardware structure |
654 | * @blk: HW block |
655 | * @ptype: the ptype to search for |
656 | * @ptg: pointer to variable that receives the PTG |
657 | * |
658 | * This function will search the PTGs for a particular ptype, returning the |
659 | * PTG ID that contains it through the PTG parameter, with the value of |
660 | * ICE_DEFAULT_PTG (0) meaning it is part the default PTG. |
661 | */ |
662 | static int |
663 | ice_ptg_find_ptype(struct ice_hw *hw, enum ice_block blk, u16 ptype, u8 *ptg) |
664 | { |
665 | if (ptype >= ICE_XLT1_CNT || !ptg) |
666 | return -EINVAL; |
667 | |
668 | *ptg = hw->blk[blk].xlt1.ptypes[ptype].ptg; |
669 | return 0; |
670 | } |
671 | |
672 | /** |
673 | * ice_ptg_alloc_val - Allocates a new packet type group ID by value |
674 | * @hw: pointer to the hardware structure |
675 | * @blk: HW block |
676 | * @ptg: the PTG to allocate |
677 | * |
678 | * This function allocates a given packet type group ID specified by the PTG |
679 | * parameter. |
680 | */ |
681 | static void ice_ptg_alloc_val(struct ice_hw *hw, enum ice_block blk, u8 ptg) |
682 | { |
683 | hw->blk[blk].xlt1.ptg_tbl[ptg].in_use = true; |
684 | } |
685 | |
686 | /** |
687 | * ice_ptg_remove_ptype - Removes ptype from a particular packet type group |
688 | * @hw: pointer to the hardware structure |
689 | * @blk: HW block |
690 | * @ptype: the ptype to remove |
691 | * @ptg: the PTG to remove the ptype from |
692 | * |
693 | * This function will remove the ptype from the specific PTG, and move it to |
694 | * the default PTG (ICE_DEFAULT_PTG). |
695 | */ |
696 | static int |
697 | ice_ptg_remove_ptype(struct ice_hw *hw, enum ice_block blk, u16 ptype, u8 ptg) |
698 | { |
699 | struct ice_ptg_ptype **ch; |
700 | struct ice_ptg_ptype *p; |
701 | |
702 | if (ptype > ICE_XLT1_CNT - 1) |
703 | return -EINVAL; |
704 | |
705 | if (!hw->blk[blk].xlt1.ptg_tbl[ptg].in_use) |
706 | return -ENOENT; |
707 | |
708 | /* Should not happen if .in_use is set, bad config */ |
709 | if (!hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype) |
710 | return -EIO; |
711 | |
712 | /* find the ptype within this PTG, and bypass the link over it */ |
713 | p = hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype; |
714 | ch = &hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype; |
715 | while (p) { |
716 | if (ptype == (p - hw->blk[blk].xlt1.ptypes)) { |
717 | *ch = p->next_ptype; |
718 | break; |
719 | } |
720 | |
721 | ch = &p->next_ptype; |
722 | p = p->next_ptype; |
723 | } |
724 | |
725 | hw->blk[blk].xlt1.ptypes[ptype].ptg = ICE_DEFAULT_PTG; |
726 | hw->blk[blk].xlt1.ptypes[ptype].next_ptype = NULL; |
727 | |
728 | return 0; |
729 | } |
730 | |
731 | /** |
732 | * ice_ptg_add_mv_ptype - Adds/moves ptype to a particular packet type group |
733 | * @hw: pointer to the hardware structure |
734 | * @blk: HW block |
735 | * @ptype: the ptype to add or move |
736 | * @ptg: the PTG to add or move the ptype to |
737 | * |
738 | * This function will either add or move a ptype to a particular PTG depending |
739 | * on if the ptype is already part of another group. Note that using a |
740 | * destination PTG ID of ICE_DEFAULT_PTG (0) will move the ptype to the |
741 | * default PTG. |
742 | */ |
743 | static int |
744 | ice_ptg_add_mv_ptype(struct ice_hw *hw, enum ice_block blk, u16 ptype, u8 ptg) |
745 | { |
746 | u8 original_ptg; |
747 | int status; |
748 | |
749 | if (ptype > ICE_XLT1_CNT - 1) |
750 | return -EINVAL; |
751 | |
752 | if (!hw->blk[blk].xlt1.ptg_tbl[ptg].in_use && ptg != ICE_DEFAULT_PTG) |
753 | return -ENOENT; |
754 | |
755 | status = ice_ptg_find_ptype(hw, blk, ptype, ptg: &original_ptg); |
756 | if (status) |
757 | return status; |
758 | |
759 | /* Is ptype already in the correct PTG? */ |
760 | if (original_ptg == ptg) |
761 | return 0; |
762 | |
763 | /* Remove from original PTG and move back to the default PTG */ |
764 | if (original_ptg != ICE_DEFAULT_PTG) |
765 | ice_ptg_remove_ptype(hw, blk, ptype, ptg: original_ptg); |
766 | |
767 | /* Moving to default PTG? Then we're done with this request */ |
768 | if (ptg == ICE_DEFAULT_PTG) |
769 | return 0; |
770 | |
771 | /* Add ptype to PTG at beginning of list */ |
772 | hw->blk[blk].xlt1.ptypes[ptype].next_ptype = |
773 | hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype; |
774 | hw->blk[blk].xlt1.ptg_tbl[ptg].first_ptype = |
775 | &hw->blk[blk].xlt1.ptypes[ptype]; |
776 | |
777 | hw->blk[blk].xlt1.ptypes[ptype].ptg = ptg; |
778 | hw->blk[blk].xlt1.t[ptype] = ptg; |
779 | |
780 | return 0; |
781 | } |
782 | |
783 | /* Block / table size info */ |
784 | struct ice_blk_size_details { |
785 | u16 xlt1; /* # XLT1 entries */ |
786 | u16 xlt2; /* # XLT2 entries */ |
787 | u16 prof_tcam; /* # profile ID TCAM entries */ |
788 | u16 prof_id; /* # profile IDs */ |
789 | u8 prof_cdid_bits; /* # CDID one-hot bits used in key */ |
790 | u16 prof_redir; /* # profile redirection entries */ |
791 | u16 es; /* # extraction sequence entries */ |
792 | u16 fvw; /* # field vector words */ |
793 | u8 overwrite; /* overwrite existing entries allowed */ |
794 | u8 reverse; /* reverse FV order */ |
795 | }; |
796 | |
797 | static const struct ice_blk_size_details blk_sizes[ICE_BLK_COUNT] = { |
798 | /** |
799 | * Table Definitions |
800 | * XLT1 - Number of entries in XLT1 table |
801 | * XLT2 - Number of entries in XLT2 table |
802 | * TCAM - Number of entries Profile ID TCAM table |
803 | * CDID - Control Domain ID of the hardware block |
804 | * PRED - Number of entries in the Profile Redirection Table |
805 | * FV - Number of entries in the Field Vector |
806 | * FVW - Width (in WORDs) of the Field Vector |
807 | * OVR - Overwrite existing table entries |
808 | * REV - Reverse FV |
809 | */ |
810 | /* XLT1 , XLT2 ,TCAM, PID,CDID,PRED, FV, FVW */ |
811 | /* Overwrite , Reverse FV */ |
812 | /* SW */ { ICE_XLT1_CNT, ICE_XLT2_CNT, 512, 256, 0, 256, 256, 48, |
813 | false, false }, |
814 | /* ACL */ { ICE_XLT1_CNT, ICE_XLT2_CNT, 512, 128, 0, 128, 128, 32, |
815 | false, false }, |
816 | /* FD */ { ICE_XLT1_CNT, ICE_XLT2_CNT, 512, 128, 0, 128, 128, 24, |
817 | false, true }, |
818 | /* RSS */ { ICE_XLT1_CNT, ICE_XLT2_CNT, 512, 128, 0, 128, 128, 24, |
819 | true, true }, |
820 | /* PE */ { ICE_XLT1_CNT, ICE_XLT2_CNT, 64, 32, 0, 32, 32, 24, |
821 | false, false }, |
822 | }; |
823 | |
824 | enum ice_sid_all { |
825 | ICE_SID_XLT1_OFF = 0, |
826 | ICE_SID_XLT2_OFF, |
827 | ICE_SID_PR_OFF, |
828 | ICE_SID_PR_REDIR_OFF, |
829 | ICE_SID_ES_OFF, |
830 | ICE_SID_OFF_COUNT, |
831 | }; |
832 | |
833 | /* Characteristic handling */ |
834 | |
835 | /** |
836 | * ice_match_prop_lst - determine if properties of two lists match |
837 | * @list1: first properties list |
838 | * @list2: second properties list |
839 | * |
840 | * Count, cookies and the order must match in order to be considered equivalent. |
841 | */ |
842 | static bool |
843 | ice_match_prop_lst(struct list_head *list1, struct list_head *list2) |
844 | { |
845 | struct ice_vsig_prof *tmp1; |
846 | struct ice_vsig_prof *tmp2; |
847 | u16 chk_count = 0; |
848 | u16 count = 0; |
849 | |
850 | /* compare counts */ |
851 | list_for_each_entry(tmp1, list1, list) |
852 | count++; |
853 | list_for_each_entry(tmp2, list2, list) |
854 | chk_count++; |
855 | if (!count || count != chk_count) |
856 | return false; |
857 | |
858 | tmp1 = list_first_entry(list1, struct ice_vsig_prof, list); |
859 | tmp2 = list_first_entry(list2, struct ice_vsig_prof, list); |
860 | |
861 | /* profile cookies must compare, and in the exact same order to take |
862 | * into account priority |
863 | */ |
864 | while (count--) { |
865 | if (tmp2->profile_cookie != tmp1->profile_cookie) |
866 | return false; |
867 | |
868 | tmp1 = list_next_entry(tmp1, list); |
869 | tmp2 = list_next_entry(tmp2, list); |
870 | } |
871 | |
872 | return true; |
873 | } |
874 | |
875 | /* VSIG Management */ |
876 | |
877 | /** |
878 | * ice_vsig_find_vsi - find a VSIG that contains a specified VSI |
879 | * @hw: pointer to the hardware structure |
880 | * @blk: HW block |
881 | * @vsi: VSI of interest |
882 | * @vsig: pointer to receive the VSI group |
883 | * |
884 | * This function will lookup the VSI entry in the XLT2 list and return |
885 | * the VSI group its associated with. |
886 | */ |
887 | static int |
888 | ice_vsig_find_vsi(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 *vsig) |
889 | { |
890 | if (!vsig || vsi >= ICE_MAX_VSI) |
891 | return -EINVAL; |
892 | |
893 | /* As long as there's a default or valid VSIG associated with the input |
894 | * VSI, the functions returns a success. Any handling of VSIG will be |
895 | * done by the following add, update or remove functions. |
896 | */ |
897 | *vsig = hw->blk[blk].xlt2.vsis[vsi].vsig; |
898 | |
899 | return 0; |
900 | } |
901 | |
902 | /** |
903 | * ice_vsig_alloc_val - allocate a new VSIG by value |
904 | * @hw: pointer to the hardware structure |
905 | * @blk: HW block |
906 | * @vsig: the VSIG to allocate |
907 | * |
908 | * This function will allocate a given VSIG specified by the VSIG parameter. |
909 | */ |
910 | static u16 ice_vsig_alloc_val(struct ice_hw *hw, enum ice_block blk, u16 vsig) |
911 | { |
912 | u16 idx = vsig & ICE_VSIG_IDX_M; |
913 | |
914 | if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) { |
915 | INIT_LIST_HEAD(list: &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst); |
916 | hw->blk[blk].xlt2.vsig_tbl[idx].in_use = true; |
917 | } |
918 | |
919 | return ICE_VSIG_VALUE(idx, hw->pf_id); |
920 | } |
921 | |
922 | /** |
923 | * ice_vsig_alloc - Finds a free entry and allocates a new VSIG |
924 | * @hw: pointer to the hardware structure |
925 | * @blk: HW block |
926 | * |
927 | * This function will iterate through the VSIG list and mark the first |
928 | * unused entry for the new VSIG entry as used and return that value. |
929 | */ |
930 | static u16 ice_vsig_alloc(struct ice_hw *hw, enum ice_block blk) |
931 | { |
932 | u16 i; |
933 | |
934 | for (i = 1; i < ICE_MAX_VSIGS; i++) |
935 | if (!hw->blk[blk].xlt2.vsig_tbl[i].in_use) |
936 | return ice_vsig_alloc_val(hw, blk, vsig: i); |
937 | |
938 | return ICE_DEFAULT_VSIG; |
939 | } |
940 | |
941 | /** |
942 | * ice_find_dup_props_vsig - find VSI group with a specified set of properties |
943 | * @hw: pointer to the hardware structure |
944 | * @blk: HW block |
945 | * @chs: characteristic list |
946 | * @vsig: returns the VSIG with the matching profiles, if found |
947 | * |
948 | * Each VSIG is associated with a characteristic set; i.e. all VSIs under |
949 | * a group have the same characteristic set. To check if there exists a VSIG |
950 | * which has the same characteristics as the input characteristics; this |
951 | * function will iterate through the XLT2 list and return the VSIG that has a |
952 | * matching configuration. In order to make sure that priorities are accounted |
953 | * for, the list must match exactly, including the order in which the |
954 | * characteristics are listed. |
955 | */ |
956 | static int |
957 | ice_find_dup_props_vsig(struct ice_hw *hw, enum ice_block blk, |
958 | struct list_head *chs, u16 *vsig) |
959 | { |
960 | struct ice_xlt2 *xlt2 = &hw->blk[blk].xlt2; |
961 | u16 i; |
962 | |
963 | for (i = 0; i < xlt2->count; i++) |
964 | if (xlt2->vsig_tbl[i].in_use && |
965 | ice_match_prop_lst(list1: chs, list2: &xlt2->vsig_tbl[i].prop_lst)) { |
966 | *vsig = ICE_VSIG_VALUE(i, hw->pf_id); |
967 | return 0; |
968 | } |
969 | |
970 | return -ENOENT; |
971 | } |
972 | |
973 | /** |
974 | * ice_vsig_free - free VSI group |
975 | * @hw: pointer to the hardware structure |
976 | * @blk: HW block |
977 | * @vsig: VSIG to remove |
978 | * |
979 | * The function will remove all VSIs associated with the input VSIG and move |
980 | * them to the DEFAULT_VSIG and mark the VSIG available. |
981 | */ |
982 | static int ice_vsig_free(struct ice_hw *hw, enum ice_block blk, u16 vsig) |
983 | { |
984 | struct ice_vsig_prof *dtmp, *del; |
985 | struct ice_vsig_vsi *vsi_cur; |
986 | u16 idx; |
987 | |
988 | idx = vsig & ICE_VSIG_IDX_M; |
989 | if (idx >= ICE_MAX_VSIGS) |
990 | return -EINVAL; |
991 | |
992 | if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) |
993 | return -ENOENT; |
994 | |
995 | hw->blk[blk].xlt2.vsig_tbl[idx].in_use = false; |
996 | |
997 | vsi_cur = hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; |
998 | /* If the VSIG has at least 1 VSI then iterate through the |
999 | * list and remove the VSIs before deleting the group. |
1000 | */ |
1001 | if (vsi_cur) { |
1002 | /* remove all vsis associated with this VSIG XLT2 entry */ |
1003 | do { |
1004 | struct ice_vsig_vsi *tmp = vsi_cur->next_vsi; |
1005 | |
1006 | vsi_cur->vsig = ICE_DEFAULT_VSIG; |
1007 | vsi_cur->changed = 1; |
1008 | vsi_cur->next_vsi = NULL; |
1009 | vsi_cur = tmp; |
1010 | } while (vsi_cur); |
1011 | |
1012 | /* NULL terminate head of VSI list */ |
1013 | hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi = NULL; |
1014 | } |
1015 | |
1016 | /* free characteristic list */ |
1017 | list_for_each_entry_safe(del, dtmp, |
1018 | &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
1019 | list) { |
1020 | list_del(entry: &del->list); |
1021 | devm_kfree(dev: ice_hw_to_dev(hw), p: del); |
1022 | } |
1023 | |
1024 | /* if VSIG characteristic list was cleared for reset |
1025 | * re-initialize the list head |
1026 | */ |
1027 | INIT_LIST_HEAD(list: &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst); |
1028 | |
1029 | return 0; |
1030 | } |
1031 | |
1032 | /** |
1033 | * ice_vsig_remove_vsi - remove VSI from VSIG |
1034 | * @hw: pointer to the hardware structure |
1035 | * @blk: HW block |
1036 | * @vsi: VSI to remove |
1037 | * @vsig: VSI group to remove from |
1038 | * |
1039 | * The function will remove the input VSI from its VSI group and move it |
1040 | * to the DEFAULT_VSIG. |
1041 | */ |
1042 | static int |
1043 | ice_vsig_remove_vsi(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 vsig) |
1044 | { |
1045 | struct ice_vsig_vsi **vsi_head, *vsi_cur, *vsi_tgt; |
1046 | u16 idx; |
1047 | |
1048 | idx = vsig & ICE_VSIG_IDX_M; |
1049 | |
1050 | if (vsi >= ICE_MAX_VSI || idx >= ICE_MAX_VSIGS) |
1051 | return -EINVAL; |
1052 | |
1053 | if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) |
1054 | return -ENOENT; |
1055 | |
1056 | /* entry already in default VSIG, don't have to remove */ |
1057 | if (idx == ICE_DEFAULT_VSIG) |
1058 | return 0; |
1059 | |
1060 | vsi_head = &hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; |
1061 | if (!(*vsi_head)) |
1062 | return -EIO; |
1063 | |
1064 | vsi_tgt = &hw->blk[blk].xlt2.vsis[vsi]; |
1065 | vsi_cur = (*vsi_head); |
1066 | |
1067 | /* iterate the VSI list, skip over the entry to be removed */ |
1068 | while (vsi_cur) { |
1069 | if (vsi_tgt == vsi_cur) { |
1070 | (*vsi_head) = vsi_cur->next_vsi; |
1071 | break; |
1072 | } |
1073 | vsi_head = &vsi_cur->next_vsi; |
1074 | vsi_cur = vsi_cur->next_vsi; |
1075 | } |
1076 | |
1077 | /* verify if VSI was removed from group list */ |
1078 | if (!vsi_cur) |
1079 | return -ENOENT; |
1080 | |
1081 | vsi_cur->vsig = ICE_DEFAULT_VSIG; |
1082 | vsi_cur->changed = 1; |
1083 | vsi_cur->next_vsi = NULL; |
1084 | |
1085 | return 0; |
1086 | } |
1087 | |
1088 | /** |
1089 | * ice_vsig_add_mv_vsi - add or move a VSI to a VSI group |
1090 | * @hw: pointer to the hardware structure |
1091 | * @blk: HW block |
1092 | * @vsi: VSI to move |
1093 | * @vsig: destination VSI group |
1094 | * |
1095 | * This function will move or add the input VSI to the target VSIG. |
1096 | * The function will find the original VSIG the VSI belongs to and |
1097 | * move the entry to the DEFAULT_VSIG, update the original VSIG and |
1098 | * then move entry to the new VSIG. |
1099 | */ |
1100 | static int |
1101 | ice_vsig_add_mv_vsi(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 vsig) |
1102 | { |
1103 | struct ice_vsig_vsi *tmp; |
1104 | u16 orig_vsig, idx; |
1105 | int status; |
1106 | |
1107 | idx = vsig & ICE_VSIG_IDX_M; |
1108 | |
1109 | if (vsi >= ICE_MAX_VSI || idx >= ICE_MAX_VSIGS) |
1110 | return -EINVAL; |
1111 | |
1112 | /* if VSIG not in use and VSIG is not default type this VSIG |
1113 | * doesn't exist. |
1114 | */ |
1115 | if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use && |
1116 | vsig != ICE_DEFAULT_VSIG) |
1117 | return -ENOENT; |
1118 | |
1119 | status = ice_vsig_find_vsi(hw, blk, vsi, vsig: &orig_vsig); |
1120 | if (status) |
1121 | return status; |
1122 | |
1123 | /* no update required if vsigs match */ |
1124 | if (orig_vsig == vsig) |
1125 | return 0; |
1126 | |
1127 | if (orig_vsig != ICE_DEFAULT_VSIG) { |
1128 | /* remove entry from orig_vsig and add to default VSIG */ |
1129 | status = ice_vsig_remove_vsi(hw, blk, vsi, vsig: orig_vsig); |
1130 | if (status) |
1131 | return status; |
1132 | } |
1133 | |
1134 | if (idx == ICE_DEFAULT_VSIG) |
1135 | return 0; |
1136 | |
1137 | /* Create VSI entry and add VSIG and prop_mask values */ |
1138 | hw->blk[blk].xlt2.vsis[vsi].vsig = vsig; |
1139 | hw->blk[blk].xlt2.vsis[vsi].changed = 1; |
1140 | |
1141 | /* Add new entry to the head of the VSIG list */ |
1142 | tmp = hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; |
1143 | hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi = |
1144 | &hw->blk[blk].xlt2.vsis[vsi]; |
1145 | hw->blk[blk].xlt2.vsis[vsi].next_vsi = tmp; |
1146 | hw->blk[blk].xlt2.t[vsi] = vsig; |
1147 | |
1148 | return 0; |
1149 | } |
1150 | |
1151 | /** |
1152 | * ice_prof_has_mask_idx - determine if profile index masking is identical |
1153 | * @hw: pointer to the hardware structure |
1154 | * @blk: HW block |
1155 | * @prof: profile to check |
1156 | * @idx: profile index to check |
1157 | * @mask: mask to match |
1158 | */ |
1159 | static bool |
1160 | ice_prof_has_mask_idx(struct ice_hw *hw, enum ice_block blk, u8 prof, u16 idx, |
1161 | u16 mask) |
1162 | { |
1163 | bool expect_no_mask = false; |
1164 | bool found = false; |
1165 | bool match = false; |
1166 | u16 i; |
1167 | |
1168 | /* If mask is 0x0000 or 0xffff, then there is no masking */ |
1169 | if (mask == 0 || mask == 0xffff) |
1170 | expect_no_mask = true; |
1171 | |
1172 | /* Scan the enabled masks on this profile, for the specified idx */ |
1173 | for (i = hw->blk[blk].masks.first; i < hw->blk[blk].masks.first + |
1174 | hw->blk[blk].masks.count; i++) |
1175 | if (hw->blk[blk].es.mask_ena[prof] & BIT(i)) |
1176 | if (hw->blk[blk].masks.masks[i].in_use && |
1177 | hw->blk[blk].masks.masks[i].idx == idx) { |
1178 | found = true; |
1179 | if (hw->blk[blk].masks.masks[i].mask == mask) |
1180 | match = true; |
1181 | break; |
1182 | } |
1183 | |
1184 | if (expect_no_mask) { |
1185 | if (found) |
1186 | return false; |
1187 | } else { |
1188 | if (!match) |
1189 | return false; |
1190 | } |
1191 | |
1192 | return true; |
1193 | } |
1194 | |
1195 | /** |
1196 | * ice_prof_has_mask - determine if profile masking is identical |
1197 | * @hw: pointer to the hardware structure |
1198 | * @blk: HW block |
1199 | * @prof: profile to check |
1200 | * @masks: masks to match |
1201 | */ |
1202 | static bool |
1203 | ice_prof_has_mask(struct ice_hw *hw, enum ice_block blk, u8 prof, u16 *masks) |
1204 | { |
1205 | u16 i; |
1206 | |
1207 | /* es->mask_ena[prof] will have the mask */ |
1208 | for (i = 0; i < hw->blk[blk].es.fvw; i++) |
1209 | if (!ice_prof_has_mask_idx(hw, blk, prof, idx: i, mask: masks[i])) |
1210 | return false; |
1211 | |
1212 | return true; |
1213 | } |
1214 | |
1215 | /** |
1216 | * ice_find_prof_id_with_mask - find profile ID for a given field vector |
1217 | * @hw: pointer to the hardware structure |
1218 | * @blk: HW block |
1219 | * @fv: field vector to search for |
1220 | * @masks: masks for FV |
1221 | * @symm: symmetric setting for RSS flows |
1222 | * @prof_id: receives the profile ID |
1223 | */ |
1224 | static int |
1225 | ice_find_prof_id_with_mask(struct ice_hw *hw, enum ice_block blk, |
1226 | struct ice_fv_word *fv, u16 *masks, bool symm, |
1227 | u8 *prof_id) |
1228 | { |
1229 | struct ice_es *es = &hw->blk[blk].es; |
1230 | u8 i; |
1231 | |
1232 | /* For FD, we don't want to re-use a existed profile with the same |
1233 | * field vector and mask. This will cause rule interference. |
1234 | */ |
1235 | if (blk == ICE_BLK_FD) |
1236 | return -ENOENT; |
1237 | |
1238 | for (i = 0; i < (u8)es->count; i++) { |
1239 | u16 off = i * es->fvw; |
1240 | |
1241 | if (blk == ICE_BLK_RSS && es->symm[i] != symm) |
1242 | continue; |
1243 | |
1244 | if (memcmp(p: &es->t[off], q: fv, size: es->fvw * sizeof(*fv))) |
1245 | continue; |
1246 | |
1247 | /* check if masks settings are the same for this profile */ |
1248 | if (masks && !ice_prof_has_mask(hw, blk, prof: i, masks)) |
1249 | continue; |
1250 | |
1251 | *prof_id = i; |
1252 | return 0; |
1253 | } |
1254 | |
1255 | return -ENOENT; |
1256 | } |
1257 | |
1258 | /** |
1259 | * ice_prof_id_rsrc_type - get profile ID resource type for a block type |
1260 | * @blk: the block type |
1261 | * @rsrc_type: pointer to variable to receive the resource type |
1262 | */ |
1263 | static bool ice_prof_id_rsrc_type(enum ice_block blk, u16 *rsrc_type) |
1264 | { |
1265 | switch (blk) { |
1266 | case ICE_BLK_FD: |
1267 | *rsrc_type = ICE_AQC_RES_TYPE_FD_PROF_BLDR_PROFID; |
1268 | break; |
1269 | case ICE_BLK_RSS: |
1270 | *rsrc_type = ICE_AQC_RES_TYPE_HASH_PROF_BLDR_PROFID; |
1271 | break; |
1272 | default: |
1273 | return false; |
1274 | } |
1275 | return true; |
1276 | } |
1277 | |
1278 | /** |
1279 | * ice_tcam_ent_rsrc_type - get TCAM entry resource type for a block type |
1280 | * @blk: the block type |
1281 | * @rsrc_type: pointer to variable to receive the resource type |
1282 | */ |
1283 | static bool ice_tcam_ent_rsrc_type(enum ice_block blk, u16 *rsrc_type) |
1284 | { |
1285 | switch (blk) { |
1286 | case ICE_BLK_FD: |
1287 | *rsrc_type = ICE_AQC_RES_TYPE_FD_PROF_BLDR_TCAM; |
1288 | break; |
1289 | case ICE_BLK_RSS: |
1290 | *rsrc_type = ICE_AQC_RES_TYPE_HASH_PROF_BLDR_TCAM; |
1291 | break; |
1292 | default: |
1293 | return false; |
1294 | } |
1295 | return true; |
1296 | } |
1297 | |
1298 | /** |
1299 | * ice_alloc_tcam_ent - allocate hardware TCAM entry |
1300 | * @hw: pointer to the HW struct |
1301 | * @blk: the block to allocate the TCAM for |
1302 | * @btm: true to allocate from bottom of table, false to allocate from top |
1303 | * @tcam_idx: pointer to variable to receive the TCAM entry |
1304 | * |
1305 | * This function allocates a new entry in a Profile ID TCAM for a specific |
1306 | * block. |
1307 | */ |
1308 | static int |
1309 | ice_alloc_tcam_ent(struct ice_hw *hw, enum ice_block blk, bool btm, |
1310 | u16 *tcam_idx) |
1311 | { |
1312 | u16 res_type; |
1313 | |
1314 | if (!ice_tcam_ent_rsrc_type(blk, rsrc_type: &res_type)) |
1315 | return -EINVAL; |
1316 | |
1317 | return ice_alloc_hw_res(hw, type: res_type, num: 1, btm, res: tcam_idx); |
1318 | } |
1319 | |
1320 | /** |
1321 | * ice_free_tcam_ent - free hardware TCAM entry |
1322 | * @hw: pointer to the HW struct |
1323 | * @blk: the block from which to free the TCAM entry |
1324 | * @tcam_idx: the TCAM entry to free |
1325 | * |
1326 | * This function frees an entry in a Profile ID TCAM for a specific block. |
1327 | */ |
1328 | static int |
1329 | ice_free_tcam_ent(struct ice_hw *hw, enum ice_block blk, u16 tcam_idx) |
1330 | { |
1331 | u16 res_type; |
1332 | |
1333 | if (!ice_tcam_ent_rsrc_type(blk, rsrc_type: &res_type)) |
1334 | return -EINVAL; |
1335 | |
1336 | return ice_free_hw_res(hw, type: res_type, num: 1, res: &tcam_idx); |
1337 | } |
1338 | |
1339 | /** |
1340 | * ice_alloc_prof_id - allocate profile ID |
1341 | * @hw: pointer to the HW struct |
1342 | * @blk: the block to allocate the profile ID for |
1343 | * @prof_id: pointer to variable to receive the profile ID |
1344 | * |
1345 | * This function allocates a new profile ID, which also corresponds to a Field |
1346 | * Vector (Extraction Sequence) entry. |
1347 | */ |
1348 | static int ice_alloc_prof_id(struct ice_hw *hw, enum ice_block blk, u8 *prof_id) |
1349 | { |
1350 | u16 res_type; |
1351 | u16 get_prof; |
1352 | int status; |
1353 | |
1354 | if (!ice_prof_id_rsrc_type(blk, rsrc_type: &res_type)) |
1355 | return -EINVAL; |
1356 | |
1357 | status = ice_alloc_hw_res(hw, type: res_type, num: 1, btm: false, res: &get_prof); |
1358 | if (!status) |
1359 | *prof_id = (u8)get_prof; |
1360 | |
1361 | return status; |
1362 | } |
1363 | |
1364 | /** |
1365 | * ice_free_prof_id - free profile ID |
1366 | * @hw: pointer to the HW struct |
1367 | * @blk: the block from which to free the profile ID |
1368 | * @prof_id: the profile ID to free |
1369 | * |
1370 | * This function frees a profile ID, which also corresponds to a Field Vector. |
1371 | */ |
1372 | static int ice_free_prof_id(struct ice_hw *hw, enum ice_block blk, u8 prof_id) |
1373 | { |
1374 | u16 tmp_prof_id = (u16)prof_id; |
1375 | u16 res_type; |
1376 | |
1377 | if (!ice_prof_id_rsrc_type(blk, rsrc_type: &res_type)) |
1378 | return -EINVAL; |
1379 | |
1380 | return ice_free_hw_res(hw, type: res_type, num: 1, res: &tmp_prof_id); |
1381 | } |
1382 | |
1383 | /** |
1384 | * ice_prof_inc_ref - increment reference count for profile |
1385 | * @hw: pointer to the HW struct |
1386 | * @blk: the block from which to free the profile ID |
1387 | * @prof_id: the profile ID for which to increment the reference count |
1388 | */ |
1389 | static int ice_prof_inc_ref(struct ice_hw *hw, enum ice_block blk, u8 prof_id) |
1390 | { |
1391 | if (prof_id > hw->blk[blk].es.count) |
1392 | return -EINVAL; |
1393 | |
1394 | hw->blk[blk].es.ref_count[prof_id]++; |
1395 | |
1396 | return 0; |
1397 | } |
1398 | |
1399 | /** |
1400 | * ice_write_prof_mask_reg - write profile mask register |
1401 | * @hw: pointer to the HW struct |
1402 | * @blk: hardware block |
1403 | * @mask_idx: mask index |
1404 | * @idx: index of the FV which will use the mask |
1405 | * @mask: the 16-bit mask |
1406 | */ |
1407 | static void |
1408 | ice_write_prof_mask_reg(struct ice_hw *hw, enum ice_block blk, u16 mask_idx, |
1409 | u16 idx, u16 mask) |
1410 | { |
1411 | u32 offset; |
1412 | u32 val; |
1413 | |
1414 | switch (blk) { |
1415 | case ICE_BLK_RSS: |
1416 | offset = GLQF_HMASK(mask_idx); |
1417 | val = FIELD_PREP(GLQF_HMASK_MSK_INDEX_M, idx); |
1418 | val |= FIELD_PREP(GLQF_HMASK_MASK_M, mask); |
1419 | break; |
1420 | case ICE_BLK_FD: |
1421 | offset = GLQF_FDMASK(mask_idx); |
1422 | val = FIELD_PREP(GLQF_FDMASK_MSK_INDEX_M, idx); |
1423 | val |= FIELD_PREP(GLQF_FDMASK_MASK_M, mask); |
1424 | break; |
1425 | default: |
1426 | ice_debug(hw, ICE_DBG_PKG, "No profile masks for block %d\n" , |
1427 | blk); |
1428 | return; |
1429 | } |
1430 | |
1431 | wr32(hw, offset, val); |
1432 | ice_debug(hw, ICE_DBG_PKG, "write mask, blk %d (%d): %x = %x\n" , |
1433 | blk, idx, offset, val); |
1434 | } |
1435 | |
1436 | /** |
1437 | * ice_write_prof_mask_enable_res - write profile mask enable register |
1438 | * @hw: pointer to the HW struct |
1439 | * @blk: hardware block |
1440 | * @prof_id: profile ID |
1441 | * @enable_mask: enable mask |
1442 | */ |
1443 | static void |
1444 | ice_write_prof_mask_enable_res(struct ice_hw *hw, enum ice_block blk, |
1445 | u16 prof_id, u32 enable_mask) |
1446 | { |
1447 | u32 offset; |
1448 | |
1449 | switch (blk) { |
1450 | case ICE_BLK_RSS: |
1451 | offset = GLQF_HMASK_SEL(prof_id); |
1452 | break; |
1453 | case ICE_BLK_FD: |
1454 | offset = GLQF_FDMASK_SEL(prof_id); |
1455 | break; |
1456 | default: |
1457 | ice_debug(hw, ICE_DBG_PKG, "No profile masks for block %d\n" , |
1458 | blk); |
1459 | return; |
1460 | } |
1461 | |
1462 | wr32(hw, offset, enable_mask); |
1463 | ice_debug(hw, ICE_DBG_PKG, "write mask enable, blk %d (%d): %x = %x\n" , |
1464 | blk, prof_id, offset, enable_mask); |
1465 | } |
1466 | |
1467 | /** |
1468 | * ice_init_prof_masks - initial prof masks |
1469 | * @hw: pointer to the HW struct |
1470 | * @blk: hardware block |
1471 | */ |
1472 | static void ice_init_prof_masks(struct ice_hw *hw, enum ice_block blk) |
1473 | { |
1474 | u16 per_pf; |
1475 | u16 i; |
1476 | |
1477 | mutex_init(&hw->blk[blk].masks.lock); |
1478 | |
1479 | per_pf = ICE_PROF_MASK_COUNT / hw->dev_caps.num_funcs; |
1480 | |
1481 | hw->blk[blk].masks.count = per_pf; |
1482 | hw->blk[blk].masks.first = hw->pf_id * per_pf; |
1483 | |
1484 | memset(hw->blk[blk].masks.masks, 0, sizeof(hw->blk[blk].masks.masks)); |
1485 | |
1486 | for (i = hw->blk[blk].masks.first; |
1487 | i < hw->blk[blk].masks.first + hw->blk[blk].masks.count; i++) |
1488 | ice_write_prof_mask_reg(hw, blk, mask_idx: i, idx: 0, mask: 0); |
1489 | } |
1490 | |
1491 | /** |
1492 | * ice_init_all_prof_masks - initialize all prof masks |
1493 | * @hw: pointer to the HW struct |
1494 | */ |
1495 | static void ice_init_all_prof_masks(struct ice_hw *hw) |
1496 | { |
1497 | ice_init_prof_masks(hw, blk: ICE_BLK_RSS); |
1498 | ice_init_prof_masks(hw, blk: ICE_BLK_FD); |
1499 | } |
1500 | |
1501 | /** |
1502 | * ice_alloc_prof_mask - allocate profile mask |
1503 | * @hw: pointer to the HW struct |
1504 | * @blk: hardware block |
1505 | * @idx: index of FV which will use the mask |
1506 | * @mask: the 16-bit mask |
1507 | * @mask_idx: variable to receive the mask index |
1508 | */ |
1509 | static int |
1510 | ice_alloc_prof_mask(struct ice_hw *hw, enum ice_block blk, u16 idx, u16 mask, |
1511 | u16 *mask_idx) |
1512 | { |
1513 | bool found_unused = false, found_copy = false; |
1514 | u16 unused_idx = 0, copy_idx = 0; |
1515 | int status = -ENOSPC; |
1516 | u16 i; |
1517 | |
1518 | if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD) |
1519 | return -EINVAL; |
1520 | |
1521 | mutex_lock(&hw->blk[blk].masks.lock); |
1522 | |
1523 | for (i = hw->blk[blk].masks.first; |
1524 | i < hw->blk[blk].masks.first + hw->blk[blk].masks.count; i++) |
1525 | if (hw->blk[blk].masks.masks[i].in_use) { |
1526 | /* if mask is in use and it exactly duplicates the |
1527 | * desired mask and index, then in can be reused |
1528 | */ |
1529 | if (hw->blk[blk].masks.masks[i].mask == mask && |
1530 | hw->blk[blk].masks.masks[i].idx == idx) { |
1531 | found_copy = true; |
1532 | copy_idx = i; |
1533 | break; |
1534 | } |
1535 | } else { |
1536 | /* save off unused index, but keep searching in case |
1537 | * there is an exact match later on |
1538 | */ |
1539 | if (!found_unused) { |
1540 | found_unused = true; |
1541 | unused_idx = i; |
1542 | } |
1543 | } |
1544 | |
1545 | if (found_copy) |
1546 | i = copy_idx; |
1547 | else if (found_unused) |
1548 | i = unused_idx; |
1549 | else |
1550 | goto err_ice_alloc_prof_mask; |
1551 | |
1552 | /* update mask for a new entry */ |
1553 | if (found_unused) { |
1554 | hw->blk[blk].masks.masks[i].in_use = true; |
1555 | hw->blk[blk].masks.masks[i].mask = mask; |
1556 | hw->blk[blk].masks.masks[i].idx = idx; |
1557 | hw->blk[blk].masks.masks[i].ref = 0; |
1558 | ice_write_prof_mask_reg(hw, blk, mask_idx: i, idx, mask); |
1559 | } |
1560 | |
1561 | hw->blk[blk].masks.masks[i].ref++; |
1562 | *mask_idx = i; |
1563 | status = 0; |
1564 | |
1565 | err_ice_alloc_prof_mask: |
1566 | mutex_unlock(lock: &hw->blk[blk].masks.lock); |
1567 | |
1568 | return status; |
1569 | } |
1570 | |
1571 | /** |
1572 | * ice_free_prof_mask - free profile mask |
1573 | * @hw: pointer to the HW struct |
1574 | * @blk: hardware block |
1575 | * @mask_idx: index of mask |
1576 | */ |
1577 | static int |
1578 | ice_free_prof_mask(struct ice_hw *hw, enum ice_block blk, u16 mask_idx) |
1579 | { |
1580 | if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD) |
1581 | return -EINVAL; |
1582 | |
1583 | if (!(mask_idx >= hw->blk[blk].masks.first && |
1584 | mask_idx < hw->blk[blk].masks.first + hw->blk[blk].masks.count)) |
1585 | return -ENOENT; |
1586 | |
1587 | mutex_lock(&hw->blk[blk].masks.lock); |
1588 | |
1589 | if (!hw->blk[blk].masks.masks[mask_idx].in_use) |
1590 | goto exit_ice_free_prof_mask; |
1591 | |
1592 | if (hw->blk[blk].masks.masks[mask_idx].ref > 1) { |
1593 | hw->blk[blk].masks.masks[mask_idx].ref--; |
1594 | goto exit_ice_free_prof_mask; |
1595 | } |
1596 | |
1597 | /* remove mask */ |
1598 | hw->blk[blk].masks.masks[mask_idx].in_use = false; |
1599 | hw->blk[blk].masks.masks[mask_idx].mask = 0; |
1600 | hw->blk[blk].masks.masks[mask_idx].idx = 0; |
1601 | |
1602 | /* update mask as unused entry */ |
1603 | ice_debug(hw, ICE_DBG_PKG, "Free mask, blk %d, mask %d\n" , blk, |
1604 | mask_idx); |
1605 | ice_write_prof_mask_reg(hw, blk, mask_idx, idx: 0, mask: 0); |
1606 | |
1607 | exit_ice_free_prof_mask: |
1608 | mutex_unlock(lock: &hw->blk[blk].masks.lock); |
1609 | |
1610 | return 0; |
1611 | } |
1612 | |
1613 | /** |
1614 | * ice_free_prof_masks - free all profile masks for a profile |
1615 | * @hw: pointer to the HW struct |
1616 | * @blk: hardware block |
1617 | * @prof_id: profile ID |
1618 | */ |
1619 | static int |
1620 | ice_free_prof_masks(struct ice_hw *hw, enum ice_block blk, u16 prof_id) |
1621 | { |
1622 | u32 mask_bm; |
1623 | u16 i; |
1624 | |
1625 | if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD) |
1626 | return -EINVAL; |
1627 | |
1628 | mask_bm = hw->blk[blk].es.mask_ena[prof_id]; |
1629 | for (i = 0; i < BITS_PER_BYTE * sizeof(mask_bm); i++) |
1630 | if (mask_bm & BIT(i)) |
1631 | ice_free_prof_mask(hw, blk, mask_idx: i); |
1632 | |
1633 | return 0; |
1634 | } |
1635 | |
1636 | /** |
1637 | * ice_shutdown_prof_masks - releases lock for masking |
1638 | * @hw: pointer to the HW struct |
1639 | * @blk: hardware block |
1640 | * |
1641 | * This should be called before unloading the driver |
1642 | */ |
1643 | static void ice_shutdown_prof_masks(struct ice_hw *hw, enum ice_block blk) |
1644 | { |
1645 | u16 i; |
1646 | |
1647 | mutex_lock(&hw->blk[blk].masks.lock); |
1648 | |
1649 | for (i = hw->blk[blk].masks.first; |
1650 | i < hw->blk[blk].masks.first + hw->blk[blk].masks.count; i++) { |
1651 | ice_write_prof_mask_reg(hw, blk, mask_idx: i, idx: 0, mask: 0); |
1652 | |
1653 | hw->blk[blk].masks.masks[i].in_use = false; |
1654 | hw->blk[blk].masks.masks[i].idx = 0; |
1655 | hw->blk[blk].masks.masks[i].mask = 0; |
1656 | } |
1657 | |
1658 | mutex_unlock(lock: &hw->blk[blk].masks.lock); |
1659 | mutex_destroy(lock: &hw->blk[blk].masks.lock); |
1660 | } |
1661 | |
1662 | /** |
1663 | * ice_shutdown_all_prof_masks - releases all locks for masking |
1664 | * @hw: pointer to the HW struct |
1665 | * |
1666 | * This should be called before unloading the driver |
1667 | */ |
1668 | static void ice_shutdown_all_prof_masks(struct ice_hw *hw) |
1669 | { |
1670 | ice_shutdown_prof_masks(hw, blk: ICE_BLK_RSS); |
1671 | ice_shutdown_prof_masks(hw, blk: ICE_BLK_FD); |
1672 | } |
1673 | |
1674 | /** |
1675 | * ice_update_prof_masking - set registers according to masking |
1676 | * @hw: pointer to the HW struct |
1677 | * @blk: hardware block |
1678 | * @prof_id: profile ID |
1679 | * @masks: masks |
1680 | */ |
1681 | static int |
1682 | ice_update_prof_masking(struct ice_hw *hw, enum ice_block blk, u16 prof_id, |
1683 | u16 *masks) |
1684 | { |
1685 | bool err = false; |
1686 | u32 ena_mask = 0; |
1687 | u16 idx; |
1688 | u16 i; |
1689 | |
1690 | /* Only support FD and RSS masking, otherwise nothing to be done */ |
1691 | if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD) |
1692 | return 0; |
1693 | |
1694 | for (i = 0; i < hw->blk[blk].es.fvw; i++) |
1695 | if (masks[i] && masks[i] != 0xFFFF) { |
1696 | if (!ice_alloc_prof_mask(hw, blk, idx: i, mask: masks[i], mask_idx: &idx)) { |
1697 | ena_mask |= BIT(idx); |
1698 | } else { |
1699 | /* not enough bitmaps */ |
1700 | err = true; |
1701 | break; |
1702 | } |
1703 | } |
1704 | |
1705 | if (err) { |
1706 | /* free any bitmaps we have allocated */ |
1707 | for (i = 0; i < BITS_PER_BYTE * sizeof(ena_mask); i++) |
1708 | if (ena_mask & BIT(i)) |
1709 | ice_free_prof_mask(hw, blk, mask_idx: i); |
1710 | |
1711 | return -EIO; |
1712 | } |
1713 | |
1714 | /* enable the masks for this profile */ |
1715 | ice_write_prof_mask_enable_res(hw, blk, prof_id, enable_mask: ena_mask); |
1716 | |
1717 | /* store enabled masks with profile so that they can be freed later */ |
1718 | hw->blk[blk].es.mask_ena[prof_id] = ena_mask; |
1719 | |
1720 | return 0; |
1721 | } |
1722 | |
1723 | /** |
1724 | * ice_write_es - write an extraction sequence and symmetric setting to hardware |
1725 | * @hw: pointer to the HW struct |
1726 | * @blk: the block in which to write the extraction sequence |
1727 | * @prof_id: the profile ID to write |
1728 | * @fv: pointer to the extraction sequence to write - NULL to clear extraction |
1729 | * @symm: symmetric setting for RSS profiles |
1730 | */ |
1731 | static void |
1732 | ice_write_es(struct ice_hw *hw, enum ice_block blk, u8 prof_id, |
1733 | struct ice_fv_word *fv, bool symm) |
1734 | { |
1735 | u16 off; |
1736 | |
1737 | off = prof_id * hw->blk[blk].es.fvw; |
1738 | if (!fv) { |
1739 | memset(&hw->blk[blk].es.t[off], 0, |
1740 | hw->blk[blk].es.fvw * sizeof(*fv)); |
1741 | hw->blk[blk].es.written[prof_id] = false; |
1742 | } else { |
1743 | memcpy(&hw->blk[blk].es.t[off], fv, |
1744 | hw->blk[blk].es.fvw * sizeof(*fv)); |
1745 | } |
1746 | |
1747 | if (blk == ICE_BLK_RSS) |
1748 | hw->blk[blk].es.symm[prof_id] = symm; |
1749 | } |
1750 | |
1751 | /** |
1752 | * ice_prof_dec_ref - decrement reference count for profile |
1753 | * @hw: pointer to the HW struct |
1754 | * @blk: the block from which to free the profile ID |
1755 | * @prof_id: the profile ID for which to decrement the reference count |
1756 | */ |
1757 | static int |
1758 | ice_prof_dec_ref(struct ice_hw *hw, enum ice_block blk, u8 prof_id) |
1759 | { |
1760 | if (prof_id > hw->blk[blk].es.count) |
1761 | return -EINVAL; |
1762 | |
1763 | if (hw->blk[blk].es.ref_count[prof_id] > 0) { |
1764 | if (!--hw->blk[blk].es.ref_count[prof_id]) { |
1765 | ice_write_es(hw, blk, prof_id, NULL, symm: false); |
1766 | ice_free_prof_masks(hw, blk, prof_id); |
1767 | return ice_free_prof_id(hw, blk, prof_id); |
1768 | } |
1769 | } |
1770 | |
1771 | return 0; |
1772 | } |
1773 | |
1774 | /* Block / table section IDs */ |
1775 | static const u32 ice_blk_sids[ICE_BLK_COUNT][ICE_SID_OFF_COUNT] = { |
1776 | /* SWITCH */ |
1777 | { ICE_SID_XLT1_SW, |
1778 | ICE_SID_XLT2_SW, |
1779 | ICE_SID_PROFID_TCAM_SW, |
1780 | ICE_SID_PROFID_REDIR_SW, |
1781 | ICE_SID_FLD_VEC_SW |
1782 | }, |
1783 | |
1784 | /* ACL */ |
1785 | { ICE_SID_XLT1_ACL, |
1786 | ICE_SID_XLT2_ACL, |
1787 | ICE_SID_PROFID_TCAM_ACL, |
1788 | ICE_SID_PROFID_REDIR_ACL, |
1789 | ICE_SID_FLD_VEC_ACL |
1790 | }, |
1791 | |
1792 | /* FD */ |
1793 | { ICE_SID_XLT1_FD, |
1794 | ICE_SID_XLT2_FD, |
1795 | ICE_SID_PROFID_TCAM_FD, |
1796 | ICE_SID_PROFID_REDIR_FD, |
1797 | ICE_SID_FLD_VEC_FD |
1798 | }, |
1799 | |
1800 | /* RSS */ |
1801 | { ICE_SID_XLT1_RSS, |
1802 | ICE_SID_XLT2_RSS, |
1803 | ICE_SID_PROFID_TCAM_RSS, |
1804 | ICE_SID_PROFID_REDIR_RSS, |
1805 | ICE_SID_FLD_VEC_RSS |
1806 | }, |
1807 | |
1808 | /* PE */ |
1809 | { ICE_SID_XLT1_PE, |
1810 | ICE_SID_XLT2_PE, |
1811 | ICE_SID_PROFID_TCAM_PE, |
1812 | ICE_SID_PROFID_REDIR_PE, |
1813 | ICE_SID_FLD_VEC_PE |
1814 | } |
1815 | }; |
1816 | |
1817 | /** |
1818 | * ice_init_sw_xlt1_db - init software XLT1 database from HW tables |
1819 | * @hw: pointer to the hardware structure |
1820 | * @blk: the HW block to initialize |
1821 | */ |
1822 | static void ice_init_sw_xlt1_db(struct ice_hw *hw, enum ice_block blk) |
1823 | { |
1824 | u16 pt; |
1825 | |
1826 | for (pt = 0; pt < hw->blk[blk].xlt1.count; pt++) { |
1827 | u8 ptg; |
1828 | |
1829 | ptg = hw->blk[blk].xlt1.t[pt]; |
1830 | if (ptg != ICE_DEFAULT_PTG) { |
1831 | ice_ptg_alloc_val(hw, blk, ptg); |
1832 | ice_ptg_add_mv_ptype(hw, blk, ptype: pt, ptg); |
1833 | } |
1834 | } |
1835 | } |
1836 | |
1837 | /** |
1838 | * ice_init_sw_xlt2_db - init software XLT2 database from HW tables |
1839 | * @hw: pointer to the hardware structure |
1840 | * @blk: the HW block to initialize |
1841 | */ |
1842 | static void ice_init_sw_xlt2_db(struct ice_hw *hw, enum ice_block blk) |
1843 | { |
1844 | u16 vsi; |
1845 | |
1846 | for (vsi = 0; vsi < hw->blk[blk].xlt2.count; vsi++) { |
1847 | u16 vsig; |
1848 | |
1849 | vsig = hw->blk[blk].xlt2.t[vsi]; |
1850 | if (vsig) { |
1851 | ice_vsig_alloc_val(hw, blk, vsig); |
1852 | ice_vsig_add_mv_vsi(hw, blk, vsi, vsig); |
1853 | /* no changes at this time, since this has been |
1854 | * initialized from the original package |
1855 | */ |
1856 | hw->blk[blk].xlt2.vsis[vsi].changed = 0; |
1857 | } |
1858 | } |
1859 | } |
1860 | |
1861 | /** |
1862 | * ice_init_sw_db - init software database from HW tables |
1863 | * @hw: pointer to the hardware structure |
1864 | */ |
1865 | static void ice_init_sw_db(struct ice_hw *hw) |
1866 | { |
1867 | u16 i; |
1868 | |
1869 | for (i = 0; i < ICE_BLK_COUNT; i++) { |
1870 | ice_init_sw_xlt1_db(hw, blk: (enum ice_block)i); |
1871 | ice_init_sw_xlt2_db(hw, blk: (enum ice_block)i); |
1872 | } |
1873 | } |
1874 | |
1875 | /** |
1876 | * ice_fill_tbl - Reads content of a single table type into database |
1877 | * @hw: pointer to the hardware structure |
1878 | * @block_id: Block ID of the table to copy |
1879 | * @sid: Section ID of the table to copy |
1880 | * |
1881 | * Will attempt to read the entire content of a given table of a single block |
1882 | * into the driver database. We assume that the buffer will always |
1883 | * be as large or larger than the data contained in the package. If |
1884 | * this condition is not met, there is most likely an error in the package |
1885 | * contents. |
1886 | */ |
1887 | static void ice_fill_tbl(struct ice_hw *hw, enum ice_block block_id, u32 sid) |
1888 | { |
1889 | u32 dst_len, sect_len, offset = 0; |
1890 | struct ice_prof_redir_section *pr; |
1891 | struct ice_prof_id_section *pid; |
1892 | struct ice_xlt1_section *xlt1; |
1893 | struct ice_xlt2_section *xlt2; |
1894 | struct ice_sw_fv_section *es; |
1895 | struct ice_pkg_enum state; |
1896 | u8 *src, *dst; |
1897 | void *sect; |
1898 | |
1899 | /* if the HW segment pointer is null then the first iteration of |
1900 | * ice_pkg_enum_section() will fail. In this case the HW tables will |
1901 | * not be filled and return success. |
1902 | */ |
1903 | if (!hw->seg) { |
1904 | ice_debug(hw, ICE_DBG_PKG, "hw->seg is NULL, tables are not filled\n" ); |
1905 | return; |
1906 | } |
1907 | |
1908 | memset(&state, 0, sizeof(state)); |
1909 | |
1910 | sect = ice_pkg_enum_section(ice_seg: hw->seg, state: &state, sect_type: sid); |
1911 | |
1912 | while (sect) { |
1913 | switch (sid) { |
1914 | case ICE_SID_XLT1_SW: |
1915 | case ICE_SID_XLT1_FD: |
1916 | case ICE_SID_XLT1_RSS: |
1917 | case ICE_SID_XLT1_ACL: |
1918 | case ICE_SID_XLT1_PE: |
1919 | xlt1 = sect; |
1920 | src = xlt1->value; |
1921 | sect_len = le16_to_cpu(xlt1->count) * |
1922 | sizeof(*hw->blk[block_id].xlt1.t); |
1923 | dst = hw->blk[block_id].xlt1.t; |
1924 | dst_len = hw->blk[block_id].xlt1.count * |
1925 | sizeof(*hw->blk[block_id].xlt1.t); |
1926 | break; |
1927 | case ICE_SID_XLT2_SW: |
1928 | case ICE_SID_XLT2_FD: |
1929 | case ICE_SID_XLT2_RSS: |
1930 | case ICE_SID_XLT2_ACL: |
1931 | case ICE_SID_XLT2_PE: |
1932 | xlt2 = sect; |
1933 | src = (__force u8 *)xlt2->value; |
1934 | sect_len = le16_to_cpu(xlt2->count) * |
1935 | sizeof(*hw->blk[block_id].xlt2.t); |
1936 | dst = (u8 *)hw->blk[block_id].xlt2.t; |
1937 | dst_len = hw->blk[block_id].xlt2.count * |
1938 | sizeof(*hw->blk[block_id].xlt2.t); |
1939 | break; |
1940 | case ICE_SID_PROFID_TCAM_SW: |
1941 | case ICE_SID_PROFID_TCAM_FD: |
1942 | case ICE_SID_PROFID_TCAM_RSS: |
1943 | case ICE_SID_PROFID_TCAM_ACL: |
1944 | case ICE_SID_PROFID_TCAM_PE: |
1945 | pid = sect; |
1946 | src = (u8 *)pid->entry; |
1947 | sect_len = le16_to_cpu(pid->count) * |
1948 | sizeof(*hw->blk[block_id].prof.t); |
1949 | dst = (u8 *)hw->blk[block_id].prof.t; |
1950 | dst_len = hw->blk[block_id].prof.count * |
1951 | sizeof(*hw->blk[block_id].prof.t); |
1952 | break; |
1953 | case ICE_SID_PROFID_REDIR_SW: |
1954 | case ICE_SID_PROFID_REDIR_FD: |
1955 | case ICE_SID_PROFID_REDIR_RSS: |
1956 | case ICE_SID_PROFID_REDIR_ACL: |
1957 | case ICE_SID_PROFID_REDIR_PE: |
1958 | pr = sect; |
1959 | src = pr->redir_value; |
1960 | sect_len = le16_to_cpu(pr->count) * |
1961 | sizeof(*hw->blk[block_id].prof_redir.t); |
1962 | dst = hw->blk[block_id].prof_redir.t; |
1963 | dst_len = hw->blk[block_id].prof_redir.count * |
1964 | sizeof(*hw->blk[block_id].prof_redir.t); |
1965 | break; |
1966 | case ICE_SID_FLD_VEC_SW: |
1967 | case ICE_SID_FLD_VEC_FD: |
1968 | case ICE_SID_FLD_VEC_RSS: |
1969 | case ICE_SID_FLD_VEC_ACL: |
1970 | case ICE_SID_FLD_VEC_PE: |
1971 | es = sect; |
1972 | src = (u8 *)es->fv; |
1973 | sect_len = (u32)(le16_to_cpu(es->count) * |
1974 | hw->blk[block_id].es.fvw) * |
1975 | sizeof(*hw->blk[block_id].es.t); |
1976 | dst = (u8 *)hw->blk[block_id].es.t; |
1977 | dst_len = (u32)(hw->blk[block_id].es.count * |
1978 | hw->blk[block_id].es.fvw) * |
1979 | sizeof(*hw->blk[block_id].es.t); |
1980 | break; |
1981 | default: |
1982 | return; |
1983 | } |
1984 | |
1985 | /* if the section offset exceeds destination length, terminate |
1986 | * table fill. |
1987 | */ |
1988 | if (offset > dst_len) |
1989 | return; |
1990 | |
1991 | /* if the sum of section size and offset exceed destination size |
1992 | * then we are out of bounds of the HW table size for that PF. |
1993 | * Changing section length to fill the remaining table space |
1994 | * of that PF. |
1995 | */ |
1996 | if ((offset + sect_len) > dst_len) |
1997 | sect_len = dst_len - offset; |
1998 | |
1999 | memcpy(dst + offset, src, sect_len); |
2000 | offset += sect_len; |
2001 | sect = ice_pkg_enum_section(NULL, state: &state, sect_type: sid); |
2002 | } |
2003 | } |
2004 | |
2005 | /** |
2006 | * ice_fill_blk_tbls - Read package context for tables |
2007 | * @hw: pointer to the hardware structure |
2008 | * |
2009 | * Reads the current package contents and populates the driver |
2010 | * database with the data iteratively for all advanced feature |
2011 | * blocks. Assume that the HW tables have been allocated. |
2012 | */ |
2013 | void ice_fill_blk_tbls(struct ice_hw *hw) |
2014 | { |
2015 | u8 i; |
2016 | |
2017 | for (i = 0; i < ICE_BLK_COUNT; i++) { |
2018 | enum ice_block blk_id = (enum ice_block)i; |
2019 | |
2020 | ice_fill_tbl(hw, block_id: blk_id, sid: hw->blk[blk_id].xlt1.sid); |
2021 | ice_fill_tbl(hw, block_id: blk_id, sid: hw->blk[blk_id].xlt2.sid); |
2022 | ice_fill_tbl(hw, block_id: blk_id, sid: hw->blk[blk_id].prof.sid); |
2023 | ice_fill_tbl(hw, block_id: blk_id, sid: hw->blk[blk_id].prof_redir.sid); |
2024 | ice_fill_tbl(hw, block_id: blk_id, sid: hw->blk[blk_id].es.sid); |
2025 | } |
2026 | |
2027 | ice_init_sw_db(hw); |
2028 | } |
2029 | |
2030 | /** |
2031 | * ice_free_prof_map - free profile map |
2032 | * @hw: pointer to the hardware structure |
2033 | * @blk_idx: HW block index |
2034 | */ |
2035 | static void ice_free_prof_map(struct ice_hw *hw, u8 blk_idx) |
2036 | { |
2037 | struct ice_es *es = &hw->blk[blk_idx].es; |
2038 | struct ice_prof_map *del, *tmp; |
2039 | |
2040 | mutex_lock(&es->prof_map_lock); |
2041 | list_for_each_entry_safe(del, tmp, &es->prof_map, list) { |
2042 | list_del(entry: &del->list); |
2043 | devm_kfree(dev: ice_hw_to_dev(hw), p: del); |
2044 | } |
2045 | INIT_LIST_HEAD(list: &es->prof_map); |
2046 | mutex_unlock(lock: &es->prof_map_lock); |
2047 | } |
2048 | |
2049 | /** |
2050 | * ice_free_flow_profs - free flow profile entries |
2051 | * @hw: pointer to the hardware structure |
2052 | * @blk_idx: HW block index |
2053 | */ |
2054 | static void ice_free_flow_profs(struct ice_hw *hw, u8 blk_idx) |
2055 | { |
2056 | struct ice_flow_prof *p, *tmp; |
2057 | |
2058 | mutex_lock(&hw->fl_profs_locks[blk_idx]); |
2059 | list_for_each_entry_safe(p, tmp, &hw->fl_profs[blk_idx], l_entry) { |
2060 | struct ice_flow_entry *e, *t; |
2061 | |
2062 | list_for_each_entry_safe(e, t, &p->entries, l_entry) |
2063 | ice_flow_rem_entry(hw, blk: (enum ice_block)blk_idx, |
2064 | ICE_FLOW_ENTRY_HNDL(e)); |
2065 | |
2066 | list_del(entry: &p->l_entry); |
2067 | |
2068 | mutex_destroy(lock: &p->entries_lock); |
2069 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
2070 | } |
2071 | mutex_unlock(lock: &hw->fl_profs_locks[blk_idx]); |
2072 | |
2073 | /* if driver is in reset and tables are being cleared |
2074 | * re-initialize the flow profile list heads |
2075 | */ |
2076 | INIT_LIST_HEAD(list: &hw->fl_profs[blk_idx]); |
2077 | } |
2078 | |
2079 | /** |
2080 | * ice_free_vsig_tbl - free complete VSIG table entries |
2081 | * @hw: pointer to the hardware structure |
2082 | * @blk: the HW block on which to free the VSIG table entries |
2083 | */ |
2084 | static void ice_free_vsig_tbl(struct ice_hw *hw, enum ice_block blk) |
2085 | { |
2086 | u16 i; |
2087 | |
2088 | if (!hw->blk[blk].xlt2.vsig_tbl) |
2089 | return; |
2090 | |
2091 | for (i = 1; i < ICE_MAX_VSIGS; i++) |
2092 | if (hw->blk[blk].xlt2.vsig_tbl[i].in_use) |
2093 | ice_vsig_free(hw, blk, vsig: i); |
2094 | } |
2095 | |
2096 | /** |
2097 | * ice_free_hw_tbls - free hardware table memory |
2098 | * @hw: pointer to the hardware structure |
2099 | */ |
2100 | void ice_free_hw_tbls(struct ice_hw *hw) |
2101 | { |
2102 | struct ice_rss_cfg *r, *rt; |
2103 | u8 i; |
2104 | |
2105 | for (i = 0; i < ICE_BLK_COUNT; i++) { |
2106 | if (hw->blk[i].is_list_init) { |
2107 | struct ice_es *es = &hw->blk[i].es; |
2108 | |
2109 | ice_free_prof_map(hw, blk_idx: i); |
2110 | mutex_destroy(lock: &es->prof_map_lock); |
2111 | |
2112 | ice_free_flow_profs(hw, blk_idx: i); |
2113 | mutex_destroy(lock: &hw->fl_profs_locks[i]); |
2114 | |
2115 | hw->blk[i].is_list_init = false; |
2116 | } |
2117 | ice_free_vsig_tbl(hw, blk: (enum ice_block)i); |
2118 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].xlt1.ptypes); |
2119 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].xlt1.ptg_tbl); |
2120 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].xlt1.t); |
2121 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].xlt2.t); |
2122 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].xlt2.vsig_tbl); |
2123 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].xlt2.vsis); |
2124 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].prof.t); |
2125 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].prof_redir.t); |
2126 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].es.t); |
2127 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].es.ref_count); |
2128 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].es.symm); |
2129 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].es.written); |
2130 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].es.mask_ena); |
2131 | devm_kfree(dev: ice_hw_to_dev(hw), p: hw->blk[i].prof_id.id); |
2132 | } |
2133 | |
2134 | list_for_each_entry_safe(r, rt, &hw->rss_list_head, l_entry) { |
2135 | list_del(entry: &r->l_entry); |
2136 | devm_kfree(dev: ice_hw_to_dev(hw), p: r); |
2137 | } |
2138 | mutex_destroy(lock: &hw->rss_locks); |
2139 | ice_shutdown_all_prof_masks(hw); |
2140 | memset(hw->blk, 0, sizeof(hw->blk)); |
2141 | } |
2142 | |
2143 | /** |
2144 | * ice_init_flow_profs - init flow profile locks and list heads |
2145 | * @hw: pointer to the hardware structure |
2146 | * @blk_idx: HW block index |
2147 | */ |
2148 | static void ice_init_flow_profs(struct ice_hw *hw, u8 blk_idx) |
2149 | { |
2150 | mutex_init(&hw->fl_profs_locks[blk_idx]); |
2151 | INIT_LIST_HEAD(list: &hw->fl_profs[blk_idx]); |
2152 | } |
2153 | |
2154 | /** |
2155 | * ice_clear_hw_tbls - clear HW tables and flow profiles |
2156 | * @hw: pointer to the hardware structure |
2157 | */ |
2158 | void ice_clear_hw_tbls(struct ice_hw *hw) |
2159 | { |
2160 | u8 i; |
2161 | |
2162 | for (i = 0; i < ICE_BLK_COUNT; i++) { |
2163 | struct ice_prof_redir *prof_redir = &hw->blk[i].prof_redir; |
2164 | struct ice_prof_id *prof_id = &hw->blk[i].prof_id; |
2165 | struct ice_prof_tcam *prof = &hw->blk[i].prof; |
2166 | struct ice_xlt1 *xlt1 = &hw->blk[i].xlt1; |
2167 | struct ice_xlt2 *xlt2 = &hw->blk[i].xlt2; |
2168 | struct ice_es *es = &hw->blk[i].es; |
2169 | |
2170 | if (hw->blk[i].is_list_init) { |
2171 | ice_free_prof_map(hw, blk_idx: i); |
2172 | ice_free_flow_profs(hw, blk_idx: i); |
2173 | } |
2174 | |
2175 | ice_free_vsig_tbl(hw, blk: (enum ice_block)i); |
2176 | |
2177 | memset(xlt1->ptypes, 0, xlt1->count * sizeof(*xlt1->ptypes)); |
2178 | memset(xlt1->ptg_tbl, 0, |
2179 | ICE_MAX_PTGS * sizeof(*xlt1->ptg_tbl)); |
2180 | memset(xlt1->t, 0, xlt1->count * sizeof(*xlt1->t)); |
2181 | |
2182 | memset(xlt2->vsis, 0, xlt2->count * sizeof(*xlt2->vsis)); |
2183 | memset(xlt2->vsig_tbl, 0, |
2184 | xlt2->count * sizeof(*xlt2->vsig_tbl)); |
2185 | memset(xlt2->t, 0, xlt2->count * sizeof(*xlt2->t)); |
2186 | |
2187 | memset(prof->t, 0, prof->count * sizeof(*prof->t)); |
2188 | memset(prof_redir->t, 0, |
2189 | prof_redir->count * sizeof(*prof_redir->t)); |
2190 | |
2191 | memset(es->t, 0, es->count * sizeof(*es->t) * es->fvw); |
2192 | memset(es->ref_count, 0, es->count * sizeof(*es->ref_count)); |
2193 | memset(es->symm, 0, es->count * sizeof(*es->symm)); |
2194 | memset(es->written, 0, es->count * sizeof(*es->written)); |
2195 | memset(es->mask_ena, 0, es->count * sizeof(*es->mask_ena)); |
2196 | |
2197 | memset(prof_id->id, 0, prof_id->count * sizeof(*prof_id->id)); |
2198 | } |
2199 | } |
2200 | |
2201 | /** |
2202 | * ice_init_hw_tbls - init hardware table memory |
2203 | * @hw: pointer to the hardware structure |
2204 | */ |
2205 | int ice_init_hw_tbls(struct ice_hw *hw) |
2206 | { |
2207 | u8 i; |
2208 | |
2209 | mutex_init(&hw->rss_locks); |
2210 | INIT_LIST_HEAD(list: &hw->rss_list_head); |
2211 | ice_init_all_prof_masks(hw); |
2212 | for (i = 0; i < ICE_BLK_COUNT; i++) { |
2213 | struct ice_prof_redir *prof_redir = &hw->blk[i].prof_redir; |
2214 | struct ice_prof_id *prof_id = &hw->blk[i].prof_id; |
2215 | struct ice_prof_tcam *prof = &hw->blk[i].prof; |
2216 | struct ice_xlt1 *xlt1 = &hw->blk[i].xlt1; |
2217 | struct ice_xlt2 *xlt2 = &hw->blk[i].xlt2; |
2218 | struct ice_es *es = &hw->blk[i].es; |
2219 | u16 j; |
2220 | |
2221 | if (hw->blk[i].is_list_init) |
2222 | continue; |
2223 | |
2224 | ice_init_flow_profs(hw, blk_idx: i); |
2225 | mutex_init(&es->prof_map_lock); |
2226 | INIT_LIST_HEAD(list: &es->prof_map); |
2227 | hw->blk[i].is_list_init = true; |
2228 | |
2229 | hw->blk[i].overwrite = blk_sizes[i].overwrite; |
2230 | es->reverse = blk_sizes[i].reverse; |
2231 | |
2232 | xlt1->sid = ice_blk_sids[i][ICE_SID_XLT1_OFF]; |
2233 | xlt1->count = blk_sizes[i].xlt1; |
2234 | |
2235 | xlt1->ptypes = devm_kcalloc(dev: ice_hw_to_dev(hw), n: xlt1->count, |
2236 | size: sizeof(*xlt1->ptypes), GFP_KERNEL); |
2237 | |
2238 | if (!xlt1->ptypes) |
2239 | goto err; |
2240 | |
2241 | xlt1->ptg_tbl = devm_kcalloc(dev: ice_hw_to_dev(hw), ICE_MAX_PTGS, |
2242 | size: sizeof(*xlt1->ptg_tbl), |
2243 | GFP_KERNEL); |
2244 | |
2245 | if (!xlt1->ptg_tbl) |
2246 | goto err; |
2247 | |
2248 | xlt1->t = devm_kcalloc(dev: ice_hw_to_dev(hw), n: xlt1->count, |
2249 | size: sizeof(*xlt1->t), GFP_KERNEL); |
2250 | if (!xlt1->t) |
2251 | goto err; |
2252 | |
2253 | xlt2->sid = ice_blk_sids[i][ICE_SID_XLT2_OFF]; |
2254 | xlt2->count = blk_sizes[i].xlt2; |
2255 | |
2256 | xlt2->vsis = devm_kcalloc(dev: ice_hw_to_dev(hw), n: xlt2->count, |
2257 | size: sizeof(*xlt2->vsis), GFP_KERNEL); |
2258 | |
2259 | if (!xlt2->vsis) |
2260 | goto err; |
2261 | |
2262 | xlt2->vsig_tbl = devm_kcalloc(dev: ice_hw_to_dev(hw), n: xlt2->count, |
2263 | size: sizeof(*xlt2->vsig_tbl), |
2264 | GFP_KERNEL); |
2265 | if (!xlt2->vsig_tbl) |
2266 | goto err; |
2267 | |
2268 | for (j = 0; j < xlt2->count; j++) |
2269 | INIT_LIST_HEAD(list: &xlt2->vsig_tbl[j].prop_lst); |
2270 | |
2271 | xlt2->t = devm_kcalloc(dev: ice_hw_to_dev(hw), n: xlt2->count, |
2272 | size: sizeof(*xlt2->t), GFP_KERNEL); |
2273 | if (!xlt2->t) |
2274 | goto err; |
2275 | |
2276 | prof->sid = ice_blk_sids[i][ICE_SID_PR_OFF]; |
2277 | prof->count = blk_sizes[i].prof_tcam; |
2278 | prof->max_prof_id = blk_sizes[i].prof_id; |
2279 | prof->cdid_bits = blk_sizes[i].prof_cdid_bits; |
2280 | prof->t = devm_kcalloc(dev: ice_hw_to_dev(hw), n: prof->count, |
2281 | size: sizeof(*prof->t), GFP_KERNEL); |
2282 | |
2283 | if (!prof->t) |
2284 | goto err; |
2285 | |
2286 | prof_redir->sid = ice_blk_sids[i][ICE_SID_PR_REDIR_OFF]; |
2287 | prof_redir->count = blk_sizes[i].prof_redir; |
2288 | prof_redir->t = devm_kcalloc(dev: ice_hw_to_dev(hw), |
2289 | n: prof_redir->count, |
2290 | size: sizeof(*prof_redir->t), |
2291 | GFP_KERNEL); |
2292 | |
2293 | if (!prof_redir->t) |
2294 | goto err; |
2295 | |
2296 | es->sid = ice_blk_sids[i][ICE_SID_ES_OFF]; |
2297 | es->count = blk_sizes[i].es; |
2298 | es->fvw = blk_sizes[i].fvw; |
2299 | es->t = devm_kcalloc(dev: ice_hw_to_dev(hw), |
2300 | n: (u32)(es->count * es->fvw), |
2301 | size: sizeof(*es->t), GFP_KERNEL); |
2302 | if (!es->t) |
2303 | goto err; |
2304 | |
2305 | es->ref_count = devm_kcalloc(dev: ice_hw_to_dev(hw), n: es->count, |
2306 | size: sizeof(*es->ref_count), |
2307 | GFP_KERNEL); |
2308 | if (!es->ref_count) |
2309 | goto err; |
2310 | |
2311 | es->symm = devm_kcalloc(dev: ice_hw_to_dev(hw), n: es->count, |
2312 | size: sizeof(*es->symm), GFP_KERNEL); |
2313 | if (!es->symm) |
2314 | goto err; |
2315 | |
2316 | es->written = devm_kcalloc(dev: ice_hw_to_dev(hw), n: es->count, |
2317 | size: sizeof(*es->written), GFP_KERNEL); |
2318 | if (!es->written) |
2319 | goto err; |
2320 | |
2321 | es->mask_ena = devm_kcalloc(dev: ice_hw_to_dev(hw), n: es->count, |
2322 | size: sizeof(*es->mask_ena), GFP_KERNEL); |
2323 | if (!es->mask_ena) |
2324 | goto err; |
2325 | |
2326 | prof_id->count = blk_sizes[i].prof_id; |
2327 | prof_id->id = devm_kcalloc(dev: ice_hw_to_dev(hw), n: prof_id->count, |
2328 | size: sizeof(*prof_id->id), GFP_KERNEL); |
2329 | if (!prof_id->id) |
2330 | goto err; |
2331 | } |
2332 | return 0; |
2333 | |
2334 | err: |
2335 | ice_free_hw_tbls(hw); |
2336 | return -ENOMEM; |
2337 | } |
2338 | |
2339 | /** |
2340 | * ice_prof_gen_key - generate profile ID key |
2341 | * @hw: pointer to the HW struct |
2342 | * @blk: the block in which to write profile ID to |
2343 | * @ptg: packet type group (PTG) portion of key |
2344 | * @vsig: VSIG portion of key |
2345 | * @cdid: CDID portion of key |
2346 | * @flags: flag portion of key |
2347 | * @vl_msk: valid mask |
2348 | * @dc_msk: don't care mask |
2349 | * @nm_msk: never match mask |
2350 | * @key: output of profile ID key |
2351 | */ |
2352 | static int |
2353 | ice_prof_gen_key(struct ice_hw *hw, enum ice_block blk, u8 ptg, u16 vsig, |
2354 | u8 cdid, u16 flags, u8 vl_msk[ICE_TCAM_KEY_VAL_SZ], |
2355 | u8 dc_msk[ICE_TCAM_KEY_VAL_SZ], u8 nm_msk[ICE_TCAM_KEY_VAL_SZ], |
2356 | u8 key[ICE_TCAM_KEY_SZ]) |
2357 | { |
2358 | struct ice_prof_id_key inkey; |
2359 | |
2360 | inkey.xlt1 = ptg; |
2361 | inkey.xlt2_cdid = cpu_to_le16(vsig); |
2362 | inkey.flags = cpu_to_le16(flags); |
2363 | |
2364 | switch (hw->blk[blk].prof.cdid_bits) { |
2365 | case 0: |
2366 | break; |
2367 | case 2: |
2368 | #define ICE_CD_2_M 0xC000U |
2369 | #define ICE_CD_2_S 14 |
2370 | inkey.xlt2_cdid &= ~cpu_to_le16(ICE_CD_2_M); |
2371 | inkey.xlt2_cdid |= cpu_to_le16(BIT(cdid) << ICE_CD_2_S); |
2372 | break; |
2373 | case 4: |
2374 | #define ICE_CD_4_M 0xF000U |
2375 | #define ICE_CD_4_S 12 |
2376 | inkey.xlt2_cdid &= ~cpu_to_le16(ICE_CD_4_M); |
2377 | inkey.xlt2_cdid |= cpu_to_le16(BIT(cdid) << ICE_CD_4_S); |
2378 | break; |
2379 | case 8: |
2380 | #define ICE_CD_8_M 0xFF00U |
2381 | #define ICE_CD_8_S 16 |
2382 | inkey.xlt2_cdid &= ~cpu_to_le16(ICE_CD_8_M); |
2383 | inkey.xlt2_cdid |= cpu_to_le16(BIT(cdid) << ICE_CD_8_S); |
2384 | break; |
2385 | default: |
2386 | ice_debug(hw, ICE_DBG_PKG, "Error in profile config\n" ); |
2387 | break; |
2388 | } |
2389 | |
2390 | return ice_set_key(key, ICE_TCAM_KEY_SZ, val: (u8 *)&inkey, upd: vl_msk, dc: dc_msk, |
2391 | nm: nm_msk, off: 0, ICE_TCAM_KEY_SZ / 2); |
2392 | } |
2393 | |
2394 | /** |
2395 | * ice_tcam_write_entry - write TCAM entry |
2396 | * @hw: pointer to the HW struct |
2397 | * @blk: the block in which to write profile ID to |
2398 | * @idx: the entry index to write to |
2399 | * @prof_id: profile ID |
2400 | * @ptg: packet type group (PTG) portion of key |
2401 | * @vsig: VSIG portion of key |
2402 | * @cdid: CDID portion of key |
2403 | * @flags: flag portion of key |
2404 | * @vl_msk: valid mask |
2405 | * @dc_msk: don't care mask |
2406 | * @nm_msk: never match mask |
2407 | */ |
2408 | static int |
2409 | ice_tcam_write_entry(struct ice_hw *hw, enum ice_block blk, u16 idx, |
2410 | u8 prof_id, u8 ptg, u16 vsig, u8 cdid, u16 flags, |
2411 | u8 vl_msk[ICE_TCAM_KEY_VAL_SZ], |
2412 | u8 dc_msk[ICE_TCAM_KEY_VAL_SZ], |
2413 | u8 nm_msk[ICE_TCAM_KEY_VAL_SZ]) |
2414 | { |
2415 | struct ice_prof_tcam_entry; |
2416 | int status; |
2417 | |
2418 | status = ice_prof_gen_key(hw, blk, ptg, vsig, cdid, flags, vl_msk, |
2419 | dc_msk, nm_msk, key: hw->blk[blk].prof.t[idx].key); |
2420 | if (!status) { |
2421 | hw->blk[blk].prof.t[idx].addr = cpu_to_le16(idx); |
2422 | hw->blk[blk].prof.t[idx].prof_id = prof_id; |
2423 | } |
2424 | |
2425 | return status; |
2426 | } |
2427 | |
2428 | /** |
2429 | * ice_vsig_get_ref - returns number of VSIs belong to a VSIG |
2430 | * @hw: pointer to the hardware structure |
2431 | * @blk: HW block |
2432 | * @vsig: VSIG to query |
2433 | * @refs: pointer to variable to receive the reference count |
2434 | */ |
2435 | static int |
2436 | ice_vsig_get_ref(struct ice_hw *hw, enum ice_block blk, u16 vsig, u16 *refs) |
2437 | { |
2438 | u16 idx = vsig & ICE_VSIG_IDX_M; |
2439 | struct ice_vsig_vsi *ptr; |
2440 | |
2441 | *refs = 0; |
2442 | |
2443 | if (!hw->blk[blk].xlt2.vsig_tbl[idx].in_use) |
2444 | return -ENOENT; |
2445 | |
2446 | ptr = hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; |
2447 | while (ptr) { |
2448 | (*refs)++; |
2449 | ptr = ptr->next_vsi; |
2450 | } |
2451 | |
2452 | return 0; |
2453 | } |
2454 | |
2455 | /** |
2456 | * ice_has_prof_vsig - check to see if VSIG has a specific profile |
2457 | * @hw: pointer to the hardware structure |
2458 | * @blk: HW block |
2459 | * @vsig: VSIG to check against |
2460 | * @hdl: profile handle |
2461 | */ |
2462 | static bool |
2463 | ice_has_prof_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsig, u64 hdl) |
2464 | { |
2465 | u16 idx = vsig & ICE_VSIG_IDX_M; |
2466 | struct ice_vsig_prof *ent; |
2467 | |
2468 | list_for_each_entry(ent, &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
2469 | list) |
2470 | if (ent->profile_cookie == hdl) |
2471 | return true; |
2472 | |
2473 | ice_debug(hw, ICE_DBG_INIT, "Characteristic list for VSI group %d not found.\n" , |
2474 | vsig); |
2475 | return false; |
2476 | } |
2477 | |
2478 | /** |
2479 | * ice_prof_bld_es - build profile ID extraction sequence changes |
2480 | * @hw: pointer to the HW struct |
2481 | * @blk: hardware block |
2482 | * @bld: the update package buffer build to add to |
2483 | * @chgs: the list of changes to make in hardware |
2484 | */ |
2485 | static int |
2486 | ice_prof_bld_es(struct ice_hw *hw, enum ice_block blk, |
2487 | struct ice_buf_build *bld, struct list_head *chgs) |
2488 | { |
2489 | u16 vec_size = hw->blk[blk].es.fvw * sizeof(struct ice_fv_word); |
2490 | struct ice_chs_chg *tmp; |
2491 | |
2492 | list_for_each_entry(tmp, chgs, list_entry) |
2493 | if (tmp->type == ICE_PTG_ES_ADD && tmp->add_prof) { |
2494 | u16 off = tmp->prof_id * hw->blk[blk].es.fvw; |
2495 | struct ice_pkg_es *p; |
2496 | u32 id; |
2497 | |
2498 | id = ice_sect_id(blk, sect: ICE_VEC_TBL); |
2499 | p = ice_pkg_buf_alloc_section(bld, type: id, |
2500 | struct_size(p, es, 1) + |
2501 | vec_size - |
2502 | sizeof(p->es[0])); |
2503 | |
2504 | if (!p) |
2505 | return -ENOSPC; |
2506 | |
2507 | p->count = cpu_to_le16(1); |
2508 | p->offset = cpu_to_le16(tmp->prof_id); |
2509 | |
2510 | memcpy(p->es, &hw->blk[blk].es.t[off], vec_size); |
2511 | } |
2512 | |
2513 | return 0; |
2514 | } |
2515 | |
2516 | /** |
2517 | * ice_prof_bld_tcam - build profile ID TCAM changes |
2518 | * @hw: pointer to the HW struct |
2519 | * @blk: hardware block |
2520 | * @bld: the update package buffer build to add to |
2521 | * @chgs: the list of changes to make in hardware |
2522 | */ |
2523 | static int |
2524 | ice_prof_bld_tcam(struct ice_hw *hw, enum ice_block blk, |
2525 | struct ice_buf_build *bld, struct list_head *chgs) |
2526 | { |
2527 | struct ice_chs_chg *tmp; |
2528 | |
2529 | list_for_each_entry(tmp, chgs, list_entry) |
2530 | if (tmp->type == ICE_TCAM_ADD && tmp->add_tcam_idx) { |
2531 | struct ice_prof_id_section *p; |
2532 | u32 id; |
2533 | |
2534 | id = ice_sect_id(blk, sect: ICE_PROF_TCAM); |
2535 | p = ice_pkg_buf_alloc_section(bld, type: id, |
2536 | struct_size(p, entry, 1)); |
2537 | |
2538 | if (!p) |
2539 | return -ENOSPC; |
2540 | |
2541 | p->count = cpu_to_le16(1); |
2542 | p->entry[0].addr = cpu_to_le16(tmp->tcam_idx); |
2543 | p->entry[0].prof_id = tmp->prof_id; |
2544 | |
2545 | memcpy(p->entry[0].key, |
2546 | &hw->blk[blk].prof.t[tmp->tcam_idx].key, |
2547 | sizeof(hw->blk[blk].prof.t->key)); |
2548 | } |
2549 | |
2550 | return 0; |
2551 | } |
2552 | |
2553 | /** |
2554 | * ice_prof_bld_xlt1 - build XLT1 changes |
2555 | * @blk: hardware block |
2556 | * @bld: the update package buffer build to add to |
2557 | * @chgs: the list of changes to make in hardware |
2558 | */ |
2559 | static int |
2560 | ice_prof_bld_xlt1(enum ice_block blk, struct ice_buf_build *bld, |
2561 | struct list_head *chgs) |
2562 | { |
2563 | struct ice_chs_chg *tmp; |
2564 | |
2565 | list_for_each_entry(tmp, chgs, list_entry) |
2566 | if (tmp->type == ICE_PTG_ES_ADD && tmp->add_ptg) { |
2567 | struct ice_xlt1_section *p; |
2568 | u32 id; |
2569 | |
2570 | id = ice_sect_id(blk, sect: ICE_XLT1); |
2571 | p = ice_pkg_buf_alloc_section(bld, type: id, |
2572 | struct_size(p, value, 1)); |
2573 | |
2574 | if (!p) |
2575 | return -ENOSPC; |
2576 | |
2577 | p->count = cpu_to_le16(1); |
2578 | p->offset = cpu_to_le16(tmp->ptype); |
2579 | p->value[0] = tmp->ptg; |
2580 | } |
2581 | |
2582 | return 0; |
2583 | } |
2584 | |
2585 | /** |
2586 | * ice_prof_bld_xlt2 - build XLT2 changes |
2587 | * @blk: hardware block |
2588 | * @bld: the update package buffer build to add to |
2589 | * @chgs: the list of changes to make in hardware |
2590 | */ |
2591 | static int |
2592 | ice_prof_bld_xlt2(enum ice_block blk, struct ice_buf_build *bld, |
2593 | struct list_head *chgs) |
2594 | { |
2595 | struct ice_chs_chg *tmp; |
2596 | |
2597 | list_for_each_entry(tmp, chgs, list_entry) { |
2598 | struct ice_xlt2_section *p; |
2599 | u32 id; |
2600 | |
2601 | switch (tmp->type) { |
2602 | case ICE_VSIG_ADD: |
2603 | case ICE_VSI_MOVE: |
2604 | case ICE_VSIG_REM: |
2605 | id = ice_sect_id(blk, sect: ICE_XLT2); |
2606 | p = ice_pkg_buf_alloc_section(bld, type: id, |
2607 | struct_size(p, value, 1)); |
2608 | |
2609 | if (!p) |
2610 | return -ENOSPC; |
2611 | |
2612 | p->count = cpu_to_le16(1); |
2613 | p->offset = cpu_to_le16(tmp->vsi); |
2614 | p->value[0] = cpu_to_le16(tmp->vsig); |
2615 | break; |
2616 | default: |
2617 | break; |
2618 | } |
2619 | } |
2620 | |
2621 | return 0; |
2622 | } |
2623 | |
2624 | /** |
2625 | * ice_upd_prof_hw - update hardware using the change list |
2626 | * @hw: pointer to the HW struct |
2627 | * @blk: hardware block |
2628 | * @chgs: the list of changes to make in hardware |
2629 | */ |
2630 | static int |
2631 | ice_upd_prof_hw(struct ice_hw *hw, enum ice_block blk, |
2632 | struct list_head *chgs) |
2633 | { |
2634 | struct ice_buf_build *b; |
2635 | struct ice_chs_chg *tmp; |
2636 | u16 pkg_sects; |
2637 | u16 xlt1 = 0; |
2638 | u16 xlt2 = 0; |
2639 | u16 tcam = 0; |
2640 | u16 es = 0; |
2641 | int status; |
2642 | u16 sects; |
2643 | |
2644 | /* count number of sections we need */ |
2645 | list_for_each_entry(tmp, chgs, list_entry) { |
2646 | switch (tmp->type) { |
2647 | case ICE_PTG_ES_ADD: |
2648 | if (tmp->add_ptg) |
2649 | xlt1++; |
2650 | if (tmp->add_prof) |
2651 | es++; |
2652 | break; |
2653 | case ICE_TCAM_ADD: |
2654 | tcam++; |
2655 | break; |
2656 | case ICE_VSIG_ADD: |
2657 | case ICE_VSI_MOVE: |
2658 | case ICE_VSIG_REM: |
2659 | xlt2++; |
2660 | break; |
2661 | default: |
2662 | break; |
2663 | } |
2664 | } |
2665 | sects = xlt1 + xlt2 + tcam + es; |
2666 | |
2667 | if (!sects) |
2668 | return 0; |
2669 | |
2670 | /* Build update package buffer */ |
2671 | b = ice_pkg_buf_alloc(hw); |
2672 | if (!b) |
2673 | return -ENOMEM; |
2674 | |
2675 | status = ice_pkg_buf_reserve_section(bld: b, count: sects); |
2676 | if (status) |
2677 | goto error_tmp; |
2678 | |
2679 | /* Preserve order of table update: ES, TCAM, PTG, VSIG */ |
2680 | if (es) { |
2681 | status = ice_prof_bld_es(hw, blk, bld: b, chgs); |
2682 | if (status) |
2683 | goto error_tmp; |
2684 | } |
2685 | |
2686 | if (tcam) { |
2687 | status = ice_prof_bld_tcam(hw, blk, bld: b, chgs); |
2688 | if (status) |
2689 | goto error_tmp; |
2690 | } |
2691 | |
2692 | if (xlt1) { |
2693 | status = ice_prof_bld_xlt1(blk, bld: b, chgs); |
2694 | if (status) |
2695 | goto error_tmp; |
2696 | } |
2697 | |
2698 | if (xlt2) { |
2699 | status = ice_prof_bld_xlt2(blk, bld: b, chgs); |
2700 | if (status) |
2701 | goto error_tmp; |
2702 | } |
2703 | |
2704 | /* After package buffer build check if the section count in buffer is |
2705 | * non-zero and matches the number of sections detected for package |
2706 | * update. |
2707 | */ |
2708 | pkg_sects = ice_pkg_buf_get_active_sections(bld: b); |
2709 | if (!pkg_sects || pkg_sects != sects) { |
2710 | status = -EINVAL; |
2711 | goto error_tmp; |
2712 | } |
2713 | |
2714 | /* update package */ |
2715 | status = ice_update_pkg(hw, bufs: ice_pkg_buf(bld: b), count: 1); |
2716 | if (status == -EIO) |
2717 | ice_debug(hw, ICE_DBG_INIT, "Unable to update HW profile\n" ); |
2718 | |
2719 | error_tmp: |
2720 | ice_pkg_buf_free(hw, bld: b); |
2721 | return status; |
2722 | } |
2723 | |
2724 | /** |
2725 | * ice_update_fd_mask - set Flow Director Field Vector mask for a profile |
2726 | * @hw: pointer to the HW struct |
2727 | * @prof_id: profile ID |
2728 | * @mask_sel: mask select |
2729 | * |
2730 | * This function enable any of the masks selected by the mask select parameter |
2731 | * for the profile specified. |
2732 | */ |
2733 | static void ice_update_fd_mask(struct ice_hw *hw, u16 prof_id, u32 mask_sel) |
2734 | { |
2735 | wr32(hw, GLQF_FDMASK_SEL(prof_id), mask_sel); |
2736 | |
2737 | ice_debug(hw, ICE_DBG_INIT, "fd mask(%d): %x = %x\n" , prof_id, |
2738 | GLQF_FDMASK_SEL(prof_id), mask_sel); |
2739 | } |
2740 | |
2741 | struct ice_fd_src_dst_pair { |
2742 | u8 prot_id; |
2743 | u8 count; |
2744 | u16 off; |
2745 | }; |
2746 | |
2747 | static const struct ice_fd_src_dst_pair ice_fd_pairs[] = { |
2748 | /* These are defined in pairs */ |
2749 | { ICE_PROT_IPV4_OF_OR_S, 2, 12 }, |
2750 | { ICE_PROT_IPV4_OF_OR_S, 2, 16 }, |
2751 | |
2752 | { ICE_PROT_IPV4_IL, 2, 12 }, |
2753 | { ICE_PROT_IPV4_IL, 2, 16 }, |
2754 | |
2755 | { ICE_PROT_IPV6_OF_OR_S, 8, 8 }, |
2756 | { ICE_PROT_IPV6_OF_OR_S, 8, 24 }, |
2757 | |
2758 | { ICE_PROT_IPV6_IL, 8, 8 }, |
2759 | { ICE_PROT_IPV6_IL, 8, 24 }, |
2760 | |
2761 | { ICE_PROT_TCP_IL, 1, 0 }, |
2762 | { ICE_PROT_TCP_IL, 1, 2 }, |
2763 | |
2764 | { ICE_PROT_UDP_OF, 1, 0 }, |
2765 | { ICE_PROT_UDP_OF, 1, 2 }, |
2766 | |
2767 | { ICE_PROT_UDP_IL_OR_S, 1, 0 }, |
2768 | { ICE_PROT_UDP_IL_OR_S, 1, 2 }, |
2769 | |
2770 | { ICE_PROT_SCTP_IL, 1, 0 }, |
2771 | { ICE_PROT_SCTP_IL, 1, 2 } |
2772 | }; |
2773 | |
2774 | #define ICE_FD_SRC_DST_PAIR_COUNT ARRAY_SIZE(ice_fd_pairs) |
2775 | |
2776 | /** |
2777 | * ice_update_fd_swap - set register appropriately for a FD FV extraction |
2778 | * @hw: pointer to the HW struct |
2779 | * @prof_id: profile ID |
2780 | * @es: extraction sequence (length of array is determined by the block) |
2781 | */ |
2782 | static int |
2783 | ice_update_fd_swap(struct ice_hw *hw, u16 prof_id, struct ice_fv_word *es) |
2784 | { |
2785 | DECLARE_BITMAP(pair_list, ICE_FD_SRC_DST_PAIR_COUNT); |
2786 | u8 pair_start[ICE_FD_SRC_DST_PAIR_COUNT] = { 0 }; |
2787 | #define ICE_FD_FV_NOT_FOUND (-2) |
2788 | s8 first_free = ICE_FD_FV_NOT_FOUND; |
2789 | u8 used[ICE_MAX_FV_WORDS] = { 0 }; |
2790 | s8 orig_free, si; |
2791 | u32 mask_sel = 0; |
2792 | u8 i, j, k; |
2793 | |
2794 | bitmap_zero(dst: pair_list, ICE_FD_SRC_DST_PAIR_COUNT); |
2795 | |
2796 | /* This code assumes that the Flow Director field vectors are assigned |
2797 | * from the end of the FV indexes working towards the zero index, that |
2798 | * only complete fields will be included and will be consecutive, and |
2799 | * that there are no gaps between valid indexes. |
2800 | */ |
2801 | |
2802 | /* Determine swap fields present */ |
2803 | for (i = 0; i < hw->blk[ICE_BLK_FD].es.fvw; i++) { |
2804 | /* Find the first free entry, assuming right to left population. |
2805 | * This is where we can start adding additional pairs if needed. |
2806 | */ |
2807 | if (first_free == ICE_FD_FV_NOT_FOUND && es[i].prot_id != |
2808 | ICE_PROT_INVALID) |
2809 | first_free = i - 1; |
2810 | |
2811 | for (j = 0; j < ICE_FD_SRC_DST_PAIR_COUNT; j++) |
2812 | if (es[i].prot_id == ice_fd_pairs[j].prot_id && |
2813 | es[i].off == ice_fd_pairs[j].off) { |
2814 | __set_bit(j, pair_list); |
2815 | pair_start[j] = i; |
2816 | } |
2817 | } |
2818 | |
2819 | orig_free = first_free; |
2820 | |
2821 | /* determine missing swap fields that need to be added */ |
2822 | for (i = 0; i < ICE_FD_SRC_DST_PAIR_COUNT; i += 2) { |
2823 | u8 bit1 = test_bit(i + 1, pair_list); |
2824 | u8 bit0 = test_bit(i, pair_list); |
2825 | |
2826 | if (bit0 ^ bit1) { |
2827 | u8 index; |
2828 | |
2829 | /* add the appropriate 'paired' entry */ |
2830 | if (!bit0) |
2831 | index = i; |
2832 | else |
2833 | index = i + 1; |
2834 | |
2835 | /* check for room */ |
2836 | if (first_free + 1 < (s8)ice_fd_pairs[index].count) |
2837 | return -ENOSPC; |
2838 | |
2839 | /* place in extraction sequence */ |
2840 | for (k = 0; k < ice_fd_pairs[index].count; k++) { |
2841 | es[first_free - k].prot_id = |
2842 | ice_fd_pairs[index].prot_id; |
2843 | es[first_free - k].off = |
2844 | ice_fd_pairs[index].off + (k * 2); |
2845 | |
2846 | if (k > first_free) |
2847 | return -EIO; |
2848 | |
2849 | /* keep track of non-relevant fields */ |
2850 | mask_sel |= BIT(first_free - k); |
2851 | } |
2852 | |
2853 | pair_start[index] = first_free; |
2854 | first_free -= ice_fd_pairs[index].count; |
2855 | } |
2856 | } |
2857 | |
2858 | /* fill in the swap array */ |
2859 | si = hw->blk[ICE_BLK_FD].es.fvw - 1; |
2860 | while (si >= 0) { |
2861 | u8 indexes_used = 1; |
2862 | |
2863 | /* assume flat at this index */ |
2864 | #define ICE_SWAP_VALID 0x80 |
2865 | used[si] = si | ICE_SWAP_VALID; |
2866 | |
2867 | if (orig_free == ICE_FD_FV_NOT_FOUND || si <= orig_free) { |
2868 | si -= indexes_used; |
2869 | continue; |
2870 | } |
2871 | |
2872 | /* check for a swap location */ |
2873 | for (j = 0; j < ICE_FD_SRC_DST_PAIR_COUNT; j++) |
2874 | if (es[si].prot_id == ice_fd_pairs[j].prot_id && |
2875 | es[si].off == ice_fd_pairs[j].off) { |
2876 | u8 idx; |
2877 | |
2878 | /* determine the appropriate matching field */ |
2879 | idx = j + ((j % 2) ? -1 : 1); |
2880 | |
2881 | indexes_used = ice_fd_pairs[idx].count; |
2882 | for (k = 0; k < indexes_used; k++) { |
2883 | used[si - k] = (pair_start[idx] - k) | |
2884 | ICE_SWAP_VALID; |
2885 | } |
2886 | |
2887 | break; |
2888 | } |
2889 | |
2890 | si -= indexes_used; |
2891 | } |
2892 | |
2893 | /* for each set of 4 swap and 4 inset indexes, write the appropriate |
2894 | * register |
2895 | */ |
2896 | for (j = 0; j < hw->blk[ICE_BLK_FD].es.fvw / 4; j++) { |
2897 | u32 raw_swap = 0; |
2898 | u32 raw_in = 0; |
2899 | |
2900 | for (k = 0; k < 4; k++) { |
2901 | u8 idx; |
2902 | |
2903 | idx = (j * 4) + k; |
2904 | if (used[idx] && !(mask_sel & BIT(idx))) { |
2905 | raw_swap |= used[idx] << (k * BITS_PER_BYTE); |
2906 | #define ICE_INSET_DFLT 0x9f |
2907 | raw_in |= ICE_INSET_DFLT << (k * BITS_PER_BYTE); |
2908 | } |
2909 | } |
2910 | |
2911 | /* write the appropriate swap register set */ |
2912 | wr32(hw, GLQF_FDSWAP(prof_id, j), raw_swap); |
2913 | |
2914 | ice_debug(hw, ICE_DBG_INIT, "swap wr(%d, %d): %x = %08x\n" , |
2915 | prof_id, j, GLQF_FDSWAP(prof_id, j), raw_swap); |
2916 | |
2917 | /* write the appropriate inset register set */ |
2918 | wr32(hw, GLQF_FDINSET(prof_id, j), raw_in); |
2919 | |
2920 | ice_debug(hw, ICE_DBG_INIT, "inset wr(%d, %d): %x = %08x\n" , |
2921 | prof_id, j, GLQF_FDINSET(prof_id, j), raw_in); |
2922 | } |
2923 | |
2924 | /* initially clear the mask select for this profile */ |
2925 | ice_update_fd_mask(hw, prof_id, mask_sel: 0); |
2926 | |
2927 | return 0; |
2928 | } |
2929 | |
2930 | /* The entries here needs to match the order of enum ice_ptype_attrib */ |
2931 | static const struct ice_ptype_attrib_info ice_ptype_attributes[] = { |
2932 | { ICE_GTP_PDU_EH, ICE_GTP_PDU_FLAG_MASK }, |
2933 | { ICE_GTP_SESSION, ICE_GTP_FLAGS_MASK }, |
2934 | { ICE_GTP_DOWNLINK, ICE_GTP_FLAGS_MASK }, |
2935 | { ICE_GTP_UPLINK, ICE_GTP_FLAGS_MASK }, |
2936 | }; |
2937 | |
2938 | /** |
2939 | * ice_get_ptype_attrib_info - get PTYPE attribute information |
2940 | * @type: attribute type |
2941 | * @info: pointer to variable to the attribute information |
2942 | */ |
2943 | static void |
2944 | ice_get_ptype_attrib_info(enum ice_ptype_attrib_type type, |
2945 | struct ice_ptype_attrib_info *info) |
2946 | { |
2947 | *info = ice_ptype_attributes[type]; |
2948 | } |
2949 | |
2950 | /** |
2951 | * ice_add_prof_attrib - add any PTG with attributes to profile |
2952 | * @prof: pointer to the profile to which PTG entries will be added |
2953 | * @ptg: PTG to be added |
2954 | * @ptype: PTYPE that needs to be looked up |
2955 | * @attr: array of attributes that will be considered |
2956 | * @attr_cnt: number of elements in the attribute array |
2957 | */ |
2958 | static int |
2959 | ice_add_prof_attrib(struct ice_prof_map *prof, u8 ptg, u16 ptype, |
2960 | const struct ice_ptype_attributes *attr, u16 attr_cnt) |
2961 | { |
2962 | bool found = false; |
2963 | u16 i; |
2964 | |
2965 | for (i = 0; i < attr_cnt; i++) |
2966 | if (attr[i].ptype == ptype) { |
2967 | found = true; |
2968 | |
2969 | prof->ptg[prof->ptg_cnt] = ptg; |
2970 | ice_get_ptype_attrib_info(type: attr[i].attrib, |
2971 | info: &prof->attr[prof->ptg_cnt]); |
2972 | |
2973 | if (++prof->ptg_cnt >= ICE_MAX_PTG_PER_PROFILE) |
2974 | return -ENOSPC; |
2975 | } |
2976 | |
2977 | if (!found) |
2978 | return -ENOENT; |
2979 | |
2980 | return 0; |
2981 | } |
2982 | |
2983 | /** |
2984 | * ice_add_prof - add profile |
2985 | * @hw: pointer to the HW struct |
2986 | * @blk: hardware block |
2987 | * @id: profile tracking ID |
2988 | * @ptypes: array of bitmaps indicating ptypes (ICE_FLOW_PTYPE_MAX bits) |
2989 | * @attr: array of attributes |
2990 | * @attr_cnt: number of elements in attr array |
2991 | * @es: extraction sequence (length of array is determined by the block) |
2992 | * @masks: mask for extraction sequence |
2993 | * @symm: symmetric setting for RSS profiles |
2994 | * |
2995 | * This function registers a profile, which matches a set of PTYPES with a |
2996 | * particular extraction sequence. While the hardware profile is allocated |
2997 | * it will not be written until the first call to ice_add_flow that specifies |
2998 | * the ID value used here. |
2999 | */ |
3000 | int |
3001 | ice_add_prof(struct ice_hw *hw, enum ice_block blk, u64 id, u8 ptypes[], |
3002 | const struct ice_ptype_attributes *attr, u16 attr_cnt, |
3003 | struct ice_fv_word *es, u16 *masks, bool symm) |
3004 | { |
3005 | u32 bytes = DIV_ROUND_UP(ICE_FLOW_PTYPE_MAX, BITS_PER_BYTE); |
3006 | DECLARE_BITMAP(ptgs_used, ICE_XLT1_CNT); |
3007 | struct ice_prof_map *prof; |
3008 | u8 byte = 0; |
3009 | u8 prof_id; |
3010 | int status; |
3011 | |
3012 | bitmap_zero(dst: ptgs_used, ICE_XLT1_CNT); |
3013 | |
3014 | mutex_lock(&hw->blk[blk].es.prof_map_lock); |
3015 | |
3016 | /* search for existing profile */ |
3017 | status = ice_find_prof_id_with_mask(hw, blk, fv: es, masks, symm, prof_id: &prof_id); |
3018 | if (status) { |
3019 | /* allocate profile ID */ |
3020 | status = ice_alloc_prof_id(hw, blk, prof_id: &prof_id); |
3021 | if (status) |
3022 | goto err_ice_add_prof; |
3023 | if (blk == ICE_BLK_FD) { |
3024 | /* For Flow Director block, the extraction sequence may |
3025 | * need to be altered in the case where there are paired |
3026 | * fields that have no match. This is necessary because |
3027 | * for Flow Director, src and dest fields need to paired |
3028 | * for filter programming and these values are swapped |
3029 | * during Tx. |
3030 | */ |
3031 | status = ice_update_fd_swap(hw, prof_id, es); |
3032 | if (status) |
3033 | goto err_ice_add_prof; |
3034 | } |
3035 | status = ice_update_prof_masking(hw, blk, prof_id, masks); |
3036 | if (status) |
3037 | goto err_ice_add_prof; |
3038 | |
3039 | /* and write new es */ |
3040 | ice_write_es(hw, blk, prof_id, fv: es, symm); |
3041 | } |
3042 | |
3043 | ice_prof_inc_ref(hw, blk, prof_id); |
3044 | |
3045 | /* add profile info */ |
3046 | prof = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*prof), GFP_KERNEL); |
3047 | if (!prof) { |
3048 | status = -ENOMEM; |
3049 | goto err_ice_add_prof; |
3050 | } |
3051 | |
3052 | prof->profile_cookie = id; |
3053 | prof->prof_id = prof_id; |
3054 | prof->ptg_cnt = 0; |
3055 | prof->context = 0; |
3056 | |
3057 | /* build list of ptgs */ |
3058 | while (bytes && prof->ptg_cnt < ICE_MAX_PTG_PER_PROFILE) { |
3059 | u8 bit; |
3060 | |
3061 | if (!ptypes[byte]) { |
3062 | bytes--; |
3063 | byte++; |
3064 | continue; |
3065 | } |
3066 | |
3067 | /* Examine 8 bits per byte */ |
3068 | for_each_set_bit(bit, (unsigned long *)&ptypes[byte], |
3069 | BITS_PER_BYTE) { |
3070 | u16 ptype; |
3071 | u8 ptg; |
3072 | |
3073 | ptype = byte * BITS_PER_BYTE + bit; |
3074 | |
3075 | /* The package should place all ptypes in a non-zero |
3076 | * PTG, so the following call should never fail. |
3077 | */ |
3078 | if (ice_ptg_find_ptype(hw, blk, ptype, ptg: &ptg)) |
3079 | continue; |
3080 | |
3081 | /* If PTG is already added, skip and continue */ |
3082 | if (test_bit(ptg, ptgs_used)) |
3083 | continue; |
3084 | |
3085 | __set_bit(ptg, ptgs_used); |
3086 | /* Check to see there are any attributes for |
3087 | * this PTYPE, and add them if found. |
3088 | */ |
3089 | status = ice_add_prof_attrib(prof, ptg, ptype, |
3090 | attr, attr_cnt); |
3091 | if (status == -ENOSPC) |
3092 | break; |
3093 | if (status) { |
3094 | /* This is simple a PTYPE/PTG with no |
3095 | * attribute |
3096 | */ |
3097 | prof->ptg[prof->ptg_cnt] = ptg; |
3098 | prof->attr[prof->ptg_cnt].flags = 0; |
3099 | prof->attr[prof->ptg_cnt].mask = 0; |
3100 | |
3101 | if (++prof->ptg_cnt >= |
3102 | ICE_MAX_PTG_PER_PROFILE) |
3103 | break; |
3104 | } |
3105 | } |
3106 | |
3107 | bytes--; |
3108 | byte++; |
3109 | } |
3110 | |
3111 | list_add(new: &prof->list, head: &hw->blk[blk].es.prof_map); |
3112 | status = 0; |
3113 | |
3114 | err_ice_add_prof: |
3115 | mutex_unlock(lock: &hw->blk[blk].es.prof_map_lock); |
3116 | return status; |
3117 | } |
3118 | |
3119 | /** |
3120 | * ice_search_prof_id - Search for a profile tracking ID |
3121 | * @hw: pointer to the HW struct |
3122 | * @blk: hardware block |
3123 | * @id: profile tracking ID |
3124 | * |
3125 | * This will search for a profile tracking ID which was previously added. |
3126 | * The profile map lock should be held before calling this function. |
3127 | */ |
3128 | struct ice_prof_map * |
3129 | ice_search_prof_id(struct ice_hw *hw, enum ice_block blk, u64 id) |
3130 | { |
3131 | struct ice_prof_map *entry = NULL; |
3132 | struct ice_prof_map *map; |
3133 | |
3134 | list_for_each_entry(map, &hw->blk[blk].es.prof_map, list) |
3135 | if (map->profile_cookie == id) { |
3136 | entry = map; |
3137 | break; |
3138 | } |
3139 | |
3140 | return entry; |
3141 | } |
3142 | |
3143 | /** |
3144 | * ice_vsig_prof_id_count - count profiles in a VSIG |
3145 | * @hw: pointer to the HW struct |
3146 | * @blk: hardware block |
3147 | * @vsig: VSIG to remove the profile from |
3148 | */ |
3149 | static u16 |
3150 | ice_vsig_prof_id_count(struct ice_hw *hw, enum ice_block blk, u16 vsig) |
3151 | { |
3152 | u16 idx = vsig & ICE_VSIG_IDX_M, count = 0; |
3153 | struct ice_vsig_prof *p; |
3154 | |
3155 | list_for_each_entry(p, &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
3156 | list) |
3157 | count++; |
3158 | |
3159 | return count; |
3160 | } |
3161 | |
3162 | /** |
3163 | * ice_rel_tcam_idx - release a TCAM index |
3164 | * @hw: pointer to the HW struct |
3165 | * @blk: hardware block |
3166 | * @idx: the index to release |
3167 | */ |
3168 | static int ice_rel_tcam_idx(struct ice_hw *hw, enum ice_block blk, u16 idx) |
3169 | { |
3170 | /* Masks to invoke a never match entry */ |
3171 | u8 vl_msk[ICE_TCAM_KEY_VAL_SZ] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
3172 | u8 dc_msk[ICE_TCAM_KEY_VAL_SZ] = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF }; |
3173 | u8 nm_msk[ICE_TCAM_KEY_VAL_SZ] = { 0x01, 0x00, 0x00, 0x00, 0x00 }; |
3174 | int status; |
3175 | |
3176 | /* write the TCAM entry */ |
3177 | status = ice_tcam_write_entry(hw, blk, idx, prof_id: 0, ptg: 0, vsig: 0, cdid: 0, flags: 0, vl_msk, |
3178 | dc_msk, nm_msk); |
3179 | if (status) |
3180 | return status; |
3181 | |
3182 | /* release the TCAM entry */ |
3183 | status = ice_free_tcam_ent(hw, blk, tcam_idx: idx); |
3184 | |
3185 | return status; |
3186 | } |
3187 | |
3188 | /** |
3189 | * ice_rem_prof_id - remove one profile from a VSIG |
3190 | * @hw: pointer to the HW struct |
3191 | * @blk: hardware block |
3192 | * @prof: pointer to profile structure to remove |
3193 | */ |
3194 | static int |
3195 | ice_rem_prof_id(struct ice_hw *hw, enum ice_block blk, |
3196 | struct ice_vsig_prof *prof) |
3197 | { |
3198 | int status; |
3199 | u16 i; |
3200 | |
3201 | for (i = 0; i < prof->tcam_count; i++) |
3202 | if (prof->tcam[i].in_use) { |
3203 | prof->tcam[i].in_use = false; |
3204 | status = ice_rel_tcam_idx(hw, blk, |
3205 | idx: prof->tcam[i].tcam_idx); |
3206 | if (status) |
3207 | return -EIO; |
3208 | } |
3209 | |
3210 | return 0; |
3211 | } |
3212 | |
3213 | /** |
3214 | * ice_rem_vsig - remove VSIG |
3215 | * @hw: pointer to the HW struct |
3216 | * @blk: hardware block |
3217 | * @vsig: the VSIG to remove |
3218 | * @chg: the change list |
3219 | */ |
3220 | static int |
3221 | ice_rem_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsig, |
3222 | struct list_head *chg) |
3223 | { |
3224 | u16 idx = vsig & ICE_VSIG_IDX_M; |
3225 | struct ice_vsig_vsi *vsi_cur; |
3226 | struct ice_vsig_prof *d, *t; |
3227 | |
3228 | /* remove TCAM entries */ |
3229 | list_for_each_entry_safe(d, t, |
3230 | &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
3231 | list) { |
3232 | int status; |
3233 | |
3234 | status = ice_rem_prof_id(hw, blk, prof: d); |
3235 | if (status) |
3236 | return status; |
3237 | |
3238 | list_del(entry: &d->list); |
3239 | devm_kfree(dev: ice_hw_to_dev(hw), p: d); |
3240 | } |
3241 | |
3242 | /* Move all VSIS associated with this VSIG to the default VSIG */ |
3243 | vsi_cur = hw->blk[blk].xlt2.vsig_tbl[idx].first_vsi; |
3244 | /* If the VSIG has at least 1 VSI then iterate through the list |
3245 | * and remove the VSIs before deleting the group. |
3246 | */ |
3247 | if (vsi_cur) |
3248 | do { |
3249 | struct ice_vsig_vsi *tmp = vsi_cur->next_vsi; |
3250 | struct ice_chs_chg *p; |
3251 | |
3252 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), |
3253 | GFP_KERNEL); |
3254 | if (!p) |
3255 | return -ENOMEM; |
3256 | |
3257 | p->type = ICE_VSIG_REM; |
3258 | p->orig_vsig = vsig; |
3259 | p->vsig = ICE_DEFAULT_VSIG; |
3260 | p->vsi = vsi_cur - hw->blk[blk].xlt2.vsis; |
3261 | |
3262 | list_add(new: &p->list_entry, head: chg); |
3263 | |
3264 | vsi_cur = tmp; |
3265 | } while (vsi_cur); |
3266 | |
3267 | return ice_vsig_free(hw, blk, vsig); |
3268 | } |
3269 | |
3270 | /** |
3271 | * ice_rem_prof_id_vsig - remove a specific profile from a VSIG |
3272 | * @hw: pointer to the HW struct |
3273 | * @blk: hardware block |
3274 | * @vsig: VSIG to remove the profile from |
3275 | * @hdl: profile handle indicating which profile to remove |
3276 | * @chg: list to receive a record of changes |
3277 | */ |
3278 | static int |
3279 | ice_rem_prof_id_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsig, u64 hdl, |
3280 | struct list_head *chg) |
3281 | { |
3282 | u16 idx = vsig & ICE_VSIG_IDX_M; |
3283 | struct ice_vsig_prof *p, *t; |
3284 | |
3285 | list_for_each_entry_safe(p, t, |
3286 | &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
3287 | list) |
3288 | if (p->profile_cookie == hdl) { |
3289 | int status; |
3290 | |
3291 | if (ice_vsig_prof_id_count(hw, blk, vsig) == 1) |
3292 | /* this is the last profile, remove the VSIG */ |
3293 | return ice_rem_vsig(hw, blk, vsig, chg); |
3294 | |
3295 | status = ice_rem_prof_id(hw, blk, prof: p); |
3296 | if (!status) { |
3297 | list_del(entry: &p->list); |
3298 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
3299 | } |
3300 | return status; |
3301 | } |
3302 | |
3303 | return -ENOENT; |
3304 | } |
3305 | |
3306 | /** |
3307 | * ice_rem_flow_all - remove all flows with a particular profile |
3308 | * @hw: pointer to the HW struct |
3309 | * @blk: hardware block |
3310 | * @id: profile tracking ID |
3311 | */ |
3312 | static int ice_rem_flow_all(struct ice_hw *hw, enum ice_block blk, u64 id) |
3313 | { |
3314 | struct ice_chs_chg *del, *tmp; |
3315 | struct list_head chg; |
3316 | int status; |
3317 | u16 i; |
3318 | |
3319 | INIT_LIST_HEAD(list: &chg); |
3320 | |
3321 | for (i = 1; i < ICE_MAX_VSIGS; i++) |
3322 | if (hw->blk[blk].xlt2.vsig_tbl[i].in_use) { |
3323 | if (ice_has_prof_vsig(hw, blk, vsig: i, hdl: id)) { |
3324 | status = ice_rem_prof_id_vsig(hw, blk, vsig: i, hdl: id, |
3325 | chg: &chg); |
3326 | if (status) |
3327 | goto err_ice_rem_flow_all; |
3328 | } |
3329 | } |
3330 | |
3331 | status = ice_upd_prof_hw(hw, blk, chgs: &chg); |
3332 | |
3333 | err_ice_rem_flow_all: |
3334 | list_for_each_entry_safe(del, tmp, &chg, list_entry) { |
3335 | list_del(entry: &del->list_entry); |
3336 | devm_kfree(dev: ice_hw_to_dev(hw), p: del); |
3337 | } |
3338 | |
3339 | return status; |
3340 | } |
3341 | |
3342 | /** |
3343 | * ice_rem_prof - remove profile |
3344 | * @hw: pointer to the HW struct |
3345 | * @blk: hardware block |
3346 | * @id: profile tracking ID |
3347 | * |
3348 | * This will remove the profile specified by the ID parameter, which was |
3349 | * previously created through ice_add_prof. If any existing entries |
3350 | * are associated with this profile, they will be removed as well. |
3351 | */ |
3352 | int ice_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 id) |
3353 | { |
3354 | struct ice_prof_map *pmap; |
3355 | int status; |
3356 | |
3357 | mutex_lock(&hw->blk[blk].es.prof_map_lock); |
3358 | |
3359 | pmap = ice_search_prof_id(hw, blk, id); |
3360 | if (!pmap) { |
3361 | status = -ENOENT; |
3362 | goto err_ice_rem_prof; |
3363 | } |
3364 | |
3365 | /* remove all flows with this profile */ |
3366 | status = ice_rem_flow_all(hw, blk, id: pmap->profile_cookie); |
3367 | if (status) |
3368 | goto err_ice_rem_prof; |
3369 | |
3370 | /* dereference profile, and possibly remove */ |
3371 | ice_prof_dec_ref(hw, blk, prof_id: pmap->prof_id); |
3372 | |
3373 | list_del(entry: &pmap->list); |
3374 | devm_kfree(dev: ice_hw_to_dev(hw), p: pmap); |
3375 | |
3376 | err_ice_rem_prof: |
3377 | mutex_unlock(lock: &hw->blk[blk].es.prof_map_lock); |
3378 | return status; |
3379 | } |
3380 | |
3381 | /** |
3382 | * ice_get_prof - get profile |
3383 | * @hw: pointer to the HW struct |
3384 | * @blk: hardware block |
3385 | * @hdl: profile handle |
3386 | * @chg: change list |
3387 | */ |
3388 | static int |
3389 | ice_get_prof(struct ice_hw *hw, enum ice_block blk, u64 hdl, |
3390 | struct list_head *chg) |
3391 | { |
3392 | struct ice_prof_map *map; |
3393 | struct ice_chs_chg *p; |
3394 | int status = 0; |
3395 | u16 i; |
3396 | |
3397 | mutex_lock(&hw->blk[blk].es.prof_map_lock); |
3398 | /* Get the details on the profile specified by the handle ID */ |
3399 | map = ice_search_prof_id(hw, blk, id: hdl); |
3400 | if (!map) { |
3401 | status = -ENOENT; |
3402 | goto err_ice_get_prof; |
3403 | } |
3404 | |
3405 | for (i = 0; i < map->ptg_cnt; i++) |
3406 | if (!hw->blk[blk].es.written[map->prof_id]) { |
3407 | /* add ES to change list */ |
3408 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), |
3409 | GFP_KERNEL); |
3410 | if (!p) { |
3411 | status = -ENOMEM; |
3412 | goto err_ice_get_prof; |
3413 | } |
3414 | |
3415 | p->type = ICE_PTG_ES_ADD; |
3416 | p->ptype = 0; |
3417 | p->ptg = map->ptg[i]; |
3418 | p->add_ptg = 0; |
3419 | |
3420 | p->add_prof = 1; |
3421 | p->prof_id = map->prof_id; |
3422 | |
3423 | hw->blk[blk].es.written[map->prof_id] = true; |
3424 | |
3425 | list_add(new: &p->list_entry, head: chg); |
3426 | } |
3427 | |
3428 | err_ice_get_prof: |
3429 | mutex_unlock(lock: &hw->blk[blk].es.prof_map_lock); |
3430 | /* let caller clean up the change list */ |
3431 | return status; |
3432 | } |
3433 | |
3434 | /** |
3435 | * ice_get_profs_vsig - get a copy of the list of profiles from a VSIG |
3436 | * @hw: pointer to the HW struct |
3437 | * @blk: hardware block |
3438 | * @vsig: VSIG from which to copy the list |
3439 | * @lst: output list |
3440 | * |
3441 | * This routine makes a copy of the list of profiles in the specified VSIG. |
3442 | */ |
3443 | static int |
3444 | ice_get_profs_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsig, |
3445 | struct list_head *lst) |
3446 | { |
3447 | struct ice_vsig_prof *ent1, *ent2; |
3448 | u16 idx = vsig & ICE_VSIG_IDX_M; |
3449 | |
3450 | list_for_each_entry(ent1, &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
3451 | list) { |
3452 | struct ice_vsig_prof *p; |
3453 | |
3454 | /* copy to the input list */ |
3455 | p = devm_kmemdup(dev: ice_hw_to_dev(hw), src: ent1, len: sizeof(*p), |
3456 | GFP_KERNEL); |
3457 | if (!p) |
3458 | goto err_ice_get_profs_vsig; |
3459 | |
3460 | list_add_tail(new: &p->list, head: lst); |
3461 | } |
3462 | |
3463 | return 0; |
3464 | |
3465 | err_ice_get_profs_vsig: |
3466 | list_for_each_entry_safe(ent1, ent2, lst, list) { |
3467 | list_del(entry: &ent1->list); |
3468 | devm_kfree(dev: ice_hw_to_dev(hw), p: ent1); |
3469 | } |
3470 | |
3471 | return -ENOMEM; |
3472 | } |
3473 | |
3474 | /** |
3475 | * ice_add_prof_to_lst - add profile entry to a list |
3476 | * @hw: pointer to the HW struct |
3477 | * @blk: hardware block |
3478 | * @lst: the list to be added to |
3479 | * @hdl: profile handle of entry to add |
3480 | */ |
3481 | static int |
3482 | ice_add_prof_to_lst(struct ice_hw *hw, enum ice_block blk, |
3483 | struct list_head *lst, u64 hdl) |
3484 | { |
3485 | struct ice_prof_map *map; |
3486 | struct ice_vsig_prof *p; |
3487 | int status = 0; |
3488 | u16 i; |
3489 | |
3490 | mutex_lock(&hw->blk[blk].es.prof_map_lock); |
3491 | map = ice_search_prof_id(hw, blk, id: hdl); |
3492 | if (!map) { |
3493 | status = -ENOENT; |
3494 | goto err_ice_add_prof_to_lst; |
3495 | } |
3496 | |
3497 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), GFP_KERNEL); |
3498 | if (!p) { |
3499 | status = -ENOMEM; |
3500 | goto err_ice_add_prof_to_lst; |
3501 | } |
3502 | |
3503 | p->profile_cookie = map->profile_cookie; |
3504 | p->prof_id = map->prof_id; |
3505 | p->tcam_count = map->ptg_cnt; |
3506 | |
3507 | for (i = 0; i < map->ptg_cnt; i++) { |
3508 | p->tcam[i].prof_id = map->prof_id; |
3509 | p->tcam[i].tcam_idx = ICE_INVALID_TCAM; |
3510 | p->tcam[i].ptg = map->ptg[i]; |
3511 | } |
3512 | |
3513 | list_add(new: &p->list, head: lst); |
3514 | |
3515 | err_ice_add_prof_to_lst: |
3516 | mutex_unlock(lock: &hw->blk[blk].es.prof_map_lock); |
3517 | return status; |
3518 | } |
3519 | |
3520 | /** |
3521 | * ice_move_vsi - move VSI to another VSIG |
3522 | * @hw: pointer to the HW struct |
3523 | * @blk: hardware block |
3524 | * @vsi: the VSI to move |
3525 | * @vsig: the VSIG to move the VSI to |
3526 | * @chg: the change list |
3527 | */ |
3528 | static int |
3529 | ice_move_vsi(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 vsig, |
3530 | struct list_head *chg) |
3531 | { |
3532 | struct ice_chs_chg *p; |
3533 | u16 orig_vsig; |
3534 | int status; |
3535 | |
3536 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), GFP_KERNEL); |
3537 | if (!p) |
3538 | return -ENOMEM; |
3539 | |
3540 | status = ice_vsig_find_vsi(hw, blk, vsi, vsig: &orig_vsig); |
3541 | if (!status) |
3542 | status = ice_vsig_add_mv_vsi(hw, blk, vsi, vsig); |
3543 | |
3544 | if (status) { |
3545 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
3546 | return status; |
3547 | } |
3548 | |
3549 | p->type = ICE_VSI_MOVE; |
3550 | p->vsi = vsi; |
3551 | p->orig_vsig = orig_vsig; |
3552 | p->vsig = vsig; |
3553 | |
3554 | list_add(new: &p->list_entry, head: chg); |
3555 | |
3556 | return 0; |
3557 | } |
3558 | |
3559 | /** |
3560 | * ice_rem_chg_tcam_ent - remove a specific TCAM entry from change list |
3561 | * @hw: pointer to the HW struct |
3562 | * @idx: the index of the TCAM entry to remove |
3563 | * @chg: the list of change structures to search |
3564 | */ |
3565 | static void |
3566 | ice_rem_chg_tcam_ent(struct ice_hw *hw, u16 idx, struct list_head *chg) |
3567 | { |
3568 | struct ice_chs_chg *pos, *tmp; |
3569 | |
3570 | list_for_each_entry_safe(tmp, pos, chg, list_entry) |
3571 | if (tmp->type == ICE_TCAM_ADD && tmp->tcam_idx == idx) { |
3572 | list_del(entry: &tmp->list_entry); |
3573 | devm_kfree(dev: ice_hw_to_dev(hw), p: tmp); |
3574 | } |
3575 | } |
3576 | |
3577 | /** |
3578 | * ice_prof_tcam_ena_dis - add enable or disable TCAM change |
3579 | * @hw: pointer to the HW struct |
3580 | * @blk: hardware block |
3581 | * @enable: true to enable, false to disable |
3582 | * @vsig: the VSIG of the TCAM entry |
3583 | * @tcam: pointer the TCAM info structure of the TCAM to disable |
3584 | * @chg: the change list |
3585 | * |
3586 | * This function appends an enable or disable TCAM entry in the change log |
3587 | */ |
3588 | static int |
3589 | ice_prof_tcam_ena_dis(struct ice_hw *hw, enum ice_block blk, bool enable, |
3590 | u16 vsig, struct ice_tcam_inf *tcam, |
3591 | struct list_head *chg) |
3592 | { |
3593 | struct ice_chs_chg *p; |
3594 | int status; |
3595 | |
3596 | u8 vl_msk[ICE_TCAM_KEY_VAL_SZ] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
3597 | u8 dc_msk[ICE_TCAM_KEY_VAL_SZ] = { 0xFF, 0xFF, 0x00, 0x00, 0x00 }; |
3598 | u8 nm_msk[ICE_TCAM_KEY_VAL_SZ] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; |
3599 | |
3600 | /* if disabling, free the TCAM */ |
3601 | if (!enable) { |
3602 | status = ice_rel_tcam_idx(hw, blk, idx: tcam->tcam_idx); |
3603 | |
3604 | /* if we have already created a change for this TCAM entry, then |
3605 | * we need to remove that entry, in order to prevent writing to |
3606 | * a TCAM entry we no longer will have ownership of. |
3607 | */ |
3608 | ice_rem_chg_tcam_ent(hw, idx: tcam->tcam_idx, chg); |
3609 | tcam->tcam_idx = 0; |
3610 | tcam->in_use = 0; |
3611 | return status; |
3612 | } |
3613 | |
3614 | /* for re-enabling, reallocate a TCAM */ |
3615 | /* for entries with empty attribute masks, allocate entry from |
3616 | * the bottom of the TCAM table; otherwise, allocate from the |
3617 | * top of the table in order to give it higher priority |
3618 | */ |
3619 | status = ice_alloc_tcam_ent(hw, blk, btm: tcam->attr.mask == 0, |
3620 | tcam_idx: &tcam->tcam_idx); |
3621 | if (status) |
3622 | return status; |
3623 | |
3624 | /* add TCAM to change list */ |
3625 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), GFP_KERNEL); |
3626 | if (!p) |
3627 | return -ENOMEM; |
3628 | |
3629 | status = ice_tcam_write_entry(hw, blk, idx: tcam->tcam_idx, prof_id: tcam->prof_id, |
3630 | ptg: tcam->ptg, vsig, cdid: 0, flags: tcam->attr.flags, |
3631 | vl_msk, dc_msk, nm_msk); |
3632 | if (status) |
3633 | goto err_ice_prof_tcam_ena_dis; |
3634 | |
3635 | tcam->in_use = 1; |
3636 | |
3637 | p->type = ICE_TCAM_ADD; |
3638 | p->add_tcam_idx = true; |
3639 | p->prof_id = tcam->prof_id; |
3640 | p->ptg = tcam->ptg; |
3641 | p->vsig = 0; |
3642 | p->tcam_idx = tcam->tcam_idx; |
3643 | |
3644 | /* log change */ |
3645 | list_add(new: &p->list_entry, head: chg); |
3646 | |
3647 | return 0; |
3648 | |
3649 | err_ice_prof_tcam_ena_dis: |
3650 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
3651 | return status; |
3652 | } |
3653 | |
3654 | /** |
3655 | * ice_adj_prof_priorities - adjust profile based on priorities |
3656 | * @hw: pointer to the HW struct |
3657 | * @blk: hardware block |
3658 | * @vsig: the VSIG for which to adjust profile priorities |
3659 | * @chg: the change list |
3660 | */ |
3661 | static int |
3662 | ice_adj_prof_priorities(struct ice_hw *hw, enum ice_block blk, u16 vsig, |
3663 | struct list_head *chg) |
3664 | { |
3665 | DECLARE_BITMAP(ptgs_used, ICE_XLT1_CNT); |
3666 | struct ice_vsig_prof *t; |
3667 | int status; |
3668 | u16 idx; |
3669 | |
3670 | bitmap_zero(dst: ptgs_used, ICE_XLT1_CNT); |
3671 | idx = vsig & ICE_VSIG_IDX_M; |
3672 | |
3673 | /* Priority is based on the order in which the profiles are added. The |
3674 | * newest added profile has highest priority and the oldest added |
3675 | * profile has the lowest priority. Since the profile property list for |
3676 | * a VSIG is sorted from newest to oldest, this code traverses the list |
3677 | * in order and enables the first of each PTG that it finds (that is not |
3678 | * already enabled); it also disables any duplicate PTGs that it finds |
3679 | * in the older profiles (that are currently enabled). |
3680 | */ |
3681 | |
3682 | list_for_each_entry(t, &hw->blk[blk].xlt2.vsig_tbl[idx].prop_lst, |
3683 | list) { |
3684 | u16 i; |
3685 | |
3686 | for (i = 0; i < t->tcam_count; i++) { |
3687 | /* Scan the priorities from newest to oldest. |
3688 | * Make sure that the newest profiles take priority. |
3689 | */ |
3690 | if (test_bit(t->tcam[i].ptg, ptgs_used) && |
3691 | t->tcam[i].in_use) { |
3692 | /* need to mark this PTG as never match, as it |
3693 | * was already in use and therefore duplicate |
3694 | * (and lower priority) |
3695 | */ |
3696 | status = ice_prof_tcam_ena_dis(hw, blk, enable: false, |
3697 | vsig, |
3698 | tcam: &t->tcam[i], |
3699 | chg); |
3700 | if (status) |
3701 | return status; |
3702 | } else if (!test_bit(t->tcam[i].ptg, ptgs_used) && |
3703 | !t->tcam[i].in_use) { |
3704 | /* need to enable this PTG, as it in not in use |
3705 | * and not enabled (highest priority) |
3706 | */ |
3707 | status = ice_prof_tcam_ena_dis(hw, blk, enable: true, |
3708 | vsig, |
3709 | tcam: &t->tcam[i], |
3710 | chg); |
3711 | if (status) |
3712 | return status; |
3713 | } |
3714 | |
3715 | /* keep track of used ptgs */ |
3716 | __set_bit(t->tcam[i].ptg, ptgs_used); |
3717 | } |
3718 | } |
3719 | |
3720 | return 0; |
3721 | } |
3722 | |
3723 | /** |
3724 | * ice_add_prof_id_vsig - add profile to VSIG |
3725 | * @hw: pointer to the HW struct |
3726 | * @blk: hardware block |
3727 | * @vsig: the VSIG to which this profile is to be added |
3728 | * @hdl: the profile handle indicating the profile to add |
3729 | * @rev: true to add entries to the end of the list |
3730 | * @chg: the change list |
3731 | */ |
3732 | static int |
3733 | ice_add_prof_id_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsig, u64 hdl, |
3734 | bool rev, struct list_head *chg) |
3735 | { |
3736 | /* Masks that ignore flags */ |
3737 | u8 vl_msk[ICE_TCAM_KEY_VAL_SZ] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
3738 | u8 dc_msk[ICE_TCAM_KEY_VAL_SZ] = { 0xFF, 0xFF, 0x00, 0x00, 0x00 }; |
3739 | u8 nm_msk[ICE_TCAM_KEY_VAL_SZ] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; |
3740 | struct ice_prof_map *map; |
3741 | struct ice_vsig_prof *t; |
3742 | struct ice_chs_chg *p; |
3743 | u16 vsig_idx, i; |
3744 | int status = 0; |
3745 | |
3746 | /* Error, if this VSIG already has this profile */ |
3747 | if (ice_has_prof_vsig(hw, blk, vsig, hdl)) |
3748 | return -EEXIST; |
3749 | |
3750 | /* new VSIG profile structure */ |
3751 | t = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*t), GFP_KERNEL); |
3752 | if (!t) |
3753 | return -ENOMEM; |
3754 | |
3755 | mutex_lock(&hw->blk[blk].es.prof_map_lock); |
3756 | /* Get the details on the profile specified by the handle ID */ |
3757 | map = ice_search_prof_id(hw, blk, id: hdl); |
3758 | if (!map) { |
3759 | status = -ENOENT; |
3760 | goto err_ice_add_prof_id_vsig; |
3761 | } |
3762 | |
3763 | t->profile_cookie = map->profile_cookie; |
3764 | t->prof_id = map->prof_id; |
3765 | t->tcam_count = map->ptg_cnt; |
3766 | |
3767 | /* create TCAM entries */ |
3768 | for (i = 0; i < map->ptg_cnt; i++) { |
3769 | u16 tcam_idx; |
3770 | |
3771 | /* add TCAM to change list */ |
3772 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), GFP_KERNEL); |
3773 | if (!p) { |
3774 | status = -ENOMEM; |
3775 | goto err_ice_add_prof_id_vsig; |
3776 | } |
3777 | |
3778 | /* allocate the TCAM entry index */ |
3779 | /* for entries with empty attribute masks, allocate entry from |
3780 | * the bottom of the TCAM table; otherwise, allocate from the |
3781 | * top of the table in order to give it higher priority |
3782 | */ |
3783 | status = ice_alloc_tcam_ent(hw, blk, btm: map->attr[i].mask == 0, |
3784 | tcam_idx: &tcam_idx); |
3785 | if (status) { |
3786 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
3787 | goto err_ice_add_prof_id_vsig; |
3788 | } |
3789 | |
3790 | t->tcam[i].ptg = map->ptg[i]; |
3791 | t->tcam[i].prof_id = map->prof_id; |
3792 | t->tcam[i].tcam_idx = tcam_idx; |
3793 | t->tcam[i].attr = map->attr[i]; |
3794 | t->tcam[i].in_use = true; |
3795 | |
3796 | p->type = ICE_TCAM_ADD; |
3797 | p->add_tcam_idx = true; |
3798 | p->prof_id = t->tcam[i].prof_id; |
3799 | p->ptg = t->tcam[i].ptg; |
3800 | p->vsig = vsig; |
3801 | p->tcam_idx = t->tcam[i].tcam_idx; |
3802 | |
3803 | /* write the TCAM entry */ |
3804 | status = ice_tcam_write_entry(hw, blk, idx: t->tcam[i].tcam_idx, |
3805 | prof_id: t->tcam[i].prof_id, |
3806 | ptg: t->tcam[i].ptg, vsig, cdid: 0, flags: 0, |
3807 | vl_msk, dc_msk, nm_msk); |
3808 | if (status) { |
3809 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
3810 | goto err_ice_add_prof_id_vsig; |
3811 | } |
3812 | |
3813 | /* log change */ |
3814 | list_add(new: &p->list_entry, head: chg); |
3815 | } |
3816 | |
3817 | /* add profile to VSIG */ |
3818 | vsig_idx = vsig & ICE_VSIG_IDX_M; |
3819 | if (rev) |
3820 | list_add_tail(new: &t->list, |
3821 | head: &hw->blk[blk].xlt2.vsig_tbl[vsig_idx].prop_lst); |
3822 | else |
3823 | list_add(new: &t->list, |
3824 | head: &hw->blk[blk].xlt2.vsig_tbl[vsig_idx].prop_lst); |
3825 | |
3826 | mutex_unlock(lock: &hw->blk[blk].es.prof_map_lock); |
3827 | return status; |
3828 | |
3829 | err_ice_add_prof_id_vsig: |
3830 | mutex_unlock(lock: &hw->blk[blk].es.prof_map_lock); |
3831 | /* let caller clean up the change list */ |
3832 | devm_kfree(dev: ice_hw_to_dev(hw), p: t); |
3833 | return status; |
3834 | } |
3835 | |
3836 | /** |
3837 | * ice_create_prof_id_vsig - add a new VSIG with a single profile |
3838 | * @hw: pointer to the HW struct |
3839 | * @blk: hardware block |
3840 | * @vsi: the initial VSI that will be in VSIG |
3841 | * @hdl: the profile handle of the profile that will be added to the VSIG |
3842 | * @chg: the change list |
3843 | */ |
3844 | static int |
3845 | ice_create_prof_id_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsi, u64 hdl, |
3846 | struct list_head *chg) |
3847 | { |
3848 | struct ice_chs_chg *p; |
3849 | u16 new_vsig; |
3850 | int status; |
3851 | |
3852 | p = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*p), GFP_KERNEL); |
3853 | if (!p) |
3854 | return -ENOMEM; |
3855 | |
3856 | new_vsig = ice_vsig_alloc(hw, blk); |
3857 | if (!new_vsig) { |
3858 | status = -EIO; |
3859 | goto err_ice_create_prof_id_vsig; |
3860 | } |
3861 | |
3862 | status = ice_move_vsi(hw, blk, vsi, vsig: new_vsig, chg); |
3863 | if (status) |
3864 | goto err_ice_create_prof_id_vsig; |
3865 | |
3866 | status = ice_add_prof_id_vsig(hw, blk, vsig: new_vsig, hdl, rev: false, chg); |
3867 | if (status) |
3868 | goto err_ice_create_prof_id_vsig; |
3869 | |
3870 | p->type = ICE_VSIG_ADD; |
3871 | p->vsi = vsi; |
3872 | p->orig_vsig = ICE_DEFAULT_VSIG; |
3873 | p->vsig = new_vsig; |
3874 | |
3875 | list_add(new: &p->list_entry, head: chg); |
3876 | |
3877 | return 0; |
3878 | |
3879 | err_ice_create_prof_id_vsig: |
3880 | /* let caller clean up the change list */ |
3881 | devm_kfree(dev: ice_hw_to_dev(hw), p); |
3882 | return status; |
3883 | } |
3884 | |
3885 | /** |
3886 | * ice_create_vsig_from_lst - create a new VSIG with a list of profiles |
3887 | * @hw: pointer to the HW struct |
3888 | * @blk: hardware block |
3889 | * @vsi: the initial VSI that will be in VSIG |
3890 | * @lst: the list of profile that will be added to the VSIG |
3891 | * @new_vsig: return of new VSIG |
3892 | * @chg: the change list |
3893 | */ |
3894 | static int |
3895 | ice_create_vsig_from_lst(struct ice_hw *hw, enum ice_block blk, u16 vsi, |
3896 | struct list_head *lst, u16 *new_vsig, |
3897 | struct list_head *chg) |
3898 | { |
3899 | struct ice_vsig_prof *t; |
3900 | int status; |
3901 | u16 vsig; |
3902 | |
3903 | vsig = ice_vsig_alloc(hw, blk); |
3904 | if (!vsig) |
3905 | return -EIO; |
3906 | |
3907 | status = ice_move_vsi(hw, blk, vsi, vsig, chg); |
3908 | if (status) |
3909 | return status; |
3910 | |
3911 | list_for_each_entry(t, lst, list) { |
3912 | /* Reverse the order here since we are copying the list */ |
3913 | status = ice_add_prof_id_vsig(hw, blk, vsig, hdl: t->profile_cookie, |
3914 | rev: true, chg); |
3915 | if (status) |
3916 | return status; |
3917 | } |
3918 | |
3919 | *new_vsig = vsig; |
3920 | |
3921 | return 0; |
3922 | } |
3923 | |
3924 | /** |
3925 | * ice_find_prof_vsig - find a VSIG with a specific profile handle |
3926 | * @hw: pointer to the HW struct |
3927 | * @blk: hardware block |
3928 | * @hdl: the profile handle of the profile to search for |
3929 | * @vsig: returns the VSIG with the matching profile |
3930 | */ |
3931 | static bool |
3932 | ice_find_prof_vsig(struct ice_hw *hw, enum ice_block blk, u64 hdl, u16 *vsig) |
3933 | { |
3934 | struct ice_vsig_prof *t; |
3935 | struct list_head lst; |
3936 | int status; |
3937 | |
3938 | INIT_LIST_HEAD(list: &lst); |
3939 | |
3940 | t = kzalloc(size: sizeof(*t), GFP_KERNEL); |
3941 | if (!t) |
3942 | return false; |
3943 | |
3944 | t->profile_cookie = hdl; |
3945 | list_add(new: &t->list, head: &lst); |
3946 | |
3947 | status = ice_find_dup_props_vsig(hw, blk, chs: &lst, vsig); |
3948 | |
3949 | list_del(entry: &t->list); |
3950 | kfree(objp: t); |
3951 | |
3952 | return !status; |
3953 | } |
3954 | |
3955 | /** |
3956 | * ice_add_prof_id_flow - add profile flow |
3957 | * @hw: pointer to the HW struct |
3958 | * @blk: hardware block |
3959 | * @vsi: the VSI to enable with the profile specified by ID |
3960 | * @hdl: profile handle |
3961 | * |
3962 | * Calling this function will update the hardware tables to enable the |
3963 | * profile indicated by the ID parameter for the VSIs specified in the VSI |
3964 | * array. Once successfully called, the flow will be enabled. |
3965 | */ |
3966 | int |
3967 | ice_add_prof_id_flow(struct ice_hw *hw, enum ice_block blk, u16 vsi, u64 hdl) |
3968 | { |
3969 | struct ice_vsig_prof *tmp1, *del1; |
3970 | struct ice_chs_chg *tmp, *del; |
3971 | struct list_head union_lst; |
3972 | struct list_head chg; |
3973 | int status; |
3974 | u16 vsig; |
3975 | |
3976 | INIT_LIST_HEAD(list: &union_lst); |
3977 | INIT_LIST_HEAD(list: &chg); |
3978 | |
3979 | /* Get profile */ |
3980 | status = ice_get_prof(hw, blk, hdl, chg: &chg); |
3981 | if (status) |
3982 | return status; |
3983 | |
3984 | /* determine if VSI is already part of a VSIG */ |
3985 | status = ice_vsig_find_vsi(hw, blk, vsi, vsig: &vsig); |
3986 | if (!status && vsig) { |
3987 | bool only_vsi; |
3988 | u16 or_vsig; |
3989 | u16 ref; |
3990 | |
3991 | /* found in VSIG */ |
3992 | or_vsig = vsig; |
3993 | |
3994 | /* make sure that there is no overlap/conflict between the new |
3995 | * characteristics and the existing ones; we don't support that |
3996 | * scenario |
3997 | */ |
3998 | if (ice_has_prof_vsig(hw, blk, vsig, hdl)) { |
3999 | status = -EEXIST; |
4000 | goto err_ice_add_prof_id_flow; |
4001 | } |
4002 | |
4003 | /* last VSI in the VSIG? */ |
4004 | status = ice_vsig_get_ref(hw, blk, vsig, refs: &ref); |
4005 | if (status) |
4006 | goto err_ice_add_prof_id_flow; |
4007 | only_vsi = (ref == 1); |
4008 | |
4009 | /* create a union of the current profiles and the one being |
4010 | * added |
4011 | */ |
4012 | status = ice_get_profs_vsig(hw, blk, vsig, lst: &union_lst); |
4013 | if (status) |
4014 | goto err_ice_add_prof_id_flow; |
4015 | |
4016 | status = ice_add_prof_to_lst(hw, blk, lst: &union_lst, hdl); |
4017 | if (status) |
4018 | goto err_ice_add_prof_id_flow; |
4019 | |
4020 | /* search for an existing VSIG with an exact charc match */ |
4021 | status = ice_find_dup_props_vsig(hw, blk, chs: &union_lst, vsig: &vsig); |
4022 | if (!status) { |
4023 | /* move VSI to the VSIG that matches */ |
4024 | status = ice_move_vsi(hw, blk, vsi, vsig, chg: &chg); |
4025 | if (status) |
4026 | goto err_ice_add_prof_id_flow; |
4027 | |
4028 | /* VSI has been moved out of or_vsig. If the or_vsig had |
4029 | * only that VSI it is now empty and can be removed. |
4030 | */ |
4031 | if (only_vsi) { |
4032 | status = ice_rem_vsig(hw, blk, vsig: or_vsig, chg: &chg); |
4033 | if (status) |
4034 | goto err_ice_add_prof_id_flow; |
4035 | } |
4036 | } else if (only_vsi) { |
4037 | /* If the original VSIG only contains one VSI, then it |
4038 | * will be the requesting VSI. In this case the VSI is |
4039 | * not sharing entries and we can simply add the new |
4040 | * profile to the VSIG. |
4041 | */ |
4042 | status = ice_add_prof_id_vsig(hw, blk, vsig, hdl, rev: false, |
4043 | chg: &chg); |
4044 | if (status) |
4045 | goto err_ice_add_prof_id_flow; |
4046 | |
4047 | /* Adjust priorities */ |
4048 | status = ice_adj_prof_priorities(hw, blk, vsig, chg: &chg); |
4049 | if (status) |
4050 | goto err_ice_add_prof_id_flow; |
4051 | } else { |
4052 | /* No match, so we need a new VSIG */ |
4053 | status = ice_create_vsig_from_lst(hw, blk, vsi, |
4054 | lst: &union_lst, new_vsig: &vsig, |
4055 | chg: &chg); |
4056 | if (status) |
4057 | goto err_ice_add_prof_id_flow; |
4058 | |
4059 | /* Adjust priorities */ |
4060 | status = ice_adj_prof_priorities(hw, blk, vsig, chg: &chg); |
4061 | if (status) |
4062 | goto err_ice_add_prof_id_flow; |
4063 | } |
4064 | } else { |
4065 | /* need to find or add a VSIG */ |
4066 | /* search for an existing VSIG with an exact charc match */ |
4067 | if (ice_find_prof_vsig(hw, blk, hdl, vsig: &vsig)) { |
4068 | /* found an exact match */ |
4069 | /* add or move VSI to the VSIG that matches */ |
4070 | status = ice_move_vsi(hw, blk, vsi, vsig, chg: &chg); |
4071 | if (status) |
4072 | goto err_ice_add_prof_id_flow; |
4073 | } else { |
4074 | /* we did not find an exact match */ |
4075 | /* we need to add a VSIG */ |
4076 | status = ice_create_prof_id_vsig(hw, blk, vsi, hdl, |
4077 | chg: &chg); |
4078 | if (status) |
4079 | goto err_ice_add_prof_id_flow; |
4080 | } |
4081 | } |
4082 | |
4083 | /* update hardware */ |
4084 | if (!status) |
4085 | status = ice_upd_prof_hw(hw, blk, chgs: &chg); |
4086 | |
4087 | err_ice_add_prof_id_flow: |
4088 | list_for_each_entry_safe(del, tmp, &chg, list_entry) { |
4089 | list_del(entry: &del->list_entry); |
4090 | devm_kfree(dev: ice_hw_to_dev(hw), p: del); |
4091 | } |
4092 | |
4093 | list_for_each_entry_safe(del1, tmp1, &union_lst, list) { |
4094 | list_del(entry: &del1->list); |
4095 | devm_kfree(dev: ice_hw_to_dev(hw), p: del1); |
4096 | } |
4097 | |
4098 | return status; |
4099 | } |
4100 | |
4101 | /** |
4102 | * ice_rem_prof_from_list - remove a profile from list |
4103 | * @hw: pointer to the HW struct |
4104 | * @lst: list to remove the profile from |
4105 | * @hdl: the profile handle indicating the profile to remove |
4106 | */ |
4107 | static int |
4108 | ice_rem_prof_from_list(struct ice_hw *hw, struct list_head *lst, u64 hdl) |
4109 | { |
4110 | struct ice_vsig_prof *ent, *tmp; |
4111 | |
4112 | list_for_each_entry_safe(ent, tmp, lst, list) |
4113 | if (ent->profile_cookie == hdl) { |
4114 | list_del(entry: &ent->list); |
4115 | devm_kfree(dev: ice_hw_to_dev(hw), p: ent); |
4116 | return 0; |
4117 | } |
4118 | |
4119 | return -ENOENT; |
4120 | } |
4121 | |
4122 | /** |
4123 | * ice_rem_prof_id_flow - remove flow |
4124 | * @hw: pointer to the HW struct |
4125 | * @blk: hardware block |
4126 | * @vsi: the VSI from which to remove the profile specified by ID |
4127 | * @hdl: profile tracking handle |
4128 | * |
4129 | * Calling this function will update the hardware tables to remove the |
4130 | * profile indicated by the ID parameter for the VSIs specified in the VSI |
4131 | * array. Once successfully called, the flow will be disabled. |
4132 | */ |
4133 | int |
4134 | ice_rem_prof_id_flow(struct ice_hw *hw, enum ice_block blk, u16 vsi, u64 hdl) |
4135 | { |
4136 | struct ice_vsig_prof *tmp1, *del1; |
4137 | struct ice_chs_chg *tmp, *del; |
4138 | struct list_head chg, copy; |
4139 | int status; |
4140 | u16 vsig; |
4141 | |
4142 | INIT_LIST_HEAD(list: ©); |
4143 | INIT_LIST_HEAD(list: &chg); |
4144 | |
4145 | /* determine if VSI is already part of a VSIG */ |
4146 | status = ice_vsig_find_vsi(hw, blk, vsi, vsig: &vsig); |
4147 | if (!status && vsig) { |
4148 | bool last_profile; |
4149 | bool only_vsi; |
4150 | u16 ref; |
4151 | |
4152 | /* found in VSIG */ |
4153 | last_profile = ice_vsig_prof_id_count(hw, blk, vsig) == 1; |
4154 | status = ice_vsig_get_ref(hw, blk, vsig, refs: &ref); |
4155 | if (status) |
4156 | goto err_ice_rem_prof_id_flow; |
4157 | only_vsi = (ref == 1); |
4158 | |
4159 | if (only_vsi) { |
4160 | /* If the original VSIG only contains one reference, |
4161 | * which will be the requesting VSI, then the VSI is not |
4162 | * sharing entries and we can simply remove the specific |
4163 | * characteristics from the VSIG. |
4164 | */ |
4165 | |
4166 | if (last_profile) { |
4167 | /* If there are no profiles left for this VSIG, |
4168 | * then simply remove the VSIG. |
4169 | */ |
4170 | status = ice_rem_vsig(hw, blk, vsig, chg: &chg); |
4171 | if (status) |
4172 | goto err_ice_rem_prof_id_flow; |
4173 | } else { |
4174 | status = ice_rem_prof_id_vsig(hw, blk, vsig, |
4175 | hdl, chg: &chg); |
4176 | if (status) |
4177 | goto err_ice_rem_prof_id_flow; |
4178 | |
4179 | /* Adjust priorities */ |
4180 | status = ice_adj_prof_priorities(hw, blk, vsig, |
4181 | chg: &chg); |
4182 | if (status) |
4183 | goto err_ice_rem_prof_id_flow; |
4184 | } |
4185 | |
4186 | } else { |
4187 | /* Make a copy of the VSIG's list of Profiles */ |
4188 | status = ice_get_profs_vsig(hw, blk, vsig, lst: ©); |
4189 | if (status) |
4190 | goto err_ice_rem_prof_id_flow; |
4191 | |
4192 | /* Remove specified profile entry from the list */ |
4193 | status = ice_rem_prof_from_list(hw, lst: ©, hdl); |
4194 | if (status) |
4195 | goto err_ice_rem_prof_id_flow; |
4196 | |
4197 | if (list_empty(head: ©)) { |
4198 | status = ice_move_vsi(hw, blk, vsi, |
4199 | ICE_DEFAULT_VSIG, chg: &chg); |
4200 | if (status) |
4201 | goto err_ice_rem_prof_id_flow; |
4202 | |
4203 | } else if (!ice_find_dup_props_vsig(hw, blk, chs: ©, |
4204 | vsig: &vsig)) { |
4205 | /* found an exact match */ |
4206 | /* add or move VSI to the VSIG that matches */ |
4207 | /* Search for a VSIG with a matching profile |
4208 | * list |
4209 | */ |
4210 | |
4211 | /* Found match, move VSI to the matching VSIG */ |
4212 | status = ice_move_vsi(hw, blk, vsi, vsig, chg: &chg); |
4213 | if (status) |
4214 | goto err_ice_rem_prof_id_flow; |
4215 | } else { |
4216 | /* since no existing VSIG supports this |
4217 | * characteristic pattern, we need to create a |
4218 | * new VSIG and TCAM entries |
4219 | */ |
4220 | status = ice_create_vsig_from_lst(hw, blk, vsi, |
4221 | lst: ©, new_vsig: &vsig, |
4222 | chg: &chg); |
4223 | if (status) |
4224 | goto err_ice_rem_prof_id_flow; |
4225 | |
4226 | /* Adjust priorities */ |
4227 | status = ice_adj_prof_priorities(hw, blk, vsig, |
4228 | chg: &chg); |
4229 | if (status) |
4230 | goto err_ice_rem_prof_id_flow; |
4231 | } |
4232 | } |
4233 | } else { |
4234 | status = -ENOENT; |
4235 | } |
4236 | |
4237 | /* update hardware tables */ |
4238 | if (!status) |
4239 | status = ice_upd_prof_hw(hw, blk, chgs: &chg); |
4240 | |
4241 | err_ice_rem_prof_id_flow: |
4242 | list_for_each_entry_safe(del, tmp, &chg, list_entry) { |
4243 | list_del(entry: &del->list_entry); |
4244 | devm_kfree(dev: ice_hw_to_dev(hw), p: del); |
4245 | } |
4246 | |
4247 | list_for_each_entry_safe(del1, tmp1, ©, list) { |
4248 | list_del(entry: &del1->list); |
4249 | devm_kfree(dev: ice_hw_to_dev(hw), p: del1); |
4250 | } |
4251 | |
4252 | return status; |
4253 | } |
4254 | |