| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */
- #include <linux/string.h>
- #include <linux/types.h>
- #include <linux/pci.h>
- #include "../libwx/wx_type.h"
- #include "../libwx/wx_lib.h"
- #include "../libwx/wx_hw.h"
- #include "txgbe_type.h"
- #include "txgbe_fdir.h"
- /* These defines allow us to quickly generate all of the necessary instructions
- * in the function below by simply calling out TXGBE_COMPUTE_SIG_HASH_ITERATION
- * for values 0 through 15
- */
- #define TXGBE_ATR_COMMON_HASH_KEY \
- (TXGBE_ATR_BUCKET_HASH_KEY & TXGBE_ATR_SIGNATURE_HASH_KEY)
- #define TXGBE_COMPUTE_SIG_HASH_ITERATION(_n) \
- do { \
- u32 n = (_n); \
- if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << n)) \
- common_hash ^= lo_hash_dword >> n; \
- else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
- bucket_hash ^= lo_hash_dword >> n; \
- else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << n)) \
- sig_hash ^= lo_hash_dword << (16 - n); \
- if (TXGBE_ATR_COMMON_HASH_KEY & (0x01 << (n + 16))) \
- common_hash ^= hi_hash_dword >> n; \
- else if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
- bucket_hash ^= hi_hash_dword >> n; \
- else if (TXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \
- sig_hash ^= hi_hash_dword << (16 - n); \
- } while (0)
- /**
- * txgbe_atr_compute_sig_hash - Compute the signature hash
- * @input: input bitstream to compute the hash on
- * @common: compressed common input dword
- * @hash: pointer to the computed hash
- *
- * This function is almost identical to the function above but contains
- * several optimizations such as unwinding all of the loops, letting the
- * compiler work out all of the conditional ifs since the keys are static
- * defines, and computing two keys at once since the hashed dword stream
- * will be the same for both keys.
- **/
- static void txgbe_atr_compute_sig_hash(union txgbe_atr_hash_dword input,
- union txgbe_atr_hash_dword common,
- u32 *hash)
- {
- u32 sig_hash = 0, bucket_hash = 0, common_hash = 0;
- u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
- u32 i;
- /* record the flow_vm_vlan bits as they are a key part to the hash */
- flow_vm_vlan = ntohl(input.dword);
- /* generate common hash dword */
- hi_hash_dword = ntohl(common.dword);
- /* low dword is word swapped version of common */
- lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
- /* apply flow ID/VM pool/VLAN ID bits to hash words */
- hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
- /* Process bits 0 and 16 */
- TXGBE_COMPUTE_SIG_HASH_ITERATION(0);
- /* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
- * delay this because bit 0 of the stream should not be processed
- * so we do not add the VLAN until after bit 0 was processed
- */
- lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
- /* Process remaining 30 bit of the key */
- for (i = 1; i <= 15; i++)
- TXGBE_COMPUTE_SIG_HASH_ITERATION(i);
- /* combine common_hash result with signature and bucket hashes */
- bucket_hash ^= common_hash;
- bucket_hash &= TXGBE_ATR_HASH_MASK;
- sig_hash ^= common_hash << 16;
- sig_hash &= TXGBE_ATR_HASH_MASK << 16;
- /* return completed signature hash */
- *hash = sig_hash ^ bucket_hash;
- }
- #define TXGBE_COMPUTE_BKT_HASH_ITERATION(_n) \
- do { \
- u32 n = (_n); \
- if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \
- bucket_hash ^= lo_hash_dword >> n; \
- if (TXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
- bucket_hash ^= hi_hash_dword >> n; \
- } while (0)
- /**
- * txgbe_atr_compute_perfect_hash - Compute the perfect filter hash
- * @input: input bitstream to compute the hash on
- * @input_mask: mask for the input bitstream
- *
- * This function serves two main purposes. First it applies the input_mask
- * to the atr_input resulting in a cleaned up atr_input data stream.
- * Secondly it computes the hash and stores it in the bkt_hash field at
- * the end of the input byte stream. This way it will be available for
- * future use without needing to recompute the hash.
- **/
- void txgbe_atr_compute_perfect_hash(union txgbe_atr_input *input,
- union txgbe_atr_input *input_mask)
- {
- u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
- u32 bucket_hash = 0;
- __be32 hi_dword = 0;
- u32 i = 0;
- /* Apply masks to input data */
- for (i = 0; i < 11; i++)
- input->dword_stream[i] &= input_mask->dword_stream[i];
- /* record the flow_vm_vlan bits as they are a key part to the hash */
- flow_vm_vlan = ntohl(input->dword_stream[0]);
- /* generate common hash dword */
- for (i = 1; i <= 10; i++)
- hi_dword ^= input->dword_stream[i];
- hi_hash_dword = ntohl(hi_dword);
- /* low dword is word swapped version of common */
- lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
- /* apply flow ID/VM pool/VLAN ID bits to hash words */
- hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16);
- /* Process bits 0 and 16 */
- TXGBE_COMPUTE_BKT_HASH_ITERATION(0);
- /* apply flow ID/VM pool/VLAN ID bits to lo hash dword, we had to
- * delay this because bit 0 of the stream should not be processed
- * so we do not add the VLAN until after bit 0 was processed
- */
- lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
- /* Process remaining 30 bit of the key */
- for (i = 1; i <= 15; i++)
- TXGBE_COMPUTE_BKT_HASH_ITERATION(i);
- /* Limit hash to 13 bits since max bucket count is 8K.
- * Store result at the end of the input stream.
- */
- input->formatted.bkt_hash = (__force __be16)(bucket_hash & 0x1FFF);
- }
- static int txgbe_fdir_check_cmd_complete(struct wx *wx)
- {
- u32 val;
- return read_poll_timeout_atomic(rd32, val,
- !(val & TXGBE_RDB_FDIR_CMD_CMD_MASK),
- 10, 100, false,
- wx, TXGBE_RDB_FDIR_CMD);
- }
- /**
- * txgbe_fdir_add_signature_filter - Adds a signature hash filter
- * @wx: pointer to hardware structure
- * @input: unique input dword
- * @common: compressed common input dword
- * @queue: queue index to direct traffic to
- *
- * @return: 0 on success and negative on failure
- **/
- static int txgbe_fdir_add_signature_filter(struct wx *wx,
- union txgbe_atr_hash_dword input,
- union txgbe_atr_hash_dword common,
- u8 queue)
- {
- u32 fdirhashcmd, fdircmd;
- u8 flow_type;
- int err;
- /* Get the flow_type in order to program FDIRCMD properly
- * lowest 2 bits are FDIRCMD.L4TYPE, third lowest bit is FDIRCMD.IPV6
- * fifth is FDIRCMD.TUNNEL_FILTER
- */
- flow_type = input.formatted.flow_type;
- switch (flow_type) {
- case TXGBE_ATR_FLOW_TYPE_TCPV4:
- case TXGBE_ATR_FLOW_TYPE_UDPV4:
- case TXGBE_ATR_FLOW_TYPE_SCTPV4:
- case TXGBE_ATR_FLOW_TYPE_TCPV6:
- case TXGBE_ATR_FLOW_TYPE_UDPV6:
- case TXGBE_ATR_FLOW_TYPE_SCTPV6:
- break;
- default:
- wx_err(wx, "Error on flow type input\n");
- return -EINVAL;
- }
- /* configure FDIRCMD register */
- fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |
- TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |
- TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;
- fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(flow_type);
- fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);
- txgbe_atr_compute_sig_hash(input, common, &fdirhashcmd);
- fdirhashcmd |= TXGBE_RDB_FDIR_HASH_BUCKET_VALID;
- wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhashcmd);
- wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);
- wx_dbg(wx, "Tx Queue=%x hash=%x\n", queue, (u32)fdirhashcmd);
- err = txgbe_fdir_check_cmd_complete(wx);
- if (err)
- wx_err(wx, "Flow Director command did not complete!\n");
- return err;
- }
- void txgbe_atr(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype)
- {
- union txgbe_atr_hash_dword common = { .dword = 0 };
- union txgbe_atr_hash_dword input = { .dword = 0 };
- struct wx_q_vector *q_vector = ring->q_vector;
- struct wx_dec_ptype dptype;
- union network_header {
- struct ipv6hdr *ipv6;
- struct iphdr *ipv4;
- void *raw;
- } hdr;
- struct tcphdr *th;
- /* if ring doesn't have a interrupt vector, cannot perform ATR */
- if (!q_vector)
- return;
- ring->atr_count++;
- dptype = wx_decode_ptype(ptype);
- if (dptype.etype) {
- if (WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)
- return;
- hdr.raw = (void *)skb_inner_network_header(first->skb);
- th = inner_tcp_hdr(first->skb);
- } else {
- if (WX_PTYPE_PKT(ptype) != WX_PTYPE_PKT_IP ||
- WX_PTYPE_TYPL4(ptype) != WX_PTYPE_TYP_TCP)
- return;
- hdr.raw = (void *)skb_network_header(first->skb);
- th = tcp_hdr(first->skb);
- }
- /* skip this packet since it is invalid or the socket is closing */
- if (!th || th->fin)
- return;
- /* sample on all syn packets or once every atr sample count */
- if (!th->syn && ring->atr_count < ring->atr_sample_rate)
- return;
- /* reset sample count */
- ring->atr_count = 0;
- /* src and dst are inverted, think how the receiver sees them
- *
- * The input is broken into two sections, a non-compressed section
- * containing vm_pool, vlan_id, and flow_type. The rest of the data
- * is XORed together and stored in the compressed dword.
- */
- input.formatted.vlan_id = htons((u16)ptype);
- /* since src port and flex bytes occupy the same word XOR them together
- * and write the value to source port portion of compressed dword
- */
- if (first->tx_flags & WX_TX_FLAGS_SW_VLAN)
- common.port.src ^= th->dest ^ first->skb->protocol;
- else if (first->tx_flags & WX_TX_FLAGS_HW_VLAN)
- common.port.src ^= th->dest ^ first->skb->vlan_proto;
- else
- common.port.src ^= th->dest ^ first->protocol;
- common.port.dst ^= th->source;
- if (WX_PTYPE_PKT_IPV6 & WX_PTYPE_PKT(ptype)) {
- input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV6;
- common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^
- hdr.ipv6->saddr.s6_addr32[1] ^
- hdr.ipv6->saddr.s6_addr32[2] ^
- hdr.ipv6->saddr.s6_addr32[3] ^
- hdr.ipv6->daddr.s6_addr32[0] ^
- hdr.ipv6->daddr.s6_addr32[1] ^
- hdr.ipv6->daddr.s6_addr32[2] ^
- hdr.ipv6->daddr.s6_addr32[3];
- } else {
- input.formatted.flow_type = TXGBE_ATR_FLOW_TYPE_TCPV4;
- common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;
- }
- /* This assumes the Rx queue and Tx queue are bound to the same CPU */
- txgbe_fdir_add_signature_filter(q_vector->wx, input, common,
- ring->queue_index);
- }
- int txgbe_fdir_set_input_mask(struct wx *wx, union txgbe_atr_input *input_mask)
- {
- u32 fdirm = 0, fdirtcpm = 0, flex = 0;
- /* Program the relevant mask registers. If src/dst_port or src/dst_addr
- * are zero, then assume a full mask for that field. Also assume that
- * a VLAN of 0 is unspecified, so mask that out as well. L4type
- * cannot be masked out in this implementation.
- *
- * This also assumes IPv4 only. IPv6 masking isn't supported at this
- * point in time.
- */
- /* verify bucket hash is cleared on hash generation */
- if (input_mask->formatted.bkt_hash)
- wx_dbg(wx, "bucket hash should always be 0 in mask\n");
- /* Program FDIRM and verify partial masks */
- switch (input_mask->formatted.vm_pool & 0x7F) {
- case 0x0:
- fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_POOL;
- break;
- case 0x7F:
- break;
- default:
- wx_err(wx, "Error on vm pool mask\n");
- return -EINVAL;
- }
- switch (input_mask->formatted.flow_type & TXGBE_ATR_L4TYPE_MASK) {
- case 0x0:
- fdirm |= TXGBE_RDB_FDIR_OTHER_MSK_L4P;
- if (input_mask->formatted.dst_port ||
- input_mask->formatted.src_port) {
- wx_err(wx, "Error on src/dst port mask\n");
- return -EINVAL;
- }
- break;
- case TXGBE_ATR_L4TYPE_MASK:
- break;
- default:
- wx_err(wx, "Error on flow type mask\n");
- return -EINVAL;
- }
- /* Now mask VM pool and destination IPv6 - bits 5 and 2 */
- wr32(wx, TXGBE_RDB_FDIR_OTHER_MSK, fdirm);
- flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0));
- flex &= ~TXGBE_RDB_FDIR_FLEX_CFG_FIELD0;
- flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |
- TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6));
- switch ((__force u16)input_mask->formatted.flex_bytes & 0xFFFF) {
- case 0x0000:
- /* Mask Flex Bytes */
- flex |= TXGBE_RDB_FDIR_FLEX_CFG_MSK;
- break;
- case 0xFFFF:
- break;
- default:
- wx_err(wx, "Error on flexible byte mask\n");
- return -EINVAL;
- }
- wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0), flex);
- /* store the TCP/UDP port masks, bit reversed from port layout */
- fdirtcpm = ntohs(input_mask->formatted.dst_port);
- fdirtcpm <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;
- fdirtcpm |= ntohs(input_mask->formatted.src_port);
- /* write both the same so that UDP and TCP use the same mask */
- wr32(wx, TXGBE_RDB_FDIR_TCP_MSK, ~fdirtcpm);
- wr32(wx, TXGBE_RDB_FDIR_UDP_MSK, ~fdirtcpm);
- wr32(wx, TXGBE_RDB_FDIR_SCTP_MSK, ~fdirtcpm);
- /* store source and destination IP masks (little-enian) */
- wr32(wx, TXGBE_RDB_FDIR_SA4_MSK,
- ntohl(~input_mask->formatted.src_ip[0]));
- wr32(wx, TXGBE_RDB_FDIR_DA4_MSK,
- ntohl(~input_mask->formatted.dst_ip[0]));
- return 0;
- }
- int txgbe_fdir_write_perfect_filter(struct wx *wx,
- union txgbe_atr_input *input,
- u16 soft_id, u8 queue)
- {
- u32 fdirport, fdirvlan, fdirhash, fdircmd;
- int err = 0;
- /* currently IPv6 is not supported, must be programmed with 0 */
- wr32(wx, TXGBE_RDB_FDIR_IP6(2), ntohl(input->formatted.src_ip[0]));
- wr32(wx, TXGBE_RDB_FDIR_IP6(1), ntohl(input->formatted.src_ip[1]));
- wr32(wx, TXGBE_RDB_FDIR_IP6(0), ntohl(input->formatted.src_ip[2]));
- /* record the source address (little-endian) */
- wr32(wx, TXGBE_RDB_FDIR_SA, ntohl(input->formatted.src_ip[0]));
- /* record the first 32 bits of the destination address
- * (little-endian)
- */
- wr32(wx, TXGBE_RDB_FDIR_DA, ntohl(input->formatted.dst_ip[0]));
- /* record source and destination port (little-endian)*/
- fdirport = ntohs(input->formatted.dst_port);
- fdirport <<= TXGBE_RDB_FDIR_PORT_DESTINATION_SHIFT;
- fdirport |= ntohs(input->formatted.src_port);
- wr32(wx, TXGBE_RDB_FDIR_PORT, fdirport);
- /* record packet type and flex_bytes (little-endian) */
- fdirvlan = ntohs(input->formatted.flex_bytes);
- fdirvlan <<= TXGBE_RDB_FDIR_FLEX_FLEX_SHIFT;
- fdirvlan |= ntohs(input->formatted.vlan_id);
- wr32(wx, TXGBE_RDB_FDIR_FLEX, fdirvlan);
- /* configure FDIRHASH register */
- fdirhash = (__force u32)input->formatted.bkt_hash |
- TXGBE_RDB_FDIR_HASH_BUCKET_VALID |
- TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);
- wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
- /* flush all previous writes to make certain registers are
- * programmed prior to issuing the command
- */
- WX_WRITE_FLUSH(wx);
- /* configure FDIRCMD register */
- fdircmd = TXGBE_RDB_FDIR_CMD_CMD_ADD_FLOW |
- TXGBE_RDB_FDIR_CMD_FILTER_UPDATE |
- TXGBE_RDB_FDIR_CMD_LAST | TXGBE_RDB_FDIR_CMD_QUEUE_EN;
- if (queue == TXGBE_RDB_FDIR_DROP_QUEUE)
- fdircmd |= TXGBE_RDB_FDIR_CMD_DROP;
- fdircmd |= TXGBE_RDB_FDIR_CMD_FLOW_TYPE(input->formatted.flow_type);
- fdircmd |= TXGBE_RDB_FDIR_CMD_RX_QUEUE(queue);
- fdircmd |= TXGBE_RDB_FDIR_CMD_VT_POOL(input->formatted.vm_pool);
- wr32(wx, TXGBE_RDB_FDIR_CMD, fdircmd);
- err = txgbe_fdir_check_cmd_complete(wx);
- if (err)
- wx_err(wx, "Flow Director command did not complete!\n");
- return err;
- }
- int txgbe_fdir_erase_perfect_filter(struct wx *wx,
- union txgbe_atr_input *input,
- u16 soft_id)
- {
- u32 fdirhash, fdircmd;
- int err = 0;
- /* configure FDIRHASH register */
- fdirhash = (__force u32)input->formatted.bkt_hash;
- fdirhash |= TXGBE_RDB_FDIR_HASH_SIG_SW_INDEX(soft_id);
- wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
- /* flush hash to HW */
- WX_WRITE_FLUSH(wx);
- /* Query if filter is present */
- wr32(wx, TXGBE_RDB_FDIR_CMD, TXGBE_RDB_FDIR_CMD_CMD_QUERY_REM_FILT);
- err = txgbe_fdir_check_cmd_complete(wx);
- if (err) {
- wx_err(wx, "Flow Director command did not complete!\n");
- return err;
- }
- fdircmd = rd32(wx, TXGBE_RDB_FDIR_CMD);
- /* if filter exists in hardware then remove it */
- if (fdircmd & TXGBE_RDB_FDIR_CMD_FILTER_VALID) {
- wr32(wx, TXGBE_RDB_FDIR_HASH, fdirhash);
- WX_WRITE_FLUSH(wx);
- wr32(wx, TXGBE_RDB_FDIR_CMD,
- TXGBE_RDB_FDIR_CMD_CMD_REMOVE_FLOW);
- }
- return 0;
- }
- /**
- * txgbe_fdir_enable - Initialize Flow Director control registers
- * @wx: pointer to hardware structure
- * @fdirctrl: value to write to flow director control register
- **/
- static void txgbe_fdir_enable(struct wx *wx, u32 fdirctrl)
- {
- u32 val;
- int ret;
- /* Prime the keys for hashing */
- wr32(wx, TXGBE_RDB_FDIR_HKEY, TXGBE_ATR_BUCKET_HASH_KEY);
- wr32(wx, TXGBE_RDB_FDIR_SKEY, TXGBE_ATR_SIGNATURE_HASH_KEY);
- wr32(wx, TXGBE_RDB_FDIR_CTL, fdirctrl);
- WX_WRITE_FLUSH(wx);
- ret = read_poll_timeout(rd32, val, val & TXGBE_RDB_FDIR_CTL_INIT_DONE,
- 1000, 10000, false, wx, TXGBE_RDB_FDIR_CTL);
- if (ret < 0)
- wx_dbg(wx, "Flow Director poll time exceeded!\n");
- }
- /**
- * txgbe_init_fdir_signature -Initialize Flow Director sig filters
- * @wx: pointer to hardware structure
- **/
- static void txgbe_init_fdir_signature(struct wx *wx)
- {
- u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;
- u32 flex = 0;
- flex = rd32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0));
- flex &= ~TXGBE_RDB_FDIR_FLEX_CFG_FIELD0;
- flex |= (TXGBE_RDB_FDIR_FLEX_CFG_BASE_MAC |
- TXGBE_RDB_FDIR_FLEX_CFG_OFST(0x6));
- wr32(wx, TXGBE_RDB_FDIR_FLEX_CFG(0), flex);
- /* Continue setup of fdirctrl register bits:
- * Move the flexible bytes to use the ethertype - shift 6 words
- * Set the maximum length per hash bucket to 0xA filters
- * Send interrupt when 64 filters are left
- */
- fdirctrl |= TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |
- TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |
- TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);
- /* write hashes and fdirctrl register, poll for completion */
- txgbe_fdir_enable(wx, fdirctrl);
- }
- /**
- * txgbe_init_fdir_perfect - Initialize Flow Director perfect filters
- * @wx: pointer to hardware structure
- **/
- static void txgbe_init_fdir_perfect(struct wx *wx)
- {
- u32 fdirctrl = TXGBE_FDIR_PBALLOC_64K;
- /* Continue setup of fdirctrl register bits:
- * Turn perfect match filtering on
- * Report hash in RSS field of Rx wb descriptor
- * Initialize the drop queue
- * Move the flexible bytes to use the ethertype - shift 6 words
- * Set the maximum length per hash bucket to 0xA filters
- * Send interrupt when 64 (0x4 * 16) filters are left
- */
- fdirctrl |= TXGBE_RDB_FDIR_CTL_PERFECT_MATCH |
- TXGBE_RDB_FDIR_CTL_DROP_Q(TXGBE_RDB_FDIR_DROP_QUEUE) |
- TXGBE_RDB_FDIR_CTL_HASH_BITS(0xF) |
- TXGBE_RDB_FDIR_CTL_MAX_LENGTH(0xA) |
- TXGBE_RDB_FDIR_CTL_FULL_THRESH(4);
- /* write hashes and fdirctrl register, poll for completion */
- txgbe_fdir_enable(wx, fdirctrl);
- }
- static void txgbe_fdir_filter_restore(struct wx *wx)
- {
- struct txgbe_fdir_filter *filter;
- struct txgbe *txgbe = wx->priv;
- struct hlist_node *node;
- u8 queue = 0;
- int ret = 0;
- spin_lock(&txgbe->fdir_perfect_lock);
- if (!hlist_empty(&txgbe->fdir_filter_list))
- ret = txgbe_fdir_set_input_mask(wx, &txgbe->fdir_mask);
- if (ret)
- goto unlock;
- hlist_for_each_entry_safe(filter, node,
- &txgbe->fdir_filter_list, fdir_node) {
- if (filter->action == TXGBE_RDB_FDIR_DROP_QUEUE) {
- queue = TXGBE_RDB_FDIR_DROP_QUEUE;
- } else {
- u32 ring = ethtool_get_flow_spec_ring(filter->action);
- if (ring >= wx->num_rx_queues) {
- wx_err(wx, "FDIR restore failed, ring:%u\n",
- ring);
- continue;
- }
- /* Map the ring onto the absolute queue index */
- queue = wx->rx_ring[ring]->reg_idx;
- }
- ret = txgbe_fdir_write_perfect_filter(wx,
- &filter->filter,
- filter->sw_idx,
- queue);
- if (ret)
- wx_err(wx, "FDIR restore failed, index:%u\n",
- filter->sw_idx);
- }
- unlock:
- spin_unlock(&txgbe->fdir_perfect_lock);
- }
- void txgbe_configure_fdir(struct wx *wx)
- {
- wx_disable_sec_rx_path(wx);
- if (test_bit(WX_FLAG_FDIR_HASH, wx->flags)) {
- txgbe_init_fdir_signature(wx);
- } else if (test_bit(WX_FLAG_FDIR_PERFECT, wx->flags)) {
- txgbe_init_fdir_perfect(wx);
- txgbe_fdir_filter_restore(wx);
- }
- wx_enable_sec_rx_path(wx);
- }
- void txgbe_fdir_filter_exit(struct wx *wx)
- {
- struct txgbe_fdir_filter *filter;
- struct txgbe *txgbe = wx->priv;
- struct hlist_node *node;
- spin_lock(&txgbe->fdir_perfect_lock);
- hlist_for_each_entry_safe(filter, node,
- &txgbe->fdir_filter_list, fdir_node) {
- hlist_del(&filter->fdir_node);
- kfree(filter);
- }
- txgbe->fdir_filter_count = 0;
- spin_unlock(&txgbe->fdir_perfect_lock);
- }
|