(struct ethtool_rx_flow_spec *)&cmd->fs;
struct i40e_fdir_filter *rule = NULL;
struct hlist_node *node2;
+ u64 input_set;
+ u16 index;
hlist_for_each_entry_safe(rule, node2,
&pf->fdir_filter_list, fdir_node) {
fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip;
fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip;
- /* Set the mask fields */
- fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFF);
- fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFF);
- fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF);
- fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFFFFFF);
+ switch (rule->flow_type) {
+ case TCP_V4_FLOW:
+ index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+ break;
+ case UDP_V4_FLOW:
+ index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
+ break;
+ case IP_USER_FLOW:
+ index = I40E_FILTER_PCTYPE_NONF_IPV4_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
+ * assign the input_set as if all fields are enabled to avoid
+ * reading unassigned memory.
+ */
+ WARN(1, "Missing input set index for flow_type %d\n",
+ rule->flow_type);
+ input_set = 0xFFFFFFFFFFFFFFFFULL;
+ goto no_input_set;
+ }
+
+ input_set = i40e_read_fd_input_set(pf, index);
+
+no_input_set:
+ if (input_set & I40E_L3_SRC_MASK)
+ fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFF);
+
+ if (input_set & I40E_L3_DST_MASK)
+ fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFF);
+
+ if (input_set & I40E_L4_SRC_MASK)
+ fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFFFFFF);
+
+ if (input_set & I40E_L4_DST_MASK)
+ fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFFFFFF);
if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET)
fsp->ring_cookie = RX_CLS_FLOW_DISC;
/**
* i40e_check_fdir_input_set - Check that a given rx_flow_spec mask is valid
+ * @vsi: pointer to the targeted VSI
* @fsp: pointer to Rx flow specification
*
* Ensures that a given ethtool_rx_flow_spec has a valid mask.
**/
-static int i40e_check_fdir_input_set(struct ethtool_rx_flow_spec *fsp)
+static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
+ struct ethtool_rx_flow_spec *fsp)
{
+ struct i40e_pf *pf = vsi->back;
struct ethtool_tcpip4_spec *tcp_ip4_spec;
struct ethtool_usrip4_spec *usr_ip4_spec;
+ u64 current_mask, new_mask;
+ u16 index;
+
+ switch (fsp->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+ break;
+ case UDP_V4_FLOW:
+ index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
+ break;
+ case IP_USER_FLOW:
+ index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Read the current input set from register memory. */
+ current_mask = i40e_read_fd_input_set(pf, index);
+ new_mask = current_mask;
/* Verify the provided mask is valid. */
switch (fsp->flow_type & ~FLOW_EXT) {
- case SCTP_V4_FLOW:
case TCP_V4_FLOW:
case UDP_V4_FLOW:
tcp_ip4_spec = &fsp->m_u.tcp_ip4_spec;
/* IPv4 source address */
- if (!tcp_ip4_spec->ip4src || ~tcp_ip4_spec->ip4src)
+ if (tcp_ip4_spec->ip4src == htonl(0xFFFFFFFF))
+ new_mask |= I40E_L3_SRC_MASK;
+ else if (!tcp_ip4_spec->ip4src)
+ new_mask &= ~I40E_L3_SRC_MASK;
+ else
return -EOPNOTSUPP;
/* IPv4 destination address */
- if (!tcp_ip4_spec->ip4dst || ~tcp_ip4_spec->ip4dst)
+ if (tcp_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
+ new_mask |= I40E_L3_DST_MASK;
+ else if (!tcp_ip4_spec->ip4dst)
+ new_mask &= ~I40E_L3_DST_MASK;
+ else
return -EOPNOTSUPP;
/* L4 source port */
- if (!tcp_ip4_spec->psrc || (__be16)~tcp_ip4_spec->psrc)
+ if (tcp_ip4_spec->psrc == htons(0xFFFF))
+ new_mask |= I40E_L4_SRC_MASK;
+ else if (!tcp_ip4_spec->psrc)
+ new_mask &= ~I40E_L4_SRC_MASK;
+ else
return -EOPNOTSUPP;
/* L4 destination port */
- if (!tcp_ip4_spec->pdst || (__be16)~tcp_ip4_spec->pdst)
+ if (tcp_ip4_spec->pdst == htons(0xFFFF))
+ new_mask |= I40E_L4_DST_MASK;
+ else if (!tcp_ip4_spec->pdst)
+ new_mask &= ~I40E_L4_DST_MASK;
+ else
return -EOPNOTSUPP;
/* Filtering on Type of Service is not supported. */
usr_ip4_spec = &fsp->m_u.usr_ip4_spec;
/* IPv4 source address */
- if (!usr_ip4_spec->ip4src || ~usr_ip4_spec->ip4src)
+ if (usr_ip4_spec->ip4src == htonl(0xFFFFFFFF))
+ new_mask |= I40E_L3_SRC_MASK;
+ else if (!usr_ip4_spec->ip4src)
+ new_mask &= ~I40E_L3_SRC_MASK;
+ else
return -EOPNOTSUPP;
/* IPv4 destination address */
- if (!usr_ip4_spec->ip4dst || ~usr_ip4_spec->ip4dst)
+ if (usr_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
+ new_mask |= I40E_L3_DST_MASK;
+ else if (!usr_ip4_spec->ip4dst)
+ new_mask &= ~I40E_L3_DST_MASK;
+ else
return -EOPNOTSUPP;
/* First 4 bytes of L4 header */
- if (!usr_ip4_spec->l4_4_bytes || ~usr_ip4_spec->l4_4_bytes)
+ if (usr_ip4_spec->l4_4_bytes == htonl(0xFFFFFFFF))
+ new_mask |= I40E_L4_SRC_MASK | I40E_L4_DST_MASK;
+ else if (!usr_ip4_spec->l4_4_bytes)
+ new_mask &= ~(I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
+ else
return -EOPNOTSUPP;
/* Filtering on Type of Service is not supported. */
/* L4 protocol doesn't have a mask field. */
if (usr_ip4_spec->proto)
return -EINVAL;
+
break;
default:
return -EOPNOTSUPP;
}
+ if (new_mask != current_mask)
+ return -EOPNOTSUPP;
+
return 0;
}
if (fsp->flow_type & FLOW_MAC_EXT)
return -EINVAL;
- ret = i40e_check_fdir_input_set(fsp);
+ ret = i40e_check_fdir_input_set(vsi, fsp);
if (ret)
return ret;