644 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */3 4#include <linux/string.h>5#include <linux/types.h>6#include <linux/pci.h>7 8#include "../libwx/wx_type.h"9#include "../libwx/wx_lib.h"10#include "../libwx/wx_hw.h"11#include "txgbe_type.h"12#include "txgbe_fdir.h"13 14/* These defines allow us to quickly generate all of the necessary instructions15 * in the function below by simply calling out TXGBE_COMPUTE_SIG_HASH_ITERATION16 * for values 0 through 1517 */18#define TXGBE_ATR_COMMON_HASH_KEY \19 (TXGBE_ATR_BUCKET_HASH_KEY & TXGBE_ATR_SIGNATURE_HASH_KEY)20#define TXGBE_COMPUTE_SIG_HASH_ITERATION(_n) \21do { \22 u32 n = (_n); \23 if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << n)) \24 common_hash ^= lo_hash_dword >> n; \25 else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \26 bucket_hash ^= lo_hash_dword >> n; \27 else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << n)) \28 sig_hash ^= lo_hash_dword << (16 - n); \29 if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << (n + 16))) \30 common_hash ^= hi_hash_dword >> n; \31 else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \32 bucket_hash ^= hi_hash_dword >> n; \33 else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \34 sig_hash ^= hi_hash_dword << (16 - n); \35} while (0)36 37/**38 * txgbe_atr_compute_sig_hash - Compute the signature hash39 * @input: input bitstream to compute the hash on40 * @common: compressed common input dword41 * @hash: pointer to the computed hash42 *43 * This function is almost identical to the function above but contains44 * several optimizations such as unwinding all of the loops, letting the45 * compiler work out all of the conditional ifs since the keys are static46 * defines, and computing two keys at once since the hashed dword stream47 * will be the same for both keys.48 **/49static void txgbe_atr_compute_sig_hash(union txgbe_atr_hash_dword input,50 union txgbe_atr_hash_dword common,51 u32 *hash)52{53 u32 sig_hash = 0, bucket_hash = 0, common_hash = 0;54 u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;55 u32 i;56 57 /* record the flow_vm_vlan bits as they are a key part to the hash */58 flow_vm_vlan = ntohl(input.dword);59 60 /* generate common hash dword */61 hi_hash_dword = ntohl(common.dword);62 63 /* low dword is word swapped version of common */64 lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);65 66 /* apply flow ID/VM pool/VLAN ID bits to hash words */67 hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);68 69 /* Process bits 0 and 16 */70 TXGBE_COMPUTE_SIG_HASH_ITERATION(0);71 72 /* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to73 * delay this because bit 0 of the stream should not be processed74 * so we do not add the VLAN until after bit 0 was processed75 */76 lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);77 78 /* Process remaining 30 bit of the key */79 for (i = 1; i <= 15; i++)80 TXGBE_COMPUTE_SIG_HASH_ITERATION(i);81 82 /* combine common_hash result with signature and bucket hashes */83 bucket_hash ^= common_hash;84 bucket_hash &= TXGBE_ATR_HASH_MASK;85 86 sig_hash ^= common_hash << 16;87 sig_hash &= TXGBE_ATR_HASH_MASK << 16;88 89 /* return completed signature hash */90 *hash = sig_hash ^ bucket_hash;91}92 93#define TXGBE_COMPUTE_BKT_HASH_ITERATION(_n) \94do { \95 u32 n = (_n); \96 if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \97 bucket_hash ^= lo_hash_dword >> n; \98 if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \99 bucket_hash ^= hi_hash_dword >> n; \100} while (0)101 102/**103 * txgbe_atr_compute_perfect_hash - Compute the perfect filter hash104 * @input: input bitstream to compute the hash on105 * @input_mask: mask for the input bitstream106 *107 * This function serves two main purposes. First it applies the input_mask108 * to the atr_input resulting in a cleaned up atr_input data stream.109 * Secondly it computes the hash and stores it in the bkt_hash field at110 * the end of the input byte stream. This way it will be available for111 * future use without needing to recompute the hash.112 **/113void txgbe_atr_compute_perfect_hash(union txgbe_atr_input *input,114 union txgbe_atr_input *input_mask)115{116 u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;117 u32 bucket_hash = 0;118 __be32 hi_dword = 0;119 u32 i = 0;120 121 /* Apply masks to input data */122 for (i = 0; i < 11; i++)123 input->dword_stream[i] &= input_mask->dword_stream[i];124 125 /* record the flow_vm_vlan bits as they are a key part to the hash */126 flow_vm_vlan = ntohl(input->dword_stream[0]);127 128 /* generate common hash dword */129 for (i = 1; i <= 10; i++)130 hi_dword ^= input->dword_stream[i];131 hi_hash_dword = ntohl(hi_dword);132 133 /* low dword is word swapped version of common */134 lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);135 136 /* apply flow ID/VM pool/VLAN ID bits to hash words */137 hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);138 139 /* Process bits 0 and 16 */140 TXGBE_COMPUTE_BKT_HASH_ITERATION(0);141 142 /* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to143 * delay this because bit 0 of the stream should not be processed144 * so we do not add the VLAN until after bit 0 was processed145 */146 lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);147 148 /* Process remaining 30 bit of the key */149 for (i = 1; i <= 15; i++)150 TXGBE_COMPUTE_BKT_HASH_ITERATION(i);151 152 /* Limit hash to 13 bits since max bucket count is 8K.153 * Store result at the end of the input stream.154 */155 input->formatted.bkt_hash = (__force __be16)(bucket_hash & 0x1FFF);156}157 158static int txgbe_fdir_check_cmd_complete(struct wx *wx)159{160 u32 val;161 162 return read_poll_timeout_atomic(rd32, val,163 !(val & TXGBE_RDB_FDIR_CMD_CMD_MASK),164 10, 100, false,165 wx, TXGBE_RDB_FDIR_CMD);166}167 168/**169 * txgbe_fdir_add_signature_filter - Adds a signature hash filter170 * @wx: pointer to hardware structure171 * @input: unique input dword172 * @common: compressed common input dword173 * @queue: queue index to direct traffic to174 *175 * @return: 0 on success and negative on failure176 **/177static int txgbe_fdir_add_signature_filter(struct wx *wx,178 union txgbe_atr_hash_dword input,179 union txgbe_atr_hash_dword common,180 u8 queue)181{182 u32 fdirhashcmd, fdircmd;183 u8 flow_type;184 int err;185 186 /* Get the flow_type in order to program FDIRCMD properly187 * lowest 2 bits are FDIRCMD.L4TYPE, third lowest bit is FDIRCMD.IPV6188 * fifth is FDIRCMD.TUNNEL_FILTER189 */190 flow_type = input.formatted.flow_type;191 switch (flow_type) {192 case TXGBE_ATR_FLOW_TYPE_TCPV4:193 case TXGBE_ATR_FLOW_TYPE_UDPV4:194 case TXGBE_ATR_FLOW_TYPE_SCTPV4:195 case TXGBE_ATR_FLOW_TYPE_TCPV6:196 case TXGBE_ATR_FLOW_TYPE_UDPV6:197 case TXGBE_ATR_FLOW_TYPE_SCTPV6:198 break;199 default:200 wx_err(wx, "Error on flow type input\n");201 return -EINVAL;202 }203 204 /* configure FDIRCMD register */205 fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |206 TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |207 TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;208 fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(flow_type);209 fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);210 211 txgbe_atr_compute_sig_hash(input, common, &fdirhashcmd);212 fdirhashcmd |= TXGBE_RDB_FDIR_HASH_BUCKET_VALID;213 wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhashcmd);214 wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);215 216 wx_dbg(wx, "Tx Queue=%x hash=%x\n", queue, (u32)fdirhashcmd);217 218 err = txgbe_fdir_check_cmd_complete(wx);219 if (err)220 wx_err(wx, "Flow Director command did not complete!\n");221 222 return err;223}224 225void txgbe_atr(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype)226{227 union txgbe_atr_hash_dword common = { .dword = 0 };228 union txgbe_atr_hash_dword input = { .dword = 0 };229 struct wx_q_vector *q_vector = ring->q_vector;230 struct wx_dec_ptype dptype;231 union network_header {232 struct ipv6hdr *ipv6;233 struct iphdr *ipv4;234 void *raw;235 } hdr;236 struct tcphdr *th;237 238 /* if ring doesn't have a interrupt vector, cannot perform ATR */239 if (!q_vector)240 return;241 242 ring->atr_count++;243 dptype = wx_decode_ptype(ptype);244 if (dptype.etype) {245 if (WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)246 return;247 hdr.raw = (void *)skb_inner_network_header(first->skb);248 th = inner_tcp_hdr(first->skb);249 } else {250 if (WX_PTYPE_PKT(ptype) != WX_PTYPE_PKT_IP ||251 WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)252 return;253 hdr.raw = (void *)skb_network_header(first->skb);254 th = tcp_hdr(first->skb);255 }256 257 /* skip this packet since it is invalid or the socket is closing */258 if (!th || th->fin)259 return;260 261 /* sample on all syn packets or once every atr sample count */262 if (!th->syn && ring->atr_count < ring->atr_sample_rate)263 return;264 265 /* reset sample count */266 ring->atr_count = 0;267 268 /* src and dst are inverted, think how the receiver sees them269 *270 * The input is broken into two sections, a non-compressed section271 * containing vm_pool, vlan_id, and flow_type. The rest of the data272 * is XORed together and stored in the compressed dword.273 */274 input.formatted.vlan_id = htons((u16)ptype);275 276 /* since src port and flex bytes occupy the same word XOR them together277 * and write the value to source port portion of compressed dword278 */279 if (first->tx_flags & WX_TX_FLAGS_SW_VLAN)280 common.port.src ^= th->dest ^ first->skb->protocol;281 else if (first->tx_flags & WX_TX_FLAGS_HW_VLAN)282 common.port.src ^= th->dest ^ first->skb->vlan_proto;283 else284 common.port.src ^= th->dest ^ first->protocol;285 common.port.dst ^= th->source;286 287 if (WX_PTYPE_PKT_IPV6 & WX_PTYPE_PKT(ptype)) {288 input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV6;289 common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^290 hdr.ipv6->saddr.s6_addr32[1] ^291 hdr.ipv6->saddr.s6_addr32[2] ^292 hdr.ipv6->saddr.s6_addr32[3] ^293 hdr.ipv6->daddr.s6_addr32[0] ^294 hdr.ipv6->daddr.s6_addr32[1] ^295 hdr.ipv6->daddr.s6_addr32[2] ^296 hdr.ipv6->daddr.s6_addr32[3];297 } else {298 input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;299 common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;300 }301 302 /* This assumes the Rx queue and Tx queue are bound to the same CPU */303 txgbe_fdir_add_signature_filter(q_vector->wx, input, common,304 ring->queue_index);305}306 307int txgbe_fdir_set_input_mask(struct wx *wx, union txgbe_atr_input *input_mask)308{309 u32 fdirm = 0, fdirtcpm = 0, flex = 0;310 311 /* Program the relevant mask registers. If src/dst_port or src/dst_addr312 * are zero, then assume a full mask for that field. Also assume that313 * a VLAN of 0 is unspecified, so mask that out as well. L4type314 * cannot be masked out in this implementation.315 *316 * This also assumes IPv4 only. IPv6 masking isn't supported at this317 * point in time.318 */319 320 /* verify bucket hash is cleared on hash generation */321 if (input_mask->formatted.bkt_hash)322 wx_dbg(wx, "bucket hash should always be 0 in mask\n");323 324 /* Program FDIRM and verify partial masks */325 switch (input_mask->formatted.vm_pool & 0x7F) {326 case 0x0:327 fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_POOL;328 break;329 case 0x7F:330 break;331 default:332 wx_err(wx, "Error on vm pool mask\n");333 return -EINVAL;334 }335 336 switch (input_mask->formatted.flow_type & TXGBE_ATR_L4TYPE_MASK) {337 case 0x0:338 fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_L4P;339 if (input_mask->formatted.dst_port ||340 input_mask->formatted.src_port) {341 wx_err(wx, "Error on src/dst port mask\n");342 return -EINVAL;343 }344 break;345 case TXGBE_ATR_L4TYPE_MASK:346 break;347 default:348 wx_err(wx, "Error on flow type mask\n");349 return -EINVAL;350 }351 352 /* Now mask VM pool and destination IPv6 - bits 5 and 2 */353 wr32(wx, TXGBE_RDB_FDIR_OTHER_MSK, fdirm);354 355 flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0));356 flex &= ~TXGBE_RDB_FDIR_FLEX_CFG_FIELD0;357 flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |358 TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6));359 360 switch ((__force u16)input_mask->formatted.flex_bytes & 0xFFFF) {361 case 0x0000:362 /* Mask Flex Bytes */363 flex |= TXGBE_RDB_FDIR_FLEX_CFG_MSK;364 break;365 case 0xFFFF:366 break;367 default:368 wx_err(wx, "Error on flexible byte mask\n");369 return -EINVAL;370 }371 wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0), flex);372 373 /* store the TCP/UDP port masks, bit reversed from port layout */374 fdirtcpm = ntohs(input_mask->formatted.dst_port);375 fdirtcpm <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;376 fdirtcpm |= ntohs(input_mask->formatted.src_port);377 378 /* write both the same so that UDP and TCP use the same mask */379 wr32(wx, TXGBE_RDB_FDIR_TCP_MSK, ~fdirtcpm);380 wr32(wx, TXGBE_RDB_FDIR_UDP_MSK, ~fdirtcpm);381 wr32(wx, TXGBE_RDB_FDIR_SCTP_MSK, ~fdirtcpm);382 383 /* store source and destination IP masks (little-enian) */384 wr32(wx, TXGBE_RDB_FDIR_SA4_MSK,385 ntohl(~input_mask->formatted.src_ip[0]));386 wr32(wx, TXGBE_RDB_FDIR_DA4_MSK,387 ntohl(~input_mask->formatted.dst_ip[0]));388 389 return 0;390}391 392int txgbe_fdir_write_perfect_filter(struct wx *wx,393 union txgbe_atr_input *input,394 u16 soft_id, u8 queue)395{396 u32 fdirport, fdirvlan, fdirhash, fdircmd;397 int err = 0;398 399 /* currently IPv6 is not supported, must be programmed with 0 */400 wr32(wx, TXGBE_RDB_FDIR_IP6(2), ntohl(input->formatted.src_ip[0]));401 wr32(wx, TXGBE_RDB_FDIR_IP6(1), ntohl(input->formatted.src_ip[1]));402 wr32(wx, TXGBE_RDB_FDIR_IP6(0), ntohl(input->formatted.src_ip[2]));403 404 /* record the source address (little-endian) */405 wr32(wx, TXGBE_RDB_FDIR_SA, ntohl(input->formatted.src_ip[0]));406 407 /* record the first 32 bits of the destination address408 * (little-endian)409 */410 wr32(wx, TXGBE_RDB_FDIR_DA, ntohl(input->formatted.dst_ip[0]));411 412 /* record source and destination port (little-endian)*/413 fdirport = ntohs(input->formatted.dst_port);414 fdirport <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;415 fdirport |= ntohs(input->formatted.src_port);416 wr32(wx, TXGBE_RDB_FDIR_PORT, fdirport);417 418 /* record packet type and flex_bytes (little-endian) */419 fdirvlan = ntohs(input->formatted.flex_bytes);420 fdirvlan <<= TXGBE_RDB_FDIR_FLEX_FLEX_SHIFT;421 fdirvlan |= ntohs(input->formatted.vlan_id);422 wr32(wx, TXGBE_RDB_FDIR_FLEX, fdirvlan);423 424 /* configure FDIRHASH register */425 fdirhash = (__force u32)input->formatted.bkt_hash |426 TXGBE_RDB_FDIR_HASH_BUCKET_VALID |427 TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);428 wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);429 430 /* flush all previous writes to make certain registers are431 * programmed prior to issuing the command432 */433 WX_WRITE_FLUSH(wx);434 435 /* configure FDIRCMD register */436 fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |437 TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |438 TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;439 if (queue == TXGBE_RDB_FDIR_DROP_QUEUE)440 fdircmd |= TXGBE_RDB_FDIR_CMD_DROP;441 fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(input->formatted.flow_type);442 fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);443 fdircmd |= TXGBE_RDB_FDIR_CMD_VT_POOL(input->formatted.vm_pool);444 445 wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);446 err = txgbe_fdir_check_cmd_complete(wx);447 if (err)448 wx_err(wx, "Flow Director command did not complete!\n");449 450 return err;451}452 453int txgbe_fdir_erase_perfect_filter(struct wx *wx,454 union txgbe_atr_input *input,455 u16 soft_id)456{457 u32 fdirhash, fdircmd;458 int err = 0;459 460 /* configure FDIRHASH register */461 fdirhash = (__force u32)input->formatted.bkt_hash;462 fdirhash |= TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);463 wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);464 465 /* flush hash to HW */466 WX_WRITE_FLUSH(wx);467 468 /* Query if filter is present */469 wr32(wx, TXGBE_RDB_FDIR_CMD, TXGBE_RDB_FDIR_CMD_CMD_QUERY_REM_FILT);470 471 err = txgbe_fdir_check_cmd_complete(wx);472 if (err) {473 wx_err(wx, "Flow Director command did not complete!\n");474 return err;475 }476 477 fdircmd = rd32(wx, TXGBE_RDB_FDIR_CMD);478 /* if filter exists in hardware then remove it */479 if (fdircmd & TXGBE_RDB_FDIR_CMD_FILTER_VALID) {480 wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);481 WX_WRITE_FLUSH(wx);482 wr32(wx, TXGBE_RDB_FDIR_CMD,483 TXGBE_RDB_FDIR_CMD_CMD_REMOVE_FLOW);484 }485 486 return 0;487}488 489/**490 * txgbe_fdir_enable - Initialize Flow Director control registers491 * @wx: pointer to hardware structure492 * @fdirctrl: value to write to flow director control register493 **/494static void txgbe_fdir_enable(struct wx *wx, u32 fdirctrl)495{496 u32 val;497 int ret;498 499 /* Prime the keys for hashing */500 wr32(wx, TXGBE_RDB_FDIR_HKEY, TXGBE_ATR_BUCKET_HASH_KEY);501 wr32(wx, TXGBE_RDB_FDIR_SKEY, TXGBE_ATR_SIGNATURE_HASH_KEY);502 503 wr32(wx, TXGBE_RDB_FDIR_CTL, fdirctrl);504 WX_WRITE_FLUSH(wx);505 ret = read_poll_timeout(rd32, val, val & TXGBE_RDB_FDIR_CTL_INIT_DONE,506 1000, 10000, false, wx, TXGBE_RDB_FDIR_CTL);507 508 if (ret < 0)509 wx_dbg(wx, "Flow Director poll time exceeded!\n");510}511 512/**513 * txgbe_init_fdir_signature -Initialize Flow Director sig filters514 * @wx: pointer to hardware structure515 **/516static void txgbe_init_fdir_signature(struct wx *wx)517{518 u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;519 u32 flex = 0;520 521 flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0));522 flex &= ~TXGBE_RDB_FDIR_FLEX_CFG_FIELD0;523 524 flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |525 TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6));526 wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0), flex);527 528 /* Continue setup of fdirctrl register bits:529 * Move the flexible bytes to use the ethertype - shift 6 words530 * Set the maximum length per hash bucket to 0xA filters531 * Send interrupt when 64 filters are left532 */533 fdirctrl |= TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |534 TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |535 TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);536 537 /* write hashes and fdirctrl register, poll for completion */538 txgbe_fdir_enable(wx, fdirctrl);539}540 541/**542 * txgbe_init_fdir_perfect - Initialize Flow Director perfect filters543 * @wx: pointer to hardware structure544 **/545static void txgbe_init_fdir_perfect(struct wx *wx)546{547 u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;548 549 /* Continue setup of fdirctrl register bits:550 * Turn perfect match filtering on551 * Report hash in RSS field of Rx wb descriptor552 * Initialize the drop queue553 * Move the flexible bytes to use the ethertype - shift 6 words554 * Set the maximum length per hash bucket to 0xA filters555 * Send interrupt when 64 (0x4 * 16) filters are left556 */557 fdirctrl |= TXGBE_RDB_FDIR_CTL_PERFECT_MATCH |558 TXGBE_RDB_FDIR_CTL_DROP_Q(TXGBE_RDB_FDIR_DROP_QUEUE) |559 TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |560 TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |561 TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);562 563 /* write hashes and fdirctrl register, poll for completion */564 txgbe_fdir_enable(wx, fdirctrl);565}566 567static void txgbe_fdir_filter_restore(struct wx *wx)568{569 struct txgbe_fdir_filter *filter;570 struct txgbe *txgbe = wx->priv;571 struct hlist_node *node;572 u8 queue = 0;573 int ret = 0;574 575 spin_lock(&txgbe->fdir_perfect_lock);576 577 if (!hlist_empty(&txgbe->fdir_filter_list))578 ret = txgbe_fdir_set_input_mask(wx, &txgbe->fdir_mask);579 580 if (ret)581 goto unlock;582 583 hlist_for_each_entry_safe(filter, node,584 &txgbe->fdir_filter_list, fdir_node) {585 if (filter->action == TXGBE_RDB_FDIR_DROP_QUEUE) {586 queue = TXGBE_RDB_FDIR_DROP_QUEUE;587 } else {588 u32 ring = ethtool_get_flow_spec_ring(filter->action);589 590 if (ring >= wx->num_rx_queues) {591 wx_err(wx, "FDIR restore failed, ring:%u\n",592 ring);593 continue;594 }595 596 /* Map the ring onto the absolute queue index */597 queue = wx->rx_ring[ring]->reg_idx;598 }599 600 ret = txgbe_fdir_write_perfect_filter(wx,601 &filter->filter,602 filter->sw_idx,603 queue);604 if (ret)605 wx_err(wx, "FDIR restore failed, index:%u\n",606 filter->sw_idx);607 }608 609unlock:610 spin_unlock(&txgbe->fdir_perfect_lock);611}612 613void txgbe_configure_fdir(struct wx *wx)614{615 wx_disable_sec_rx_path(wx);616 617 if (test_bit(WX_FLAG_FDIR_HASH, wx->flags)) {618 txgbe_init_fdir_signature(wx);619 } else if (test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)) {620 txgbe_init_fdir_perfect(wx);621 txgbe_fdir_filter_restore(wx);622 }623 624 wx_enable_sec_rx_path(wx);625}626 627void txgbe_fdir_filter_exit(struct wx *wx)628{629 struct txgbe_fdir_filter *filter;630 struct txgbe *txgbe = wx->priv;631 struct hlist_node *node;632 633 spin_lock(&txgbe->fdir_perfect_lock);634 635 hlist_for_each_entry_safe(filter, node,636 &txgbe->fdir_filter_list, fdir_node) {637 hlist_del(&filter->fdir_node);638 kfree(filter);639 }640 txgbe->fdir_filter_count = 0;641 642 spin_unlock(&txgbe->fdir_perfect_lock);643}644