Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2015-2020 Beijing WangXun Technology Co., Ltd.
3 : : * Copyright(c) 2010-2017 Intel Corporation
4 : : */
5 : :
6 : : #include <stdio.h>
7 : : #include <stdint.h>
8 : : #include <stdarg.h>
9 : : #include <errno.h>
10 : : #include <sys/queue.h>
11 : : #include <rte_malloc.h>
12 : :
13 : : #include "txgbe_logs.h"
14 : : #include "base/txgbe.h"
15 : : #include "txgbe_ethdev.h"
16 : :
17 : : #define TXGBE_DEFAULT_FLEXBYTES_OFFSET 12 /*default flexbytes offset in bytes*/
18 : : #define TXGBE_MAX_FLX_SOURCE_OFF 62
19 : : #define TXGBE_FDIRCMD_CMD_INTERVAL_US 10
20 : :
21 : : #define IPV6_ADDR_TO_MASK(ipaddr, ipv6m) do { \
22 : : uint8_t ipv6_addr[16]; \
23 : : uint8_t i; \
24 : : rte_memcpy(ipv6_addr, (ipaddr), sizeof(ipv6_addr));\
25 : : (ipv6m) = 0; \
26 : : for (i = 0; i < sizeof(ipv6_addr); i++) { \
27 : : if (ipv6_addr[i] == UINT8_MAX) \
28 : : (ipv6m) |= 1 << i; \
29 : : else if (ipv6_addr[i] != 0) { \
30 : : PMD_DRV_LOG(ERR, " invalid IPv6 address mask."); \
31 : : return -EINVAL; \
32 : : } \
33 : : } \
34 : : } while (0)
35 : :
36 : : #define IPV6_MASK_TO_ADDR(ipv6m, ipaddr) do { \
37 : : uint8_t ipv6_addr[16]; \
38 : : uint8_t i; \
39 : : for (i = 0; i < sizeof(ipv6_addr); i++) { \
40 : : if ((ipv6m) & (1 << i)) \
41 : : ipv6_addr[i] = UINT8_MAX; \
42 : : else \
43 : : ipv6_addr[i] = 0; \
44 : : } \
45 : : rte_memcpy((ipaddr), ipv6_addr, sizeof(ipv6_addr));\
46 : : } while (0)
47 : :
48 : : /**
49 : : * Initialize Flow Director control registers
50 : : * @hw: pointer to hardware structure
51 : : * @fdirctrl: value to write to flow director control register
52 : : **/
53 : : static int
54 : 0 : txgbe_fdir_enable(struct txgbe_hw *hw, uint32_t fdirctrl)
55 : : {
56 : : int i;
57 : :
58 : 0 : PMD_INIT_FUNC_TRACE();
59 : :
60 : : /* Prime the keys for hashing */
61 : : wr32(hw, TXGBE_FDIRBKTHKEY, TXGBE_ATR_BUCKET_HASH_KEY);
62 : : wr32(hw, TXGBE_FDIRSIGHKEY, TXGBE_ATR_SIGNATURE_HASH_KEY);
63 : :
64 : : /*
65 : : * Continue setup of fdirctrl register bits:
66 : : * Set the maximum length per hash bucket to 0xA filters
67 : : * Send interrupt when 64 filters are left
68 : : */
69 : 0 : fdirctrl |= TXGBE_FDIRCTL_MAXLEN(0xA) |
70 : : TXGBE_FDIRCTL_FULLTHR(4);
71 : :
72 : : /*
73 : : * Poll init-done after we write the register. Estimated times:
74 : : * 10G: PBALLOC = 11b, timing is 60us
75 : : * 1G: PBALLOC = 11b, timing is 600us
76 : : * 100M: PBALLOC = 11b, timing is 6ms
77 : : *
78 : : * Multiple these timings by 4 if under full Rx load
79 : : *
80 : : * So we'll poll for TXGBE_FDIR_INIT_DONE_POLL times, sleeping for
81 : : * 1 msec per poll time. If we're at line rate and drop to 100M, then
82 : : * this might not finish in our poll time, but we can live with that
83 : : * for now.
84 : : */
85 : : wr32(hw, TXGBE_FDIRCTL, fdirctrl);
86 : : txgbe_flush(hw);
87 [ # # ]: 0 : for (i = 0; i < TXGBE_FDIR_INIT_DONE_POLL; i++) {
88 [ # # ]: 0 : if (rd32(hw, TXGBE_FDIRCTL) & TXGBE_FDIRCTL_INITDONE)
89 : : break;
90 : : msec_delay(1);
91 : : }
92 : :
93 [ # # ]: 0 : if (i >= TXGBE_FDIR_INIT_DONE_POLL) {
94 : 0 : PMD_INIT_LOG(ERR, "Flow Director poll time exceeded during enabling!");
95 : 0 : return -ETIMEDOUT;
96 : : }
97 : : return 0;
98 : : }
99 : :
100 : : /*
101 : : * Set appropriate bits in fdirctrl for: variable reporting levels, moving
102 : : * flexbytes matching field, and drop queue (only for perfect matching mode).
103 : : */
104 : : static inline int
105 : 0 : configure_fdir_flags(const struct rte_eth_fdir_conf *conf,
106 : : uint32_t *fdirctrl, uint32_t *flex)
107 : : {
108 : 0 : *fdirctrl = 0;
109 : 0 : *flex = 0;
110 : :
111 [ # # # # ]: 0 : switch (conf->pballoc) {
112 : 0 : case RTE_ETH_FDIR_PBALLOC_64K:
113 : : /* 8k - 1 signature filters */
114 : 0 : *fdirctrl |= TXGBE_FDIRCTL_BUF_64K;
115 : 0 : break;
116 : 0 : case RTE_ETH_FDIR_PBALLOC_128K:
117 : : /* 16k - 1 signature filters */
118 : 0 : *fdirctrl |= TXGBE_FDIRCTL_BUF_128K;
119 : 0 : break;
120 : 0 : case RTE_ETH_FDIR_PBALLOC_256K:
121 : : /* 32k - 1 signature filters */
122 : 0 : *fdirctrl |= TXGBE_FDIRCTL_BUF_256K;
123 : 0 : break;
124 : 0 : default:
125 : : /* bad value */
126 : 0 : PMD_INIT_LOG(ERR, "Invalid fdir_conf->pballoc value");
127 : 0 : return -EINVAL;
128 : : };
129 : :
130 : : /* status flags: write hash & swindex in the rx descriptor */
131 [ # # # # ]: 0 : switch (conf->status) {
132 : : case RTE_FDIR_NO_REPORT_STATUS:
133 : : /* do nothing, default mode */
134 : : break;
135 : 0 : case RTE_FDIR_REPORT_STATUS:
136 : : /* report status when the packet matches a fdir rule */
137 : 0 : *fdirctrl |= TXGBE_FDIRCTL_REPORT_MATCH;
138 : 0 : break;
139 : 0 : case RTE_FDIR_REPORT_STATUS_ALWAYS:
140 : : /* always report status */
141 : 0 : *fdirctrl |= TXGBE_FDIRCTL_REPORT_ALWAYS;
142 : 0 : break;
143 : 0 : default:
144 : : /* bad value */
145 : 0 : PMD_INIT_LOG(ERR, "Invalid fdir_conf->status value");
146 : 0 : return -EINVAL;
147 : : };
148 : :
149 : 0 : *flex |= TXGBE_FDIRFLEXCFG_BASE_MAC;
150 : 0 : *flex |= TXGBE_FDIRFLEXCFG_OFST(TXGBE_DEFAULT_FLEXBYTES_OFFSET / 2);
151 : :
152 [ # # # ]: 0 : switch (conf->mode) {
153 : : case RTE_FDIR_MODE_SIGNATURE:
154 : : break;
155 : 0 : case RTE_FDIR_MODE_PERFECT:
156 : 0 : *fdirctrl |= TXGBE_FDIRCTL_PERFECT;
157 : 0 : *fdirctrl |= TXGBE_FDIRCTL_DROPQP(conf->drop_queue);
158 : 0 : break;
159 : 0 : default:
160 : : /* bad value */
161 : 0 : PMD_INIT_LOG(ERR, "Invalid fdir_conf->mode value");
162 : 0 : return -EINVAL;
163 : : }
164 : :
165 : : return 0;
166 : : }
167 : :
168 : : int
169 : 0 : txgbe_fdir_set_input_mask(struct rte_eth_dev *dev)
170 : : {
171 : 0 : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
172 : : struct txgbe_hw_fdir_info *info = TXGBE_DEV_FDIR(dev);
173 : 0 : enum rte_fdir_mode mode = TXGBE_DEV_FDIR_CONF(dev)->mode;
174 : : /*
175 : : * mask VM pool and DIPv6 since there are currently not supported
176 : : * mask FLEX byte, it will be set in flex_conf
177 : : */
178 : : uint32_t fdirm = TXGBE_FDIRMSK_POOL;
179 : : uint32_t fdirtcpm; /* TCP source and destination port masks. */
180 : : uint32_t fdiripv6m; /* IPv6 source and destination masks. */
181 : :
182 : 0 : PMD_INIT_FUNC_TRACE();
183 : :
184 [ # # ]: 0 : if (mode != RTE_FDIR_MODE_SIGNATURE &&
185 : : mode != RTE_FDIR_MODE_PERFECT) {
186 : 0 : PMD_DRV_LOG(ERR, "Not supported fdir mode - %d!", mode);
187 : 0 : return -ENOTSUP;
188 : : }
189 : :
190 : : /*
191 : : * Program the relevant mask registers. If src/dst_port or src/dst_addr
192 : : * are zero, then assume a full mask for that field. Also assume that
193 : : * a VLAN of 0 is unspecified, so mask that out as well. L4type
194 : : * cannot be masked out in this implementation.
195 : : */
196 [ # # ]: 0 : if (info->mask.dst_port_mask == 0 && info->mask.src_port_mask == 0) {
197 : : /* use the L4 protocol mask for raw IPv4/IPv6 traffic */
198 : : fdirm |= TXGBE_FDIRMSK_L4P;
199 : : }
200 : :
201 : : /* TBD: don't support encapsulation yet */
202 : : wr32(hw, TXGBE_FDIRMSK, fdirm);
203 : :
204 : : /* store the TCP/UDP port masks */
205 [ # # ]: 0 : fdirtcpm = rte_be_to_cpu_16(info->mask.dst_port_mask) << 16;
206 [ # # ]: 0 : fdirtcpm |= rte_be_to_cpu_16(info->mask.src_port_mask);
207 : :
208 : : /* write all the same so that UDP, TCP and SCTP use the same mask
209 : : * (little-endian)
210 : : */
211 : 0 : wr32(hw, TXGBE_FDIRTCPMSK, ~fdirtcpm);
212 : : wr32(hw, TXGBE_FDIRUDPMSK, ~fdirtcpm);
213 : : wr32(hw, TXGBE_FDIRSCTPMSK, ~fdirtcpm);
214 : :
215 : : /* Store source and destination IPv4 masks (big-endian) */
216 : 0 : wr32(hw, TXGBE_FDIRSIP4MSK, ~info->mask.src_ipv4_mask);
217 : 0 : wr32(hw, TXGBE_FDIRDIP4MSK, ~info->mask.dst_ipv4_mask);
218 : :
219 [ # # ]: 0 : if (mode == RTE_FDIR_MODE_SIGNATURE) {
220 : : /*
221 : : * Store source and destination IPv6 masks (bit reversed)
222 : : */
223 : 0 : fdiripv6m = TXGBE_FDIRIP6MSK_DST(info->mask.dst_ipv6_mask) |
224 : 0 : TXGBE_FDIRIP6MSK_SRC(info->mask.src_ipv6_mask);
225 : :
226 : 0 : wr32(hw, TXGBE_FDIRIP6MSK, ~fdiripv6m);
227 : : }
228 : :
229 : : return 0;
230 : : }
231 : :
232 : : static int
233 : 0 : txgbe_fdir_store_input_mask(struct rte_eth_dev *dev)
234 : : {
235 : 0 : struct rte_eth_fdir_masks *input_mask = &TXGBE_DEV_FDIR_CONF(dev)->mask;
236 : 0 : enum rte_fdir_mode mode = TXGBE_DEV_FDIR_CONF(dev)->mode;
237 : : struct txgbe_hw_fdir_info *info = TXGBE_DEV_FDIR(dev);
238 : : uint16_t dst_ipv6m = 0;
239 : : uint16_t src_ipv6m = 0;
240 : :
241 [ # # ]: 0 : if (mode != RTE_FDIR_MODE_SIGNATURE &&
242 : : mode != RTE_FDIR_MODE_PERFECT) {
243 : 0 : PMD_DRV_LOG(ERR, "Not supported fdir mode - %d!", mode);
244 : 0 : return -ENOTSUP;
245 : : }
246 : :
247 [ # # ]: 0 : memset(&info->mask, 0, sizeof(struct txgbe_hw_fdir_mask));
248 : 0 : info->mask.vlan_tci_mask = input_mask->vlan_tci_mask;
249 : 0 : info->mask.src_port_mask = input_mask->src_port_mask;
250 : 0 : info->mask.dst_port_mask = input_mask->dst_port_mask;
251 : 0 : info->mask.src_ipv4_mask = input_mask->ipv4_mask.src_ip;
252 : 0 : info->mask.dst_ipv4_mask = input_mask->ipv4_mask.dst_ip;
253 [ # # # # : 0 : IPV6_ADDR_TO_MASK(input_mask->ipv6_mask.src_ip, src_ipv6m);
# # # # ]
254 [ # # # # : 0 : IPV6_ADDR_TO_MASK(input_mask->ipv6_mask.dst_ip, dst_ipv6m);
# # # # ]
255 : 0 : info->mask.src_ipv6_mask = src_ipv6m;
256 : 0 : info->mask.dst_ipv6_mask = dst_ipv6m;
257 : :
258 : 0 : return 0;
259 : : }
260 : :
261 : : int
262 : 0 : txgbe_fdir_set_flexbytes_offset(struct rte_eth_dev *dev,
263 : : uint16_t offset)
264 : : {
265 : 0 : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
266 : : int i;
267 : :
268 [ # # ]: 0 : for (i = 0; i < 64; i++) {
269 : : uint32_t flexreg, flex;
270 : 0 : flexreg = rd32(hw, TXGBE_FDIRFLEXCFG(i / 4));
271 : : flex = TXGBE_FDIRFLEXCFG_BASE_MAC;
272 : 0 : flex |= TXGBE_FDIRFLEXCFG_OFST(offset / 2);
273 : 0 : flexreg &= ~(TXGBE_FDIRFLEXCFG_ALL(~0UL, i % 4));
274 : 0 : flexreg |= TXGBE_FDIRFLEXCFG_ALL(flex, i % 4);
275 : : wr32(hw, TXGBE_FDIRFLEXCFG(i / 4), flexreg);
276 : : }
277 : :
278 : : txgbe_flush(hw);
279 [ # # ]: 0 : for (i = 0; i < TXGBE_FDIR_INIT_DONE_POLL; i++) {
280 [ # # ]: 0 : if (rd32(hw, TXGBE_FDIRCTL) &
281 : : TXGBE_FDIRCTL_INITDONE)
282 : : break;
283 : : msec_delay(1);
284 : : }
285 : 0 : return 0;
286 : : }
287 : :
288 : : /*
289 : : * txgbe_check_fdir_flex_conf -check if the flex payload and mask configuration
290 : : * arguments are valid
291 : : */
292 : : static int
293 : 0 : txgbe_set_fdir_flex_conf(struct rte_eth_dev *dev, uint32_t flex)
294 : : {
295 : : const struct rte_eth_fdir_flex_conf *conf =
296 : 0 : &TXGBE_DEV_FDIR_CONF(dev)->flex_conf;
297 : : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
298 : : struct txgbe_hw_fdir_info *info = TXGBE_DEV_FDIR(dev);
299 : : const struct rte_eth_flex_payload_cfg *flex_cfg;
300 : : const struct rte_eth_fdir_flex_mask *flex_mask;
301 : : uint16_t flexbytes = 0;
302 : : uint16_t i;
303 : :
304 : : if (conf == NULL) {
305 : : PMD_DRV_LOG(ERR, "NULL pointer.");
306 : : return -EINVAL;
307 : : }
308 : :
309 : 0 : flex |= TXGBE_FDIRFLEXCFG_DIA;
310 : :
311 [ # # ]: 0 : for (i = 0; i < conf->nb_payloads; i++) {
312 : 0 : flex_cfg = &conf->flex_set[i];
313 [ # # ]: 0 : if (flex_cfg->type != RTE_ETH_RAW_PAYLOAD) {
314 : 0 : PMD_DRV_LOG(ERR, "unsupported payload type.");
315 : 0 : return -EINVAL;
316 : : }
317 [ # # ]: 0 : if (((flex_cfg->src_offset[0] & 0x1) == 0) &&
318 [ # # # # ]: 0 : (flex_cfg->src_offset[1] == flex_cfg->src_offset[0] + 1) &&
319 : : flex_cfg->src_offset[0] <= TXGBE_MAX_FLX_SOURCE_OFF) {
320 : 0 : flex &= ~TXGBE_FDIRFLEXCFG_OFST_MASK;
321 : 0 : flex |=
322 : 0 : TXGBE_FDIRFLEXCFG_OFST(flex_cfg->src_offset[0] / 2);
323 : : } else {
324 : 0 : PMD_DRV_LOG(ERR, "invalid flexbytes arguments.");
325 : 0 : return -EINVAL;
326 : : }
327 : : }
328 : :
329 [ # # ]: 0 : for (i = 0; i < conf->nb_flexmasks; i++) {
330 : 0 : flex_mask = &conf->flex_mask[i];
331 [ # # ]: 0 : if (flex_mask->flow_type != RTE_ETH_FLOW_UNKNOWN) {
332 : 0 : PMD_DRV_LOG(ERR, "flexmask should be set globally.");
333 : 0 : return -EINVAL;
334 : : }
335 : 0 : flexbytes = (uint16_t)(((flex_mask->mask[1] << 8) & 0xFF00) |
336 : 0 : ((flex_mask->mask[0]) & 0xFF));
337 [ # # ]: 0 : if (flexbytes == UINT16_MAX) {
338 : 0 : flex &= ~TXGBE_FDIRFLEXCFG_DIA;
339 [ # # ]: 0 : } else if (flexbytes != 0) {
340 : : /* TXGBE_FDIRFLEXCFG_DIA is set by default when set mask */
341 : 0 : PMD_DRV_LOG(ERR, " invalid flexbytes mask arguments.");
342 : 0 : return -EINVAL;
343 : : }
344 : : }
345 : :
346 [ # # ]: 0 : info->mask.flex_bytes_mask = flexbytes ? UINT16_MAX : 0;
347 : 0 : info->flex_bytes_offset = (uint8_t)(TXGBD_FDIRFLEXCFG_OFST(flex) * 2);
348 : :
349 [ # # ]: 0 : for (i = 0; i < 64; i++) {
350 : : uint32_t flexreg;
351 : 0 : flexreg = rd32(hw, TXGBE_FDIRFLEXCFG(i / 4));
352 : 0 : flexreg &= ~(TXGBE_FDIRFLEXCFG_ALL(~0UL, i % 4));
353 : 0 : flexreg |= TXGBE_FDIRFLEXCFG_ALL(flex, i % 4);
354 : : wr32(hw, TXGBE_FDIRFLEXCFG(i / 4), flexreg);
355 : : }
356 : : return 0;
357 : : }
358 : :
359 : : int
360 : 0 : txgbe_fdir_configure(struct rte_eth_dev *dev)
361 : : {
362 : 0 : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
363 : : int err;
364 : : uint32_t fdirctrl, flex, pbsize;
365 : : int i;
366 : 0 : enum rte_fdir_mode mode = TXGBE_DEV_FDIR_CONF(dev)->mode;
367 : :
368 : 0 : PMD_INIT_FUNC_TRACE();
369 : :
370 : : /* supports mac-vlan and tunnel mode */
371 [ # # ]: 0 : if (mode != RTE_FDIR_MODE_SIGNATURE &&
372 : : mode != RTE_FDIR_MODE_PERFECT)
373 : : return -ENOSYS;
374 : :
375 : 0 : err = configure_fdir_flags(TXGBE_DEV_FDIR_CONF(dev), &fdirctrl, &flex);
376 [ # # ]: 0 : if (err)
377 : : return err;
378 : :
379 : : /*
380 : : * Before enabling Flow Director, the Rx Packet Buffer size
381 : : * must be reduced. The new value is the current size minus
382 : : * flow director memory usage size.
383 : : */
384 : : pbsize = rd32(hw, TXGBE_PBRXSIZE(0));
385 : 0 : pbsize -= TXGBD_FDIRCTL_BUF_BYTE(fdirctrl);
386 : : wr32(hw, TXGBE_PBRXSIZE(0), pbsize);
387 : :
388 : : /*
389 : : * The defaults in the HW for RX PB 1-7 are not zero and so should be
390 : : * initialized to zero for non DCB mode otherwise actual total RX PB
391 : : * would be bigger than programmed and filter space would run into
392 : : * the PB 0 region.
393 : : */
394 [ # # ]: 0 : for (i = 1; i < 8; i++)
395 : 0 : wr32(hw, TXGBE_PBRXSIZE(i), 0);
396 : :
397 : 0 : err = txgbe_fdir_store_input_mask(dev);
398 [ # # ]: 0 : if (err < 0) {
399 : 0 : PMD_INIT_LOG(ERR, " Error on setting FD mask");
400 : 0 : return err;
401 : : }
402 : :
403 : 0 : err = txgbe_fdir_set_input_mask(dev);
404 [ # # ]: 0 : if (err < 0) {
405 : 0 : PMD_INIT_LOG(ERR, " Error on setting FD mask");
406 : 0 : return err;
407 : : }
408 : :
409 : 0 : err = txgbe_set_fdir_flex_conf(dev, flex);
410 [ # # ]: 0 : if (err < 0) {
411 : 0 : PMD_INIT_LOG(ERR, " Error on setting FD flexible arguments.");
412 : 0 : return err;
413 : : }
414 : :
415 : 0 : err = txgbe_fdir_enable(hw, fdirctrl);
416 [ # # ]: 0 : if (err < 0) {
417 : 0 : PMD_INIT_LOG(ERR, " Error on enabling FD.");
418 : 0 : return err;
419 : : }
420 : : return 0;
421 : : }
422 : :
423 : : /*
424 : : * Note that the bkt_hash field in the txgbe_atr_input structure is also never
425 : : * set.
426 : : *
427 : : * Compute the hashes for SW ATR
428 : : * @stream: input bitstream to compute the hash on
429 : : * @key: 32-bit hash key
430 : : **/
431 : : static uint32_t
432 : 0 : txgbe_atr_compute_hash(struct txgbe_atr_input *atr_input,
433 : : uint32_t key)
434 : : {
435 : : /*
436 : : * The algorithm is as follows:
437 : : * Hash[15:0] = Sum { S[n] x K[n+16] }, n = 0...350
438 : : * where Sum {A[n]}, n = 0...n is bitwise XOR of A[0], A[1]...A[n]
439 : : * and A[n] x B[n] is bitwise AND between same length strings
440 : : *
441 : : * K[n] is 16 bits, defined as:
442 : : * for n modulo 32 >= 15, K[n] = K[n % 32 : (n % 32) - 15]
443 : : * for n modulo 32 < 15, K[n] =
444 : : * K[(n % 32:0) | (31:31 - (14 - (n % 32)))]
445 : : *
446 : : * S[n] is 16 bits, defined as:
447 : : * for n >= 15, S[n] = S[n:n - 15]
448 : : * for n < 15, S[n] = S[(n:0) | (350:350 - (14 - n))]
449 : : *
450 : : * To simplify for programming, the algorithm is implemented
451 : : * in software this way:
452 : : *
453 : : * key[31:0], hi_hash_dword[31:0], lo_hash_dword[31:0], hash[15:0]
454 : : *
455 : : * for (i = 0; i < 352; i+=32)
456 : : * hi_hash_dword[31:0] ^= Stream[(i+31):i];
457 : : *
458 : : * lo_hash_dword[15:0] ^= Stream[15:0];
459 : : * lo_hash_dword[15:0] ^= hi_hash_dword[31:16];
460 : : * lo_hash_dword[31:16] ^= hi_hash_dword[15:0];
461 : : *
462 : : * hi_hash_dword[31:0] ^= Stream[351:320];
463 : : *
464 : : * if (key[0])
465 : : * hash[15:0] ^= Stream[15:0];
466 : : *
467 : : * for (i = 0; i < 16; i++) {
468 : : * if (key[i])
469 : : * hash[15:0] ^= lo_hash_dword[(i+15):i];
470 : : * if (key[i + 16])
471 : : * hash[15:0] ^= hi_hash_dword[(i+15):i];
472 : : * }
473 : : *
474 : : */
475 : : __be32 *dword_stream = (__be32 *)atr_input;
476 : : __be32 common_hash_dword = 0;
477 : : u32 hi_hash_dword, lo_hash_dword, flow_pool_ptid;
478 : : u32 hash_result = 0;
479 : : u8 i;
480 : :
481 : : /* record the flow_vm_vlan bits as they are a key part to the hash */
482 [ # # ]: 0 : flow_pool_ptid = be_to_cpu32(dword_stream[0]);
483 : :
484 : : /* generate common hash dword */
485 [ # # ]: 0 : for (i = 1; i <= 10; i++)
486 : 0 : common_hash_dword ^= dword_stream[i];
487 : :
488 [ # # ]: 0 : hi_hash_dword = be_to_cpu32(common_hash_dword);
489 : :
490 : : /* low dword is word swapped version of common */
491 : 0 : lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
492 : :
493 : : /* apply (Flow ID/VM Pool/Packet Type) bits to hash words */
494 : 0 : hi_hash_dword ^= flow_pool_ptid ^ (flow_pool_ptid >> 16);
495 : :
496 : : /* Process bits 0 and 16 */
497 [ # # ]: 0 : if (key & 0x0001)
498 : : hash_result ^= lo_hash_dword;
499 [ # # ]: 0 : if (key & 0x00010000)
500 : 0 : hash_result ^= hi_hash_dword;
501 : :
502 : : /*
503 : : * apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
504 : : * delay this because bit 0 of the stream should not be processed
505 : : * so we do not add the vlan until after bit 0 was processed
506 : : */
507 : 0 : lo_hash_dword ^= flow_pool_ptid ^ (flow_pool_ptid << 16);
508 : :
509 : : /* process the remaining 30 bits in the key 2 bits at a time */
510 [ # # ]: 0 : for (i = 15; i; i--) {
511 [ # # ]: 0 : if (key & (0x0001 << i))
512 : 0 : hash_result ^= lo_hash_dword >> i;
513 [ # # ]: 0 : if (key & (0x00010000 << i))
514 : 0 : hash_result ^= hi_hash_dword >> i;
515 : : }
516 : :
517 : 0 : return hash_result;
518 : : }
519 : :
520 : : static uint32_t
521 : : atr_compute_perfect_hash(struct txgbe_atr_input *input,
522 : : enum rte_eth_fdir_pballoc_type pballoc)
523 : : {
524 : : uint32_t bucket_hash;
525 : :
526 : 0 : bucket_hash = txgbe_atr_compute_hash(input,
527 : : TXGBE_ATR_BUCKET_HASH_KEY);
528 [ # # ]: 0 : if (pballoc == RTE_ETH_FDIR_PBALLOC_256K)
529 : 0 : bucket_hash &= PERFECT_BUCKET_256KB_HASH_MASK;
530 [ # # ]: 0 : else if (pballoc == RTE_ETH_FDIR_PBALLOC_128K)
531 : 0 : bucket_hash &= PERFECT_BUCKET_128KB_HASH_MASK;
532 : : else
533 : 0 : bucket_hash &= PERFECT_BUCKET_64KB_HASH_MASK;
534 : :
535 : : return TXGBE_FDIRPIHASH_BKT(bucket_hash);
536 : : }
537 : :
538 : : /**
539 : : * txgbe_fdir_check_cmd_complete - poll to check whether FDIRPICMD is complete
540 : : * @hw: pointer to hardware structure
541 : : */
542 : : static inline int
543 : : txgbe_fdir_check_cmd_complete(struct txgbe_hw *hw, uint32_t *fdircmd)
544 : : {
545 : : int i;
546 : :
547 [ # # # # : 0 : for (i = 0; i < TXGBE_FDIRCMD_CMD_POLL; i++) {
# # # # ]
548 : : *fdircmd = rd32(hw, TXGBE_FDIRPICMD);
549 [ # # # # : 0 : if (!(*fdircmd & TXGBE_FDIRPICMD_OP_MASK))
# # # # ]
550 : : return 0;
551 : 0 : rte_delay_us(TXGBE_FDIRCMD_CMD_INTERVAL_US);
552 : : }
553 : :
554 : : return -ETIMEDOUT;
555 : : }
556 : :
557 : : /*
558 : : * Calculate the hash value needed for signature-match filters. In the FreeBSD
559 : : * driver, this is done by the optimised function
560 : : * txgbe_atr_compute_sig_hash_raptor(). However that can't be used here as it
561 : : * doesn't support calculating a hash for an IPv6 filter.
562 : : */
563 : : static uint32_t
564 : 0 : atr_compute_signature_hash(struct txgbe_atr_input *input,
565 : : enum rte_eth_fdir_pballoc_type pballoc)
566 : : {
567 : : uint32_t bucket_hash, sig_hash;
568 : :
569 : 0 : bucket_hash = txgbe_atr_compute_hash(input,
570 : : TXGBE_ATR_BUCKET_HASH_KEY);
571 [ # # ]: 0 : if (pballoc == RTE_ETH_FDIR_PBALLOC_256K)
572 : 0 : bucket_hash &= SIG_BUCKET_256KB_HASH_MASK;
573 [ # # ]: 0 : else if (pballoc == RTE_ETH_FDIR_PBALLOC_128K)
574 : 0 : bucket_hash &= SIG_BUCKET_128KB_HASH_MASK;
575 : : else
576 : 0 : bucket_hash &= SIG_BUCKET_64KB_HASH_MASK;
577 : :
578 : 0 : sig_hash = txgbe_atr_compute_hash(input,
579 : : TXGBE_ATR_SIGNATURE_HASH_KEY);
580 : :
581 : 0 : return TXGBE_FDIRPIHASH_SIG(sig_hash) |
582 : : TXGBE_FDIRPIHASH_BKT(bucket_hash);
583 : : }
584 : :
585 : : /**
586 : : * With the ability to set extra flags in FDIRPICMD register
587 : : * added, and IPv6 support also added. The hash value is also pre-calculated
588 : : * as the pballoc value is needed to do it.
589 : : */
590 : : static int
591 : 0 : fdir_write_perfect_filter(struct txgbe_hw *hw,
592 : : struct txgbe_atr_input *input, uint8_t queue,
593 : : uint32_t fdircmd, uint32_t fdirhash,
594 : : enum rte_fdir_mode mode)
595 : : {
596 : : uint32_t fdirport, fdirflex;
597 : : int err = 0;
598 : :
599 : : UNREFERENCED_PARAMETER(mode);
600 : :
601 : : /* record the IPv4 address (little-endian)
602 : : * can not use wr32.
603 : : */
604 [ # # ]: 0 : wr32(hw, TXGBE_FDIRPISIP4, be_to_le32(input->src_ip[0]));
605 [ # # ]: 0 : wr32(hw, TXGBE_FDIRPIDIP4, be_to_le32(input->dst_ip[0]));
606 : :
607 : : /* record source and destination port (little-endian)*/
608 [ # # ]: 0 : fdirport = TXGBE_FDIRPIPORT_DST(be_to_le16(input->dst_port));
609 [ # # ]: 0 : fdirport |= TXGBE_FDIRPIPORT_SRC(be_to_le16(input->src_port));
610 : : wr32(hw, TXGBE_FDIRPIPORT, fdirport);
611 : :
612 : : /* record pkt_type (little-endian) and flex_bytes(big-endian) */
613 [ # # ]: 0 : fdirflex = TXGBE_FDIRPIFLEX_FLEX(be_to_npu16(input->flex_bytes));
614 [ # # ]: 0 : fdirflex |= TXGBE_FDIRPIFLEX_PTYPE(be_to_le16(input->pkt_type));
615 : : wr32(hw, TXGBE_FDIRPIFLEX, fdirflex);
616 : :
617 : : /* configure FDIRHASH register */
618 : 0 : fdirhash |= TXGBE_FDIRPIHASH_VLD;
619 : : wr32(hw, TXGBE_FDIRPIHASH, fdirhash);
620 : :
621 : : /*
622 : : * flush all previous writes to make certain registers are
623 : : * programmed prior to issuing the command
624 : : */
625 : : txgbe_flush(hw);
626 : :
627 : : /* configure FDIRPICMD register */
628 : 0 : fdircmd |= TXGBE_FDIRPICMD_OP_ADD |
629 : : TXGBE_FDIRPICMD_UPD |
630 : : TXGBE_FDIRPICMD_LAST |
631 : : TXGBE_FDIRPICMD_QPENA;
632 : 0 : fdircmd |= TXGBE_FDIRPICMD_FT(input->flow_type);
633 : 0 : fdircmd |= TXGBE_FDIRPICMD_QP(queue);
634 : 0 : fdircmd |= TXGBE_FDIRPICMD_POOL(input->vm_pool);
635 : :
636 : : wr32(hw, TXGBE_FDIRPICMD, fdircmd);
637 : :
638 : 0 : PMD_DRV_LOG(DEBUG, "Rx Queue=%x hash=%x", queue, fdirhash);
639 : :
640 : : err = txgbe_fdir_check_cmd_complete(hw, &fdircmd);
641 [ # # ]: 0 : if (err < 0)
642 : 0 : PMD_DRV_LOG(ERR, "Timeout writing flow director filter.");
643 : :
644 : 0 : return err;
645 : : }
646 : :
647 : : /**
648 : : * This function supports setting extra fields in the FDIRPICMD register, and
649 : : * removes the code that was verifying the flow_type field. According to the
650 : : * documentation, a flow type of 00 (i.e. not TCP, UDP, or SCTP) is not
651 : : * supported, however it appears to work ok...
652 : : * Adds a signature hash filter
653 : : * @hw: pointer to hardware structure
654 : : * @input: unique input dword
655 : : * @queue: queue index to direct traffic to
656 : : * @fdircmd: any extra flags to set in fdircmd register
657 : : * @fdirhash: pre-calculated hash value for the filter
658 : : **/
659 : : static int
660 : 0 : fdir_add_signature_filter(struct txgbe_hw *hw,
661 : : struct txgbe_atr_input *input, uint8_t queue, uint32_t fdircmd,
662 : : uint32_t fdirhash)
663 : : {
664 : : int err = 0;
665 : :
666 : 0 : PMD_INIT_FUNC_TRACE();
667 : :
668 : : /* configure FDIRPICMD register */
669 : 0 : fdircmd |= TXGBE_FDIRPICMD_OP_ADD |
670 : : TXGBE_FDIRPICMD_UPD |
671 : : TXGBE_FDIRPICMD_LAST |
672 : : TXGBE_FDIRPICMD_QPENA;
673 : 0 : fdircmd |= TXGBE_FDIRPICMD_FT(input->flow_type);
674 : 0 : fdircmd |= TXGBE_FDIRPICMD_QP(queue);
675 : :
676 : 0 : fdirhash |= TXGBE_FDIRPIHASH_VLD;
677 : : wr32(hw, TXGBE_FDIRPIHASH, fdirhash);
678 : : wr32(hw, TXGBE_FDIRPICMD, fdircmd);
679 : :
680 : 0 : PMD_DRV_LOG(DEBUG, "Rx Queue=%x hash=%x", queue, fdirhash);
681 : :
682 : : err = txgbe_fdir_check_cmd_complete(hw, &fdircmd);
683 [ # # ]: 0 : if (err < 0)
684 : 0 : PMD_DRV_LOG(ERR, "Timeout writing flow director filter.");
685 : :
686 : 0 : return err;
687 : : }
688 : :
689 : : /*
690 : : * This is modified to take in the hash as a parameter so that
691 : : * it can be used for removing signature and perfect filters.
692 : : */
693 : : static int
694 : 0 : fdir_erase_filter_raptor(struct txgbe_hw *hw, uint32_t fdirhash)
695 : : {
696 : : uint32_t fdircmd = 0;
697 : : int err = 0;
698 : :
699 : : wr32(hw, TXGBE_FDIRPIHASH, fdirhash);
700 : :
701 : : /* flush hash to HW */
702 : : txgbe_flush(hw);
703 : :
704 : : /* Query if filter is present */
705 : : wr32(hw, TXGBE_FDIRPICMD, TXGBE_FDIRPICMD_OP_QRY);
706 : :
707 : : err = txgbe_fdir_check_cmd_complete(hw, &fdircmd);
708 [ # # ]: 0 : if (err < 0) {
709 : 0 : PMD_INIT_LOG(ERR, "Timeout querying for flow director filter.");
710 : 0 : return err;
711 : : }
712 : :
713 : : /* if filter exists in hardware then remove it */
714 [ # # ]: 0 : if (fdircmd & TXGBE_FDIRPICMD_VLD) {
715 : : wr32(hw, TXGBE_FDIRPIHASH, fdirhash);
716 : : txgbe_flush(hw);
717 : : wr32(hw, TXGBE_FDIRPICMD, TXGBE_FDIRPICMD_OP_REM);
718 : : }
719 : :
720 : : err = txgbe_fdir_check_cmd_complete(hw, &fdircmd);
721 [ # # ]: 0 : if (err < 0)
722 : 0 : PMD_INIT_LOG(ERR, "Timeout erasing flow director filter.");
723 : :
724 : : return err;
725 : : }
726 : :
727 : : static inline struct txgbe_fdir_filter *
728 : : txgbe_fdir_filter_lookup(struct txgbe_hw_fdir_info *fdir_info,
729 : : struct txgbe_atr_input *input)
730 : : {
731 : : int ret;
732 : :
733 : 0 : ret = rte_hash_lookup(fdir_info->hash_handle, (const void *)input);
734 [ # # ]: 0 : if (ret < 0)
735 : : return NULL;
736 : :
737 : 0 : return fdir_info->hash_map[ret];
738 : : }
739 : :
740 : : static inline int
741 : 0 : txgbe_insert_fdir_filter(struct txgbe_hw_fdir_info *fdir_info,
742 : : struct txgbe_fdir_filter *fdir_filter)
743 : : {
744 : : int ret;
745 : :
746 : 0 : ret = rte_hash_add_key(fdir_info->hash_handle, &fdir_filter->input);
747 [ # # ]: 0 : if (ret < 0) {
748 : 0 : PMD_DRV_LOG(ERR,
749 : : "Failed to insert fdir filter to hash table %d!",
750 : : ret);
751 : 0 : return ret;
752 : : }
753 : :
754 : 0 : fdir_info->hash_map[ret] = fdir_filter;
755 : :
756 : 0 : TAILQ_INSERT_TAIL(&fdir_info->fdir_list, fdir_filter, entries);
757 : :
758 : 0 : return 0;
759 : : }
760 : :
761 : : static inline int
762 : 0 : txgbe_remove_fdir_filter(struct txgbe_hw_fdir_info *fdir_info,
763 : : struct txgbe_atr_input *input)
764 : : {
765 : : int ret;
766 : : struct txgbe_fdir_filter *fdir_filter;
767 : :
768 : 0 : ret = rte_hash_del_key(fdir_info->hash_handle, input);
769 [ # # ]: 0 : if (ret < 0)
770 : : return ret;
771 : :
772 : 0 : fdir_filter = fdir_info->hash_map[ret];
773 : 0 : fdir_info->hash_map[ret] = NULL;
774 : :
775 [ # # ]: 0 : TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries);
776 : 0 : rte_free(fdir_filter);
777 : :
778 : 0 : return 0;
779 : : }
780 : :
781 : : int
782 : 0 : txgbe_fdir_filter_program(struct rte_eth_dev *dev,
783 : : struct txgbe_fdir_rule *rule,
784 : : bool del,
785 : : bool update)
786 : : {
787 : 0 : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
788 : : uint32_t fdirhash;
789 : : uint8_t queue;
790 : : bool is_perfect = FALSE;
791 : : int err;
792 : 0 : struct txgbe_hw_fdir_info *info = TXGBE_DEV_FDIR(dev);
793 : 0 : enum rte_fdir_mode fdir_mode = TXGBE_DEV_FDIR_CONF(dev)->mode;
794 : : struct txgbe_fdir_filter *node;
795 : :
796 [ # # ]: 0 : if (fdir_mode == RTE_FDIR_MODE_NONE ||
797 [ # # ]: 0 : fdir_mode != rule->mode)
798 : : return -ENOTSUP;
799 : :
800 [ # # ]: 0 : if (fdir_mode >= RTE_FDIR_MODE_PERFECT)
801 : : is_perfect = TRUE;
802 : :
803 : : if (is_perfect) {
804 [ # # ]: 0 : if (rule->input.flow_type & TXGBE_ATR_L3TYPE_IPV6) {
805 : 0 : PMD_DRV_LOG(ERR, "IPv6 is not supported in"
806 : : " perfect mode!");
807 : 0 : return -ENOTSUP;
808 : : }
809 : 0 : fdirhash = atr_compute_perfect_hash(&rule->input,
810 : : TXGBE_DEV_FDIR_CONF(dev)->pballoc);
811 : 0 : fdirhash |= TXGBE_FDIRPIHASH_IDX(rule->soft_id);
812 : : } else {
813 : 0 : fdirhash = atr_compute_signature_hash(&rule->input,
814 : : TXGBE_DEV_FDIR_CONF(dev)->pballoc);
815 : : }
816 : :
817 [ # # ]: 0 : if (del) {
818 : 0 : err = txgbe_remove_fdir_filter(info, &rule->input);
819 [ # # ]: 0 : if (err < 0) {
820 : 0 : PMD_DRV_LOG(ERR,
821 : : "No such fdir filter to delete %d!", err);
822 : 0 : return err;
823 : : }
824 : :
825 : 0 : err = fdir_erase_filter_raptor(hw, fdirhash);
826 [ # # ]: 0 : if (err < 0)
827 : 0 : PMD_DRV_LOG(ERR, "Fail to delete FDIR filter!");
828 : : else
829 : 0 : PMD_DRV_LOG(DEBUG, "Success to delete FDIR filter!");
830 : 0 : return err;
831 : : }
832 : :
833 : : /* add or update an fdir filter*/
834 [ # # ]: 0 : if (rule->fdirflags & TXGBE_FDIRPICMD_DROP) {
835 [ # # ]: 0 : if (!is_perfect) {
836 : 0 : PMD_DRV_LOG(ERR, "Drop option is not supported in"
837 : : " signature mode.");
838 : 0 : return -EINVAL;
839 : : }
840 : 0 : queue = TXGBE_DEV_FDIR_CONF(dev)->drop_queue;
841 [ # # ]: 0 : } else if (rule->queue < TXGBE_MAX_RX_QUEUE_NUM) {
842 : : queue = rule->queue;
843 : : } else {
844 : : return -EINVAL;
845 : : }
846 : :
847 [ # # ]: 0 : if (RTE_ETH_DEV_SRIOV(dev).active)
848 : 0 : queue = RTE_ETH_DEV_SRIOV(dev).def_pool_q_idx + queue;
849 : :
850 : 0 : node = txgbe_fdir_filter_lookup(info, &rule->input);
851 [ # # ]: 0 : if (node) {
852 [ # # ]: 0 : if (!update) {
853 : 0 : PMD_DRV_LOG(ERR, "Conflict with existing fdir filter!");
854 : 0 : return -EINVAL;
855 : : }
856 : 0 : node->fdirflags = rule->fdirflags;
857 : 0 : node->fdirhash = fdirhash;
858 : 0 : node->queue = queue;
859 : : } else {
860 : 0 : node = rte_zmalloc("txgbe_fdir",
861 : : sizeof(struct txgbe_fdir_filter), 0);
862 [ # # ]: 0 : if (!node)
863 : : return -ENOMEM;
864 [ # # ]: 0 : rte_memcpy(&node->input, &rule->input,
865 : : sizeof(struct txgbe_atr_input));
866 : 0 : node->fdirflags = rule->fdirflags;
867 : 0 : node->fdirhash = fdirhash;
868 : 0 : node->queue = queue;
869 : :
870 : 0 : err = txgbe_insert_fdir_filter(info, node);
871 [ # # ]: 0 : if (err < 0) {
872 : 0 : rte_free(node);
873 : 0 : return err;
874 : : }
875 : : }
876 : :
877 [ # # ]: 0 : if (is_perfect)
878 : 0 : err = fdir_write_perfect_filter(hw, &node->input,
879 : 0 : node->queue, node->fdirflags,
880 : : node->fdirhash, fdir_mode);
881 : : else
882 : 0 : err = fdir_add_signature_filter(hw, &node->input,
883 : 0 : node->queue, node->fdirflags,
884 : : node->fdirhash);
885 [ # # ]: 0 : if (err < 0) {
886 : 0 : PMD_DRV_LOG(ERR, "Fail to add FDIR filter!");
887 : 0 : txgbe_remove_fdir_filter(info, &rule->input);
888 : : } else {
889 : 0 : PMD_DRV_LOG(DEBUG, "Success to add FDIR filter");
890 : : }
891 : :
892 : : return err;
893 : : }
894 : :
895 : : static int
896 : 0 : txgbe_fdir_flush(struct rte_eth_dev *dev)
897 : : {
898 : 0 : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
899 : : struct txgbe_hw_fdir_info *info = TXGBE_DEV_FDIR(dev);
900 : : int ret;
901 : :
902 : 0 : ret = txgbe_reinit_fdir_tables(hw);
903 [ # # ]: 0 : if (ret < 0) {
904 : 0 : PMD_INIT_LOG(ERR, "Failed to re-initialize FD table.");
905 : 0 : return ret;
906 : : }
907 : :
908 : 0 : info->f_add = 0;
909 : 0 : info->f_remove = 0;
910 : 0 : info->add = 0;
911 : 0 : info->remove = 0;
912 : :
913 : 0 : return ret;
914 : : }
915 : :
916 : : /* restore flow director filter */
917 : : void
918 : 0 : txgbe_fdir_filter_restore(struct rte_eth_dev *dev)
919 : : {
920 : 0 : struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
921 : : struct txgbe_hw_fdir_info *fdir_info = TXGBE_DEV_FDIR(dev);
922 : : struct txgbe_fdir_filter *node;
923 : : bool is_perfect = FALSE;
924 : 0 : enum rte_fdir_mode fdir_mode = TXGBE_DEV_FDIR_CONF(dev)->mode;
925 : :
926 [ # # ]: 0 : if (fdir_mode >= RTE_FDIR_MODE_PERFECT &&
927 : : fdir_mode <= RTE_FDIR_MODE_PERFECT_TUNNEL)
928 : : is_perfect = TRUE;
929 : :
930 : : if (is_perfect) {
931 [ # # ]: 0 : TAILQ_FOREACH(node, &fdir_info->fdir_list, entries) {
932 : 0 : (void)fdir_write_perfect_filter(hw,
933 : : &node->input,
934 : 0 : node->queue,
935 : : node->fdirflags,
936 : : node->fdirhash,
937 : : fdir_mode);
938 : : }
939 : : } else {
940 [ # # ]: 0 : TAILQ_FOREACH(node, &fdir_info->fdir_list, entries) {
941 : 0 : (void)fdir_add_signature_filter(hw,
942 : : &node->input,
943 : 0 : node->queue,
944 : : node->fdirflags,
945 : : node->fdirhash);
946 : : }
947 : : }
948 : 0 : }
949 : :
950 : : /* remove all the flow director filters */
951 : : int
952 : 0 : txgbe_clear_all_fdir_filter(struct rte_eth_dev *dev)
953 : : {
954 : 0 : struct txgbe_hw_fdir_info *fdir_info = TXGBE_DEV_FDIR(dev);
955 : : struct txgbe_fdir_filter *fdir_filter;
956 : : struct txgbe_fdir_filter *filter_flag;
957 : : int ret = 0;
958 : :
959 : : /* flush flow director */
960 : 0 : rte_hash_reset(fdir_info->hash_handle);
961 : 0 : memset(fdir_info->hash_map, 0,
962 : : sizeof(struct txgbe_fdir_filter *) * TXGBE_MAX_FDIR_FILTER_NUM);
963 : 0 : filter_flag = TAILQ_FIRST(&fdir_info->fdir_list);
964 [ # # ]: 0 : while ((fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list))) {
965 [ # # ]: 0 : TAILQ_REMOVE(&fdir_info->fdir_list,
966 : : fdir_filter,
967 : : entries);
968 : 0 : rte_free(fdir_filter);
969 : : }
970 : :
971 [ # # ]: 0 : if (filter_flag != NULL)
972 : 0 : ret = txgbe_fdir_flush(dev);
973 : :
974 : 0 : return ret;
975 : : }
|