Commit efca91e8 authored by Przemyslaw Patynowski's avatar Przemyslaw Patynowski Committed by Tony Nguyen

i40e: Add flow director support for IPv6

Flow director for IPv6 is not supported.
1) Implementation of support for IPv6 flow director.
2) Added handlers for addition of TCP6, UDP6, SCTP6, IPv6.
3) Refactored legacy code to make it more generic.
4) Added packet templates for TCP6, UDP6, SCTP6, IPv6.
5) Added handling of IPv6 source and destination address for flow director.
6) Improved argument passing for source and destination portin TCP6, UDP6
   and SCTP6.
7) Added handling of ethtool -n for IPv6, TCP6,UDP6, SCTP6.
8) Used correct bit flag regarding FLEXOFF field of flow director data
   descriptor.

Without this patch, there would be no support for flow director on IPv6,
TCP6, UDP6, SCTP6.
Tested based on x710 datasheet by using:
ethtool -N enp133s0f0 flow-type tcp4 src-port 13 dst-port 37 user-def 0x44142 action 1
ethtool -N enp133s0f0 flow-type tcp6 src-port 13 dst-port 40 user-def 0x44142 action 2
ethtool -N enp133s0f0 flow-type udp4 src-port 20 dst-port 40 user-def 0x44142 action 3
ethtool -N enp133s0f0 flow-type udp6 src-port 25 dst-port 40 user-def 0x44142 action 4
ethtool -N enp133s0f0 flow-type sctp4 src-port 55 dst-port 65 user-def 0x44142 action 5
ethtool -N enp133s0f0 flow-type sctp6 src-port 60 dst-port 40 user-def 0x44142 action 6
ethtool -N enp133s0f0 flow-type ip4 src-ip 1.1.1.1 dst-ip 1.1.1.4 user-def 0x44142 action 7
ethtool -N enp133s0f0 flow-type ip6 src-ip fe80::3efd:feff:fe6f:bbbb dst-ip fe80::3efd:feff:fe6f:aaaa user-def 0x44142 action 8
Then send traffic from client which matches the criteria provided to ethtool.
Observe that packets are redirected to user set queues with ethtool -S <interface>
Signed-off-by: default avatarPrzemyslaw Patynowski <przemyslawx.patynowski@intel.com>
Tested-by: default avatarTony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent 95f352dc
......@@ -213,10 +213,12 @@ struct i40e_fdir_filter {
struct hlist_node fdir_node;
/* filter ipnut set */
u8 flow_type;
u8 ip4_proto;
u8 ipl4_proto;
/* TX packet view of src and dst */
__be32 dst_ip;
__be32 src_ip;
__be32 dst_ip6[4];
__be32 src_ip6[4];
__be16 src_port;
__be16 dst_port;
__be32 sctp_v_tag;
......@@ -477,6 +479,11 @@ struct i40e_pf {
u16 fd_sctp4_filter_cnt;
u16 fd_ip4_filter_cnt;
u16 fd_tcp6_filter_cnt;
u16 fd_udp6_filter_cnt;
u16 fd_sctp6_filter_cnt;
u16 fd_ip6_filter_cnt;
/* Flexible filter table values that need to be programmed into
* hardware, which expects L3 and L4 to be programmed separately. We
* need to ensure that the values are in ascended order and don't have
......
......@@ -3222,13 +3222,30 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
fsp->m_u.usr_ip4_spec.proto = 0;
}
/* Reverse the src and dest notion, since the HW views them from
* Tx perspective where as the user expects it from Rx filter view.
*/
fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip;
fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip;
if (fsp->flow_type == IPV6_USER_FLOW ||
fsp->flow_type == UDP_V6_FLOW ||
fsp->flow_type == TCP_V6_FLOW ||
fsp->flow_type == SCTP_V6_FLOW) {
/* Reverse the src and dest notion, since the HW views them
* from Tx perspective where as the user expects it from
* Rx filter view.
*/
fsp->h_u.tcp_ip6_spec.psrc = rule->dst_port;
fsp->h_u.tcp_ip6_spec.pdst = rule->src_port;
memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->src_ip6,
sizeof(__be32) * 4);
memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->dst_ip6,
sizeof(__be32) * 4);
} else {
/* Reverse the src and dest notion, since the HW views them
* from Tx perspective where as the user expects it from
* Rx filter view.
*/
fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip;
fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip;
}
switch (rule->flow_type) {
case SCTP_V4_FLOW:
......@@ -3240,9 +3257,21 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
case UDP_V4_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
break;
case SCTP_V6_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
break;
case TCP_V6_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
break;
case UDP_V6_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
break;
case IP_USER_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
break;
case IPV6_USER_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
break;
default:
/* If we have stored a filter with a flow type not listed here
* it is almost certainly a driver bug. WARN(), and then
......@@ -3258,6 +3287,20 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
input_set = i40e_read_fd_input_set(pf, index);
no_input_set:
if (input_set & I40E_L3_V6_SRC_MASK) {
fsp->m_u.tcp_ip6_spec.ip6src[0] = htonl(0xFFFFFFFF);
fsp->m_u.tcp_ip6_spec.ip6src[1] = htonl(0xFFFFFFFF);
fsp->m_u.tcp_ip6_spec.ip6src[2] = htonl(0xFFFFFFFF);
fsp->m_u.tcp_ip6_spec.ip6src[3] = htonl(0xFFFFFFFF);
}
if (input_set & I40E_L3_V6_DST_MASK) {
fsp->m_u.tcp_ip6_spec.ip6dst[0] = htonl(0xFFFFFFFF);
fsp->m_u.tcp_ip6_spec.ip6dst[1] = htonl(0xFFFFFFFF);
fsp->m_u.tcp_ip6_spec.ip6dst[2] = htonl(0xFFFFFFFF);
fsp->m_u.tcp_ip6_spec.ip6dst[3] = htonl(0xFFFFFFFF);
}
if (input_set & I40E_L3_SRC_MASK)
fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF);
......@@ -3921,6 +3964,14 @@ static const char *i40e_flow_str(struct ethtool_rx_flow_spec *fsp)
return "sctp4";
case IP_USER_FLOW:
return "ip4";
case TCP_V6_FLOW:
return "tcp6";
case UDP_V6_FLOW:
return "udp6";
case SCTP_V6_FLOW:
return "sctp6";
case IPV6_USER_FLOW:
return "ip6";
default:
return "unknown";
}
......@@ -4056,9 +4107,14 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
struct ethtool_rx_flow_spec *fsp,
struct i40e_rx_flow_userdef *userdef)
{
struct i40e_pf *pf = vsi->back;
static const __be32 ipv6_full_mask[4] = {cpu_to_be32(0xffffffff),
cpu_to_be32(0xffffffff), cpu_to_be32(0xffffffff),
cpu_to_be32(0xffffffff)};
struct ethtool_tcpip6_spec *tcp_ip6_spec;
struct ethtool_usrip6_spec *usr_ip6_spec;
struct ethtool_tcpip4_spec *tcp_ip4_spec;
struct ethtool_usrip4_spec *usr_ip4_spec;
struct i40e_pf *pf = vsi->back;
u64 current_mask, new_mask;
bool new_flex_offset = false;
bool flex_l3 = false;
......@@ -4080,11 +4136,28 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
fdir_filter_count = &pf->fd_udp4_filter_cnt;
break;
case SCTP_V6_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
fdir_filter_count = &pf->fd_sctp6_filter_cnt;
break;
case TCP_V6_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
fdir_filter_count = &pf->fd_tcp6_filter_cnt;
break;
case UDP_V6_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
fdir_filter_count = &pf->fd_udp6_filter_cnt;
break;
case IP_USER_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
fdir_filter_count = &pf->fd_ip4_filter_cnt;
flex_l3 = true;
break;
case IPV6_USER_FLOW:
index = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
fdir_filter_count = &pf->fd_ip6_filter_cnt;
flex_l3 = true;
break;
default:
return -EOPNOTSUPP;
}
......@@ -4147,6 +4220,53 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
return -EOPNOTSUPP;
break;
case SCTP_V6_FLOW:
new_mask &= ~I40E_VERIFY_TAG_MASK;
fallthrough;
case TCP_V6_FLOW:
case UDP_V6_FLOW:
tcp_ip6_spec = &fsp->m_u.tcp_ip6_spec;
/* Check if user provided IPv6 source address. */
if (ipv6_addr_equal((struct in6_addr *)&tcp_ip6_spec->ip6src,
(struct in6_addr *)&ipv6_full_mask))
new_mask |= I40E_L3_V6_SRC_MASK;
else if (ipv6_addr_any((struct in6_addr *)
&tcp_ip6_spec->ip6src))
new_mask &= ~I40E_L3_V6_SRC_MASK;
else
return -EOPNOTSUPP;
/* Check if user provided destination address. */
if (ipv6_addr_equal((struct in6_addr *)&tcp_ip6_spec->ip6dst,
(struct in6_addr *)&ipv6_full_mask))
new_mask |= I40E_L3_V6_DST_MASK;
else if (ipv6_addr_any((struct in6_addr *)
&tcp_ip6_spec->ip6src))
new_mask &= ~I40E_L3_V6_DST_MASK;
else
return -EOPNOTSUPP;
/* L4 source port */
if (tcp_ip6_spec->psrc == htons(0xFFFF))
new_mask |= I40E_L4_SRC_MASK;
else if (!tcp_ip6_spec->psrc)
new_mask &= ~I40E_L4_SRC_MASK;
else
return -EOPNOTSUPP;
/* L4 destination port */
if (tcp_ip6_spec->pdst == htons(0xFFFF))
new_mask |= I40E_L4_DST_MASK;
else if (!tcp_ip6_spec->pdst)
new_mask &= ~I40E_L4_DST_MASK;
else
return -EOPNOTSUPP;
/* Filtering on Traffic Classes is not supported. */
if (tcp_ip6_spec->tclass)
return -EOPNOTSUPP;
break;
case IP_USER_FLOW:
usr_ip4_spec = &fsp->m_u.usr_ip4_spec;
......@@ -4186,6 +4306,45 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
if (usr_ip4_spec->proto)
return -EINVAL;
break;
case IPV6_USER_FLOW:
usr_ip6_spec = &fsp->m_u.usr_ip6_spec;
/* Check if user provided IPv6 source address. */
if (ipv6_addr_equal((struct in6_addr *)&usr_ip6_spec->ip6src,
(struct in6_addr *)&ipv6_full_mask))
new_mask |= I40E_L3_V6_SRC_MASK;
else if (ipv6_addr_any((struct in6_addr *)
&usr_ip6_spec->ip6src))
new_mask &= ~I40E_L3_V6_SRC_MASK;
else
return -EOPNOTSUPP;
/* Check if user provided destination address. */
if (ipv6_addr_equal((struct in6_addr *)&usr_ip6_spec->ip6dst,
(struct in6_addr *)&ipv6_full_mask))
new_mask |= I40E_L3_V6_DST_MASK;
else if (ipv6_addr_any((struct in6_addr *)
&usr_ip6_spec->ip6src))
new_mask &= ~I40E_L3_V6_DST_MASK;
else
return -EOPNOTSUPP;
if (usr_ip6_spec->l4_4_bytes == htonl(0xFFFFFFFF))
new_mask |= I40E_L4_SRC_MASK | I40E_L4_DST_MASK;
else if (!usr_ip6_spec->l4_4_bytes)
new_mask &= ~(I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
else
return -EOPNOTSUPP;
/* Filtering on Traffic class is not supported. */
if (usr_ip6_spec->tclass)
return -EOPNOTSUPP;
/* Filtering on L4 protocol is not supported */
if (usr_ip6_spec->l4_proto)
return -EINVAL;
break;
default:
return -EOPNOTSUPP;
......@@ -4370,7 +4529,7 @@ static bool i40e_match_fdir_filter(struct i40e_fdir_filter *a,
a->dst_port != b->dst_port ||
a->src_port != b->src_port ||
a->flow_type != b->flow_type ||
a->ip4_proto != b->ip4_proto)
a->ipl4_proto != b->ipl4_proto)
return false;
return true;
......@@ -4528,15 +4687,33 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src;
input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
input->flow_type = fsp->flow_type & ~FLOW_EXT;
input->ip4_proto = fsp->h_u.usr_ip4_spec.proto;
/* Reverse the src and dest notion, since the HW expects them to be from
* Tx perspective where as the input from user is from Rx filter view.
*/
input->dst_port = fsp->h_u.tcp_ip4_spec.psrc;
input->src_port = fsp->h_u.tcp_ip4_spec.pdst;
input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src;
input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
if (input->flow_type == IPV6_USER_FLOW ||
input->flow_type == UDP_V6_FLOW ||
input->flow_type == TCP_V6_FLOW ||
input->flow_type == SCTP_V6_FLOW) {
/* Reverse the src and dest notion, since the HW expects them
* to be from Tx perspective where as the input from user is
* from Rx filter view.
*/
input->ipl4_proto = fsp->h_u.usr_ip6_spec.l4_proto;
input->dst_port = fsp->h_u.tcp_ip6_spec.psrc;
input->src_port = fsp->h_u.tcp_ip6_spec.pdst;
memcpy(input->dst_ip6, fsp->h_u.ah_ip6_spec.ip6src,
sizeof(__be32) * 4);
memcpy(input->src_ip6, fsp->h_u.ah_ip6_spec.ip6dst,
sizeof(__be32) * 4);
} else {
/* Reverse the src and dest notion, since the HW expects them
* to be from Tx perspective where as the input from user is
* from Rx filter view.
*/
input->ipl4_proto = fsp->h_u.usr_ip4_spec.proto;
input->dst_port = fsp->h_u.tcp_ip4_spec.psrc;
input->src_port = fsp->h_u.tcp_ip4_spec.pdst;
input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src;
input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
}
if (userdef.flex_filter) {
input->flex_filter = true;
......
......@@ -3495,6 +3495,24 @@ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi)
i40e_set_rx_mode(vsi->netdev);
}
/**
* i40e_reset_fdir_filter_cnt - Reset flow director filter counters
* @pf: Pointer to the targeted PF
*
* Set all flow director counters to 0.
*/
static void i40e_reset_fdir_filter_cnt(struct i40e_pf *pf)
{
pf->fd_tcp4_filter_cnt = 0;
pf->fd_udp4_filter_cnt = 0;
pf->fd_sctp4_filter_cnt = 0;
pf->fd_ip4_filter_cnt = 0;
pf->fd_tcp6_filter_cnt = 0;
pf->fd_udp6_filter_cnt = 0;
pf->fd_sctp6_filter_cnt = 0;
pf->fd_ip6_filter_cnt = 0;
}
/**
* i40e_fdir_filter_restore - Restore the Sideband Flow Director filters
* @vsi: Pointer to the targeted VSI
......@@ -3512,10 +3530,7 @@ static void i40e_fdir_filter_restore(struct i40e_vsi *vsi)
return;
/* Reset FDir counters as we're replaying all existing filters */
pf->fd_tcp4_filter_cnt = 0;
pf->fd_udp4_filter_cnt = 0;
pf->fd_sctp4_filter_cnt = 0;
pf->fd_ip4_filter_cnt = 0;
i40e_reset_fdir_filter_cnt(pf);
hlist_for_each_entry_safe(filter, node,
&pf->fdir_filter_list, fdir_node) {
......@@ -8763,32 +8778,51 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
INIT_LIST_HEAD(&pf->l4_flex_pit_list);
pf->fdir_pf_active_filters = 0;
pf->fd_tcp4_filter_cnt = 0;
pf->fd_udp4_filter_cnt = 0;
pf->fd_sctp4_filter_cnt = 0;
pf->fd_ip4_filter_cnt = 0;
i40e_reset_fdir_filter_cnt(pf);
/* Reprogram the default input set for TCP/IPv4 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_TCP,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK |
I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
/* Reprogram the default input set for TCP/IPv6 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV6_TCP,
I40E_L3_V6_SRC_MASK | I40E_L3_V6_DST_MASK |
I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
/* Reprogram the default input set for UDP/IPv4 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_UDP,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK |
I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
/* Reprogram the default input set for UDP/IPv6 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV6_UDP,
I40E_L3_V6_SRC_MASK | I40E_L3_V6_DST_MASK |
I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
/* Reprogram the default input set for SCTP/IPv4 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_SCTP,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK |
I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
/* Reprogram the default input set for SCTP/IPv6 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV6_SCTP,
I40E_L3_V6_SRC_MASK | I40E_L3_V6_DST_MASK |
I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
/* Reprogram the default input set for Other/IPv4 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK);
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_FRAG_IPV4,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK);
/* Reprogram the default input set for Other/IPv6 */
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV6_OTHER,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK);
i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_FRAG_IPV6,
I40E_L3_SRC_MASK | I40E_L3_DST_MASK);
}
/**
......@@ -9270,8 +9304,17 @@ static void i40e_delete_invalid_filter(struct i40e_pf *pf,
case SCTP_V4_FLOW:
pf->fd_sctp4_filter_cnt--;
break;
case TCP_V6_FLOW:
pf->fd_tcp6_filter_cnt--;
break;
case UDP_V6_FLOW:
pf->fd_udp6_filter_cnt--;
break;
case SCTP_V6_FLOW:
pf->fd_udp6_filter_cnt--;
break;
case IP_USER_FLOW:
switch (filter->ip4_proto) {
switch (filter->ipl4_proto) {
case IPPROTO_TCP:
pf->fd_tcp4_filter_cnt--;
break;
......@@ -9286,6 +9329,22 @@ static void i40e_delete_invalid_filter(struct i40e_pf *pf,
break;
}
break;
case IPV6_USER_FLOW:
switch (filter->ipl4_proto) {
case IPPROTO_TCP:
pf->fd_tcp6_filter_cnt--;
break;
case IPPROTO_UDP:
pf->fd_udp6_filter_cnt--;
break;
case IPPROTO_SCTP:
pf->fd_sctp6_filter_cnt--;
break;
case IPPROTO_IP:
pf->fd_ip6_filter_cnt--;
break;
}
break;
}
/* Remove the filter from the list and free memory */
......@@ -9319,7 +9378,7 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
* rules active.
*/
if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) &&
(pf->fd_tcp4_filter_cnt == 0))
pf->fd_tcp4_filter_cnt == 0 && pf->fd_tcp6_filter_cnt == 0)
i40e_reenable_fdir_atr(pf);
/* if hw had a problem adding a filter, delete it */
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment