diff options
Diffstat (limited to 'drivers/net/ixgbe/ixgbe_82599.c')
-rw-r--r-- | drivers/net/ixgbe/ixgbe_82599.c | 749 |
1 files changed, 294 insertions, 455 deletions
diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c index bfd3c227cd4a..8d316d9cd29d 100644 --- a/drivers/net/ixgbe/ixgbe_82599.c +++ b/drivers/net/ixgbe/ixgbe_82599.c @@ -1003,7 +1003,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) udelay(10); } if (i >= IXGBE_FDIRCMD_CMD_POLL) { - hw_dbg(hw ,"Flow Director previous command isn't complete, " + hw_dbg(hw, "Flow Director previous command isn't complete, " "aborting table re-initialization.\n"); return IXGBE_ERR_FDIR_REINIT_FAILED; } @@ -1113,13 +1113,10 @@ s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 pballoc) /* Move the flexible bytes to use the ethertype - shift 6 words */ fdirctrl |= (0x6 << IXGBE_FDIRCTRL_FLEX_SHIFT); - fdirctrl |= IXGBE_FDIRCTRL_REPORT_STATUS; /* Prime the keys for hashing */ - IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, - htonl(IXGBE_ATR_BUCKET_HASH_KEY)); - IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, - htonl(IXGBE_ATR_SIGNATURE_HASH_KEY)); + IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, IXGBE_ATR_BUCKET_HASH_KEY); + IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, IXGBE_ATR_SIGNATURE_HASH_KEY); /* * Poll init-done after we write the register. Estimated times: @@ -1209,10 +1206,8 @@ s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 pballoc) fdirctrl |= (0x6 << IXGBE_FDIRCTRL_FLEX_SHIFT); /* Prime the keys for hashing */ - IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, - htonl(IXGBE_ATR_BUCKET_HASH_KEY)); - IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, - htonl(IXGBE_ATR_SIGNATURE_HASH_KEY)); + IXGBE_WRITE_REG(hw, IXGBE_FDIRHKEY, IXGBE_ATR_BUCKET_HASH_KEY); + IXGBE_WRITE_REG(hw, IXGBE_FDIRSKEY, IXGBE_ATR_SIGNATURE_HASH_KEY); /* * Poll init-done after we write the register. Estimated times: @@ -1251,8 +1246,8 @@ s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 pballoc) * @stream: input bitstream to compute the hash on * @key: 32-bit hash key **/ -static u16 ixgbe_atr_compute_hash_82599(struct ixgbe_atr_input *atr_input, - u32 key) +static u32 ixgbe_atr_compute_hash_82599(union ixgbe_atr_input *atr_input, + u32 key) { /* * The algorithm is as follows: @@ -1272,410 +1267,250 @@ static u16 ixgbe_atr_compute_hash_82599(struct ixgbe_atr_input *atr_input, * To simplify for programming, the algorithm is implemented * in software this way: * - * Key[31:0], Stream[335:0] + * key[31:0], hi_hash_dword[31:0], lo_hash_dword[31:0], hash[15:0] + * + * for (i = 0; i < 352; i+=32) + * hi_hash_dword[31:0] ^= Stream[(i+31):i]; + * + * lo_hash_dword[15:0] ^= Stream[15:0]; + * lo_hash_dword[15:0] ^= hi_hash_dword[31:16]; + * lo_hash_dword[31:16] ^= hi_hash_dword[15:0]; * - * tmp_key[11 * 32 - 1:0] = 11{Key[31:0] = key concatenated 11 times - * int_key[350:0] = tmp_key[351:1] - * int_stream[365:0] = Stream[14:0] | Stream[335:0] | Stream[335:321] + * hi_hash_dword[31:0] ^= Stream[351:320]; * - * hash[15:0] = 0; - * for (i = 0; i < 351; i++) { - * if (int_key[i]) - * hash ^= int_stream[(i + 15):i]; + * if(key[0]) + * hash[15:0] ^= Stream[15:0]; + * + * for (i = 0; i < 16; i++) { + * if (key[i]) + * hash[15:0] ^= lo_hash_dword[(i+15):i]; + * if (key[i + 16]) + * hash[15:0] ^= hi_hash_dword[(i+15):i]; * } + * */ + __be32 common_hash_dword = 0; + u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan; + u32 hash_result = 0; + u8 i; - union { - u64 fill[6]; - u32 key[11]; - u8 key_stream[44]; - } tmp_key; + /* record the flow_vm_vlan bits as they are a key part to the hash */ + flow_vm_vlan = ntohl(atr_input->dword_stream[0]); - u8 *stream = (u8 *)atr_input; - u8 int_key[44]; /* upper-most bit unused */ - u8 hash_str[46]; /* upper-most 2 bits unused */ - u16 hash_result = 0; - int i, j, k, h; + /* generate common hash dword */ + for (i = 10; i; i -= 2) + common_hash_dword ^= atr_input->dword_stream[i] ^ + atr_input->dword_stream[i - 1]; - /* - * Initialize the fill member to prevent warnings - * on some compilers - */ - tmp_key.fill[0] = 0; + hi_hash_dword = ntohl(common_hash_dword); - /* First load the temporary key stream */ - for (i = 0; i < 6; i++) { - u64 fillkey = ((u64)key << 32) | key; - tmp_key.fill[i] = fillkey; - } + /* low dword is word swapped version of common */ + lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16); - /* - * Set the interim key for the hashing. Bit 352 is unused, so we must - * shift and compensate when building the key. - */ + /* apply flow ID/VM pool/VLAN ID bits to hash words */ + hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16); - int_key[0] = tmp_key.key_stream[0] >> 1; - for (i = 1, j = 0; i < 44; i++) { - unsigned int this_key = tmp_key.key_stream[j] << 7; - j++; - int_key[i] = (u8)(this_key | (tmp_key.key_stream[j] >> 1)); - } - - /* - * Set the interim bit string for the hashing. Bits 368 and 367 are - * unused, so shift and compensate when building the string. - */ - hash_str[0] = (stream[40] & 0x7f) >> 1; - for (i = 1, j = 40; i < 46; i++) { - unsigned int this_str = stream[j] << 7; - j++; - if (j > 41) - j = 0; - hash_str[i] = (u8)(this_str | (stream[j] >> 1)); - } + /* Process bits 0 and 16 */ + if (key & 0x0001) hash_result ^= lo_hash_dword; + if (key & 0x00010000) hash_result ^= hi_hash_dword; /* - * Now compute the hash. i is the index into hash_str, j is into our - * key stream, k is counting the number of bits, and h interates within - * each byte. + * 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 */ - for (i = 45, j = 43, k = 0; k < 351 && i >= 2 && j >= 0; i--, j--) { - for (h = 0; h < 8 && k < 351; h++, k++) { - if (int_key[j] & (1 << h)) { - /* - * Key bit is set, XOR in the current 16-bit - * string. Example of processing: - * h = 0, - * tmp = (hash_str[i - 2] & 0 << 16) | - * (hash_str[i - 1] & 0xff << 8) | - * (hash_str[i] & 0xff >> 0) - * So tmp = hash_str[15 + k:k], since the - * i + 2 clause rolls off the 16-bit value - * h = 7, - * tmp = (hash_str[i - 2] & 0x7f << 9) | - * (hash_str[i - 1] & 0xff << 1) | - * (hash_str[i] & 0x80 >> 7) - */ - int tmp = (hash_str[i] >> h); - tmp |= (hash_str[i - 1] << (8 - h)); - tmp |= (int)(hash_str[i - 2] & ((1 << h) - 1)) - << (16 - h); - hash_result ^= (u16)tmp; - } - } - } - - return hash_result; -} - -/** - * ixgbe_atr_set_vlan_id_82599 - Sets the VLAN id in the ATR input stream - * @input: input stream to modify - * @vlan: the VLAN id to load - **/ -s32 ixgbe_atr_set_vlan_id_82599(struct ixgbe_atr_input *input, u16 vlan) -{ - input->byte_stream[IXGBE_ATR_VLAN_OFFSET + 1] = vlan >> 8; - input->byte_stream[IXGBE_ATR_VLAN_OFFSET] = vlan & 0xff; - - return 0; -} - -/** - * ixgbe_atr_set_src_ipv4_82599 - Sets the source IPv4 address - * @input: input stream to modify - * @src_addr: the IP address to load - **/ -s32 ixgbe_atr_set_src_ipv4_82599(struct ixgbe_atr_input *input, u32 src_addr) -{ - input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET + 3] = src_addr >> 24; - input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET + 2] = - (src_addr >> 16) & 0xff; - input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET + 1] = - (src_addr >> 8) & 0xff; - input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET] = src_addr & 0xff; - - return 0; -} - -/** - * ixgbe_atr_set_dst_ipv4_82599 - Sets the destination IPv4 address - * @input: input stream to modify - * @dst_addr: the IP address to load - **/ -s32 ixgbe_atr_set_dst_ipv4_82599(struct ixgbe_atr_input *input, u32 dst_addr) -{ - input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET + 3] = dst_addr >> 24; - input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET + 2] = - (dst_addr >> 16) & 0xff; - input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET + 1] = - (dst_addr >> 8) & 0xff; - input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET] = dst_addr & 0xff; - - return 0; -} + lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16); -/** - * ixgbe_atr_set_src_port_82599 - Sets the source port - * @input: input stream to modify - * @src_port: the source port to load - **/ -s32 ixgbe_atr_set_src_port_82599(struct ixgbe_atr_input *input, u16 src_port) -{ - input->byte_stream[IXGBE_ATR_SRC_PORT_OFFSET + 1] = src_port >> 8; - input->byte_stream[IXGBE_ATR_SRC_PORT_OFFSET] = src_port & 0xff; - - return 0; -} - -/** - * ixgbe_atr_set_dst_port_82599 - Sets the destination port - * @input: input stream to modify - * @dst_port: the destination port to load - **/ -s32 ixgbe_atr_set_dst_port_82599(struct ixgbe_atr_input *input, u16 dst_port) -{ - input->byte_stream[IXGBE_ATR_DST_PORT_OFFSET + 1] = dst_port >> 8; - input->byte_stream[IXGBE_ATR_DST_PORT_OFFSET] = dst_port & 0xff; - - return 0; -} - -/** - * ixgbe_atr_set_flex_byte_82599 - Sets the flexible bytes - * @input: input stream to modify - * @flex_bytes: the flexible bytes to load - **/ -s32 ixgbe_atr_set_flex_byte_82599(struct ixgbe_atr_input *input, u16 flex_byte) -{ - input->byte_stream[IXGBE_ATR_FLEX_BYTE_OFFSET + 1] = flex_byte >> 8; - input->byte_stream[IXGBE_ATR_FLEX_BYTE_OFFSET] = flex_byte & 0xff; - - return 0; -} - -/** - * ixgbe_atr_set_l4type_82599 - Sets the layer 4 packet type - * @input: input stream to modify - * @l4type: the layer 4 type value to load - **/ -s32 ixgbe_atr_set_l4type_82599(struct ixgbe_atr_input *input, u8 l4type) -{ - input->byte_stream[IXGBE_ATR_L4TYPE_OFFSET] = l4type; - - return 0; -} - -/** - * ixgbe_atr_get_vlan_id_82599 - Gets the VLAN id from the ATR input stream - * @input: input stream to search - * @vlan: the VLAN id to load - **/ -static s32 ixgbe_atr_get_vlan_id_82599(struct ixgbe_atr_input *input, u16 *vlan) -{ - *vlan = input->byte_stream[IXGBE_ATR_VLAN_OFFSET]; - *vlan |= input->byte_stream[IXGBE_ATR_VLAN_OFFSET + 1] << 8; - - return 0; -} - -/** - * ixgbe_atr_get_src_ipv4_82599 - Gets the source IPv4 address - * @input: input stream to search - * @src_addr: the IP address to load - **/ -static s32 ixgbe_atr_get_src_ipv4_82599(struct ixgbe_atr_input *input, - u32 *src_addr) -{ - *src_addr = input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET]; - *src_addr |= input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET + 1] << 8; - *src_addr |= input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET + 2] << 16; - *src_addr |= input->byte_stream[IXGBE_ATR_SRC_IPV4_OFFSET + 3] << 24; - - return 0; -} -/** - * ixgbe_atr_get_dst_ipv4_82599 - Gets the destination IPv4 address - * @input: input stream to search - * @dst_addr: the IP address to load - **/ -static s32 ixgbe_atr_get_dst_ipv4_82599(struct ixgbe_atr_input *input, - u32 *dst_addr) -{ - *dst_addr = input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET]; - *dst_addr |= input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET + 1] << 8; - *dst_addr |= input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET + 2] << 16; - *dst_addr |= input->byte_stream[IXGBE_ATR_DST_IPV4_OFFSET + 3] << 24; + /* process the remaining 30 bits in the key 2 bits at a time */ + for (i = 15; i; i-- ) { + if (key & (0x0001 << i)) hash_result ^= lo_hash_dword >> i; + if (key & (0x00010000 << i)) hash_result ^= hi_hash_dword >> i; + } - return 0; + return hash_result & IXGBE_ATR_HASH_MASK; } -/** - * ixgbe_atr_get_src_ipv6_82599 - Gets the source IPv6 address - * @input: input stream to search - * @src_addr_1: the first 4 bytes of the IP address to load - * @src_addr_2: the second 4 bytes of the IP address to load - * @src_addr_3: the third 4 bytes of the IP address to load - * @src_addr_4: the fourth 4 bytes of the IP address to load - **/ -static s32 ixgbe_atr_get_src_ipv6_82599(struct ixgbe_atr_input *input, - u32 *src_addr_1, u32 *src_addr_2, - u32 *src_addr_3, u32 *src_addr_4) -{ - *src_addr_1 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 12]; - *src_addr_1 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 13] << 8; - *src_addr_1 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 14] << 16; - *src_addr_1 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 15] << 24; - - *src_addr_2 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 8]; - *src_addr_2 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 9] << 8; - *src_addr_2 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 10] << 16; - *src_addr_2 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 11] << 24; - - *src_addr_3 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 4]; - *src_addr_3 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 5] << 8; - *src_addr_3 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 6] << 16; - *src_addr_3 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 7] << 24; - - *src_addr_4 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET]; - *src_addr_4 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 1] << 8; - *src_addr_4 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 2] << 16; - *src_addr_4 = input->byte_stream[IXGBE_ATR_SRC_IPV6_OFFSET + 3] << 24; - - return 0; -} +/* + * These defines allow us to quickly generate all of the necessary instructions + * in the function below by simply calling out IXGBE_COMPUTE_SIG_HASH_ITERATION + * for values 0 through 15 + */ +#define IXGBE_ATR_COMMON_HASH_KEY \ + (IXGBE_ATR_BUCKET_HASH_KEY & IXGBE_ATR_SIGNATURE_HASH_KEY) +#define IXGBE_COMPUTE_SIG_HASH_ITERATION(_n) \ +do { \ + u32 n = (_n); \ + if (IXGBE_ATR_COMMON_HASH_KEY & (0x01 << n)) \ + common_hash ^= lo_hash_dword >> n; \ + else if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << n)) \ + bucket_hash ^= lo_hash_dword >> n; \ + else if (IXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << n)) \ + sig_hash ^= lo_hash_dword << (16 - n); \ + if (IXGBE_ATR_COMMON_HASH_KEY & (0x01 << (n + 16))) \ + common_hash ^= hi_hash_dword >> n; \ + else if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \ + bucket_hash ^= hi_hash_dword >> n; \ + else if (IXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \ + sig_hash ^= hi_hash_dword << (16 - n); \ +} while (0); /** - * ixgbe_atr_get_src_port_82599 - Gets the source port - * @input: input stream to modify - * @src_port: the source port to load + * ixgbe_atr_compute_sig_hash_82599 - Compute the signature hash + * @stream: input bitstream to compute the hash on * - * Even though the input is given in big-endian, the FDIRPORT registers - * expect the ports to be programmed in little-endian. Hence the need to swap - * endianness when retrieving the data. This can be confusing since the - * internal hash engine expects it to be big-endian. + * This function is almost identical to the function above but contains + * several optomizations 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 s32 ixgbe_atr_get_src_port_82599(struct ixgbe_atr_input *input, - u16 *src_port) +static u32 ixgbe_atr_compute_sig_hash_82599(union ixgbe_atr_hash_dword input, + union ixgbe_atr_hash_dword common) { - *src_port = input->byte_stream[IXGBE_ATR_SRC_PORT_OFFSET] << 8; - *src_port |= input->byte_stream[IXGBE_ATR_SRC_PORT_OFFSET + 1]; + u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan; + u32 sig_hash = 0, bucket_hash = 0, common_hash = 0; - return 0; -} + /* record the flow_vm_vlan bits as they are a key part to the hash */ + flow_vm_vlan = ntohl(input.dword); -/** - * ixgbe_atr_get_dst_port_82599 - Gets the destination port - * @input: input stream to modify - * @dst_port: the destination port to load - * - * Even though the input is given in big-endian, the FDIRPORT registers - * expect the ports to be programmed in little-endian. Hence the need to swap - * endianness when retrieving the data. This can be confusing since the - * internal hash engine expects it to be big-endian. - **/ -static s32 ixgbe_atr_get_dst_port_82599(struct ixgbe_atr_input *input, - u16 *dst_port) -{ - *dst_port = input->byte_stream[IXGBE_ATR_DST_PORT_OFFSET] << 8; - *dst_port |= input->byte_stream[IXGBE_ATR_DST_PORT_OFFSET + 1]; + /* generate common hash dword */ + hi_hash_dword = ntohl(common.dword); - return 0; -} + /* low dword is word swapped version of common */ + lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16); -/** - * ixgbe_atr_get_flex_byte_82599 - Gets the flexible bytes - * @input: input stream to modify - * @flex_bytes: the flexible bytes to load - **/ -static s32 ixgbe_atr_get_flex_byte_82599(struct ixgbe_atr_input *input, - u16 *flex_byte) -{ - *flex_byte = input->byte_stream[IXGBE_ATR_FLEX_BYTE_OFFSET]; - *flex_byte |= input->byte_stream[IXGBE_ATR_FLEX_BYTE_OFFSET + 1] << 8; + /* apply flow ID/VM pool/VLAN ID bits to hash words */ + hi_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan >> 16); - return 0; -} + /* Process bits 0 and 16 */ + IXGBE_COMPUTE_SIG_HASH_ITERATION(0); -/** - * ixgbe_atr_get_l4type_82599 - Gets the layer 4 packet type - * @input: input stream to modify - * @l4type: the layer 4 type value to load - **/ -static s32 ixgbe_atr_get_l4type_82599(struct ixgbe_atr_input *input, - u8 *l4type) -{ - *l4type = input->byte_stream[IXGBE_ATR_L4TYPE_OFFSET]; + /* + * 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 */ + IXGBE_COMPUTE_SIG_HASH_ITERATION(1); + IXGBE_COMPUTE_SIG_HASH_ITERATION(2); + IXGBE_COMPUTE_SIG_HASH_ITERATION(3); + IXGBE_COMPUTE_SIG_HASH_ITERATION(4); + IXGBE_COMPUTE_SIG_HASH_ITERATION(5); + IXGBE_COMPUTE_SIG_HASH_ITERATION(6); + IXGBE_COMPUTE_SIG_HASH_ITERATION(7); + IXGBE_COMPUTE_SIG_HASH_ITERATION(8); + IXGBE_COMPUTE_SIG_HASH_ITERATION(9); + IXGBE_COMPUTE_SIG_HASH_ITERATION(10); + IXGBE_COMPUTE_SIG_HASH_ITERATION(11); + IXGBE_COMPUTE_SIG_HASH_ITERATION(12); + IXGBE_COMPUTE_SIG_HASH_ITERATION(13); + IXGBE_COMPUTE_SIG_HASH_ITERATION(14); + IXGBE_COMPUTE_SIG_HASH_ITERATION(15); + + /* combine common_hash result with signature and bucket hashes */ + bucket_hash ^= common_hash; + bucket_hash &= IXGBE_ATR_HASH_MASK; - return 0; + sig_hash ^= common_hash << 16; + sig_hash &= IXGBE_ATR_HASH_MASK << 16; + + /* return completed signature hash */ + return sig_hash ^ bucket_hash; } /** * ixgbe_atr_add_signature_filter_82599 - Adds a signature hash filter * @hw: pointer to hardware structure - * @stream: input bitstream + * @input: unique input dword + * @common: compressed common input dword * @queue: queue index to direct traffic to **/ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, - struct ixgbe_atr_input *input, + union ixgbe_atr_hash_dword input, + union ixgbe_atr_hash_dword common, u8 queue) { u64 fdirhashcmd; - u64 fdircmd; - u32 fdirhash; - u16 bucket_hash, sig_hash; - u8 l4type; - - bucket_hash = ixgbe_atr_compute_hash_82599(input, - IXGBE_ATR_BUCKET_HASH_KEY); - - /* bucket_hash is only 15 bits */ - bucket_hash &= IXGBE_ATR_HASH_MASK; - - sig_hash = ixgbe_atr_compute_hash_82599(input, - IXGBE_ATR_SIGNATURE_HASH_KEY); - - /* Get the l4type in order to program FDIRCMD properly */ - /* lowest 2 bits are FDIRCMD.L4TYPE, third lowest bit is FDIRCMD.IPV6 */ - ixgbe_atr_get_l4type_82599(input, &l4type); + u32 fdircmd; /* - * The lower 32-bits of fdirhashcmd is for FDIRHASH, the upper 32-bits - * is for FDIRCMD. Then do a 64-bit register write from FDIRHASH. + * Get the flow_type in order to program FDIRCMD properly + * lowest 2 bits are FDIRCMD.L4TYPE, third lowest bit is FDIRCMD.IPV6 */ - fdirhash = sig_hash << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT | bucket_hash; - - fdircmd = (IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE | - IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN); - - switch (l4type & IXGBE_ATR_L4TYPE_MASK) { - case IXGBE_ATR_L4TYPE_TCP: - fdircmd |= IXGBE_FDIRCMD_L4TYPE_TCP; - break; - case IXGBE_ATR_L4TYPE_UDP: - fdircmd |= IXGBE_FDIRCMD_L4TYPE_UDP; - break; - case IXGBE_ATR_L4TYPE_SCTP: - fdircmd |= IXGBE_FDIRCMD_L4TYPE_SCTP; + switch (input.formatted.flow_type) { + case IXGBE_ATR_FLOW_TYPE_TCPV4: + case IXGBE_ATR_FLOW_TYPE_UDPV4: + case IXGBE_ATR_FLOW_TYPE_SCTPV4: + case IXGBE_ATR_FLOW_TYPE_TCPV6: + case IXGBE_ATR_FLOW_TYPE_UDPV6: + case IXGBE_ATR_FLOW_TYPE_SCTPV6: break; default: - hw_dbg(hw, "Error on l4type input\n"); + hw_dbg(hw, " Error on flow type input\n"); return IXGBE_ERR_CONFIG; } - if (l4type & IXGBE_ATR_L4TYPE_IPV6_MASK) - fdircmd |= IXGBE_FDIRCMD_IPV6; + /* configure FDIRCMD register */ + fdircmd = IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE | + IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN; + fdircmd |= input.formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT; + fdircmd |= (u32)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT; - fdircmd |= ((u64)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT); - fdirhashcmd = ((fdircmd << 32) | fdirhash); + /* + * The lower 32-bits of fdirhashcmd is for FDIRHASH, the upper 32-bits + * is for FDIRCMD. Then do a 64-bit register write from FDIRHASH. + */ + fdirhashcmd = (u64)fdircmd << 32; + fdirhashcmd |= ixgbe_atr_compute_sig_hash_82599(input, common); IXGBE_WRITE_REG64(hw, IXGBE_FDIRHASH, fdirhashcmd); + hw_dbg(hw, "Tx Queue=%x hash=%x\n", queue, (u32)fdirhashcmd); + return 0; } /** + * ixgbe_get_fdirtcpm_82599 - generate a tcp port from atr_input_masks + * @input_mask: mask to be bit swapped + * + * The source and destination port masks for flow director are bit swapped + * in that bit 15 effects bit 0, 14 effects 1, 13, 2 etc. In order to + * generate a correctly swapped value we need to bit swap the mask and that + * is what is accomplished by this function. + **/ +static u32 ixgbe_get_fdirtcpm_82599(struct ixgbe_atr_input_masks *input_masks) +{ + u32 mask = ntohs(input_masks->dst_port_mask); + mask <<= IXGBE_FDIRTCPM_DPORTM_SHIFT; + mask |= ntohs(input_masks->src_port_mask); + mask = ((mask & 0x55555555) << 1) | ((mask & 0xAAAAAAAA) >> 1); + mask = ((mask & 0x33333333) << 2) | ((mask & 0xCCCCCCCC) >> 2); + mask = ((mask & 0x0F0F0F0F) << 4) | ((mask & 0xF0F0F0F0) >> 4); + return ((mask & 0x00FF00FF) << 8) | ((mask & 0xFF00FF00) >> 8); +} + +/* + * These two macros are meant to address the fact that we have registers + * that are either all or in part big-endian. As a result on big-endian + * systems we will end up byte swapping the value to little-endian before + * it is byte swapped again and written to the hardware in the original + * big-endian format. + */ +#define IXGBE_STORE_AS_BE32(_value) \ + (((u32)(_value) >> 24) | (((u32)(_value) & 0x00FF0000) >> 8) | \ + (((u32)(_value) & 0x0000FF00) << 8) | ((u32)(_value) << 24)) + +#define IXGBE_WRITE_REG_BE32(a, reg, value) \ + IXGBE_WRITE_REG((a), (reg), IXGBE_STORE_AS_BE32(ntohl(value))) + +#define IXGBE_STORE_AS_BE16(_value) \ + (((u16)(_value) >> 8) | ((u16)(_value) << 8)) + +/** * ixgbe_fdir_add_perfect_filter_82599 - Adds a perfect filter * @hw: pointer to hardware structure * @input: input bitstream @@ -1687,135 +1522,139 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, * hardware writes must be protected from one another. **/ s32 ixgbe_fdir_add_perfect_filter_82599(struct ixgbe_hw *hw, - struct ixgbe_atr_input *input, + union ixgbe_atr_input *input, struct ixgbe_atr_input_masks *input_masks, u16 soft_id, u8 queue) { - u32 fdircmd = 0; u32 fdirhash; - u32 src_ipv4 = 0, dst_ipv4 = 0; - u32 src_ipv6_1, src_ipv6_2, src_ipv6_3, src_ipv6_4; - u16 src_port, dst_port, vlan_id, flex_bytes; - u16 bucket_hash; - u8 l4type; - u8 fdirm = 0; - - /* Get our input values */ - ixgbe_atr_get_l4type_82599(input, &l4type); + u32 fdircmd; + u32 fdirport, fdirtcpm; + u32 fdirvlan; + /* start with VLAN, flex bytes, VM pool, and IPv6 destination masked */ + u32 fdirm = IXGBE_FDIRM_VLANID | IXGBE_FDIRM_VLANP | IXGBE_FDIRM_FLEX | + IXGBE_FDIRM_POOL | IXGBE_FDIRM_DIPv6; /* - * Check l4type formatting, and bail out before we touch the hardware + * Check flow_type formatting, and bail out before we touch the hardware * if there's a configuration issue */ - switch (l4type & IXGBE_ATR_L4TYPE_MASK) { - case IXGBE_ATR_L4TYPE_TCP: - fdircmd |= IXGBE_FDIRCMD_L4TYPE_TCP; - break; - case IXGBE_ATR_L4TYPE_UDP: - fdircmd |= IXGBE_FDIRCMD_L4TYPE_UDP; - break; - case IXGBE_ATR_L4TYPE_SCTP: - fdircmd |= IXGBE_FDIRCMD_L4TYPE_SCTP; + switch (input->formatted.flow_type) { + case IXGBE_ATR_FLOW_TYPE_IPV4: + /* use the L4 protocol mask for raw IPv4/IPv6 traffic */ + fdirm |= IXGBE_FDIRM_L4P; + case IXGBE_ATR_FLOW_TYPE_SCTPV4: + if (input_masks->dst_port_mask || input_masks->src_port_mask) { + hw_dbg(hw, " Error on src/dst port mask\n"); + return IXGBE_ERR_CONFIG; + } + case IXGBE_ATR_FLOW_TYPE_TCPV4: + case IXGBE_ATR_FLOW_TYPE_UDPV4: break; default: - hw_dbg(hw, "Error on l4type input\n"); + hw_dbg(hw, " Error on flow type input\n"); return IXGBE_ERR_CONFIG; } - bucket_hash = ixgbe_atr_compute_hash_82599(input, - IXGBE_ATR_BUCKET_HASH_KEY); - - /* bucket_hash is only 15 bits */ - bucket_hash &= IXGBE_ATR_HASH_MASK; - - ixgbe_atr_get_vlan_id_82599(input, &vlan_id); - ixgbe_atr_get_src_port_82599(input, &src_port); - ixgbe_atr_get_dst_port_82599(input, &dst_port); - ixgbe_atr_get_flex_byte_82599(input, &flex_bytes); - - fdirhash = soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT | bucket_hash; - - /* Now figure out if we're IPv4 or IPv6 */ - if (l4type & IXGBE_ATR_L4TYPE_IPV6_MASK) { - /* IPv6 */ - ixgbe_atr_get_src_ipv6_82599(input, &src_ipv6_1, &src_ipv6_2, - &src_ipv6_3, &src_ipv6_4); - - IXGBE_WRITE_REG(hw, IXGBE_FDIRSIPv6(0), src_ipv6_1); - IXGBE_WRITE_REG(hw, IXGBE_FDIRSIPv6(1), src_ipv6_2); - IXGBE_WRITE_REG(hw, IXGBE_FDIRSIPv6(2), src_ipv6_3); - /* The last 4 bytes is the same register as IPv4 */ - IXGBE_WRITE_REG(hw, IXGBE_FDIRIPSA, src_ipv6_4); - - fdircmd |= IXGBE_FDIRCMD_IPV6; - fdircmd |= IXGBE_FDIRCMD_IPv6DMATCH; - } else { - /* IPv4 */ - ixgbe_atr_get_src_ipv4_82599(input, &src_ipv4); - IXGBE_WRITE_REG(hw, IXGBE_FDIRIPSA, src_ipv4); - } - - ixgbe_atr_get_dst_ipv4_82599(input, &dst_ipv4); - IXGBE_WRITE_REG(hw, IXGBE_FDIRIPDA, dst_ipv4); - - IXGBE_WRITE_REG(hw, IXGBE_FDIRVLAN, (vlan_id | - (flex_bytes << IXGBE_FDIRVLAN_FLEX_SHIFT))); - IXGBE_WRITE_REG(hw, IXGBE_FDIRPORT, (src_port | - (dst_port << IXGBE_FDIRPORT_DESTINATION_SHIFT))); - /* - * Program the relevant mask registers. L4type cannot be - * masked out in this implementation. + * 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. */ - IXGBE_WRITE_REG(hw, IXGBE_FDIRSIP4M, input_masks->src_ip_mask); - IXGBE_WRITE_REG(hw, IXGBE_FDIRDIP4M, input_masks->dst_ip_mask); - - switch (l4type & IXGBE_ATR_L4TYPE_MASK) { - case IXGBE_ATR_L4TYPE_TCP: - IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, input_masks->src_port_mask); - IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, - (IXGBE_READ_REG(hw, IXGBE_FDIRTCPM) | - (input_masks->dst_port_mask << 16))); + + /* Program FDIRM */ + switch (ntohs(input_masks->vlan_id_mask) & 0xEFFF) { + case 0xEFFF: + /* Unmask VLAN ID - bit 0 and fall through to unmask prio */ + fdirm &= ~IXGBE_FDIRM_VLANID; + case 0xE000: + /* Unmask VLAN prio - bit 1 */ + fdirm &= ~IXGBE_FDIRM_VLANP; break; - case IXGBE_ATR_L4TYPE_UDP: - IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, input_masks->src_port_mask); - IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, - (IXGBE_READ_REG(hw, IXGBE_FDIRUDPM) | - (input_masks->src_port_mask << 16))); + case 0x0FFF: + /* Unmask VLAN ID - bit 0 */ + fdirm &= ~IXGBE_FDIRM_VLANID; break; - default: - /* this already would have failed above */ + case 0x0000: + /* do nothing, vlans already masked */ break; + default: + hw_dbg(hw, " Error on VLAN mask\n"); + return IXGBE_ERR_CONFIG; } - /* Program the last mask register, FDIRM */ - if (input_masks->vlan_id_mask) - /* Mask both VLAN and VLANP - bits 0 and 1 */ - fdirm |= 0x3; - - if (input_masks->data_mask) - /* Flex bytes need masking, so mask the whole thing - bit 4 */ - fdirm |= 0x10; + if (input_masks->flex_mask & 0xFFFF) { + if ((input_masks->flex_mask & 0xFFFF) != 0xFFFF) { + hw_dbg(hw, " Error on flexible byte mask\n"); + return IXGBE_ERR_CONFIG; + } + /* Unmask Flex Bytes - bit 4 */ + fdirm &= ~IXGBE_FDIRM_FLEX; + } /* Now mask VM pool and destination IPv6 - bits 5 and 2 */ - fdirm |= 0x24; - IXGBE_WRITE_REG(hw, IXGBE_FDIRM, fdirm); - fdircmd |= IXGBE_FDIRCMD_CMD_ADD_FLOW; - fdircmd |= IXGBE_FDIRCMD_FILTER_UPDATE; - fdircmd |= IXGBE_FDIRCMD_LAST; - fdircmd |= IXGBE_FDIRCMD_QUEUE_EN; - fdircmd |= queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT; + /* store the TCP/UDP port masks, bit reversed from port layout */ + fdirtcpm = ixgbe_get_fdirtcpm_82599(input_masks); + + /* write both the same so that UDP and TCP use the same mask */ + IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, ~fdirtcpm); + IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, ~fdirtcpm); + + /* store source and destination IP masks (big-enian) */ + IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIP4M, + ~input_masks->src_ip_mask[0]); + IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRDIP4M, + ~input_masks->dst_ip_mask[0]); + + /* Apply masks to input data */ + input->formatted.vlan_id &= input_masks->vlan_id_mask; + input->formatted.flex_bytes &= input_masks->flex_mask; + input->formatted.src_port &= input_masks->src_port_mask; + input->formatted.dst_port &= input_masks->dst_port_mask; + input->formatted.src_ip[0] &= input_masks->src_ip_mask[0]; + input->formatted.dst_ip[0] &= input_masks->dst_ip_mask[0]; + + /* record vlan (little-endian) and flex_bytes(big-endian) */ + fdirvlan = + IXGBE_STORE_AS_BE16(ntohs(input->formatted.flex_bytes)); + fdirvlan <<= IXGBE_FDIRVLAN_FLEX_SHIFT; + fdirvlan |= ntohs(input->formatted.vlan_id); + IXGBE_WRITE_REG(hw, IXGBE_FDIRVLAN, fdirvlan); + + /* record source and destination port (little-endian)*/ + fdirport = ntohs(input->formatted.dst_port); + fdirport <<= IXGBE_FDIRPORT_DESTINATION_SHIFT; + fdirport |= ntohs(input->formatted.src_port); + IXGBE_WRITE_REG(hw, IXGBE_FDIRPORT, fdirport); + + /* record the first 32 bits of the destination address (big-endian) */ + IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRIPDA, input->formatted.dst_ip[0]); + + /* record the source address (big-endian) */ + IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRIPSA, input->formatted.src_ip[0]); + + /* configure FDIRCMD register */ + fdircmd = IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE | + IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN; + fdircmd |= input->formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT; + fdircmd |= (u32)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT; + + /* we only want the bucket hash so drop the upper 16 bits */ + fdirhash = ixgbe_atr_compute_hash_82599(input, + IXGBE_ATR_BUCKET_HASH_KEY); + fdirhash |= soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT; IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash); IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, fdircmd); return 0; } + /** * ixgbe_read_analog_reg8_82599 - Reads 8 bit Omer analog register * @hw: pointer to hardware structure |