Commit 527691bf authored by Haiyue Wang's avatar Haiyue Wang Committed by Tony Nguyen
Browse files

iavf: Support IPv4 Flow Director filters



Support the addition and deletion of IPv4 filters.

Supported fields are: src-ip, dst-ip, src-port, dst-port and l4proto
Supported flow-types are: tcp4, udp4, sctp4, ip4, ah4, esp4

Example usage:
ethtool -N ens787f0v0 flow-type tcp4 src-ip 192.168.0.20 \
  dst-ip 192.168.0.21 tos 4 src-port 22 dst-port 23 action 8

L2TPv3 over IP with 'Session ID' 17:
ethtool -N ens787f0v0 flow-type ip4 l4proto 115 l4data 17 action 3

Signed-off-by: default avatarHaiyue Wang <haiyue.wang@intel.com>
Tested-by: default avatarChen Bo <BoX.C.Chen@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent 0dbfbabb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -11,5 +11,5 @@ subdir-ccflags-y += -I$(src)

obj-$(CONFIG_IAVF) += iavf.o

iavf-objs := iavf_main.o iavf_ethtool.o iavf_virtchnl.o \
iavf-objs := iavf_main.o iavf_ethtool.o iavf_virtchnl.o iavf_fdir.o \
	     iavf_txrx.o iavf_common.o iavf_adminq.o iavf_client.o
+404 −0
Original line number Diff line number Diff line
@@ -827,6 +827,396 @@ static int iavf_set_per_queue_coalesce(struct net_device *netdev, u32 queue,
	return __iavf_set_coalesce(netdev, ec, queue);
}

/**
 * iavf_fltr_to_ethtool_flow - convert filter type values to ethtool
 * flow type values
 * @flow: filter type to be converted
 *
 * Returns the corresponding ethtool flow type.
 */
static int iavf_fltr_to_ethtool_flow(enum iavf_fdir_flow_type flow)
{
	switch (flow) {
	case IAVF_FDIR_FLOW_IPV4_TCP:
		return TCP_V4_FLOW;
	case IAVF_FDIR_FLOW_IPV4_UDP:
		return UDP_V4_FLOW;
	case IAVF_FDIR_FLOW_IPV4_SCTP:
		return SCTP_V4_FLOW;
	case IAVF_FDIR_FLOW_IPV4_AH:
		return AH_V4_FLOW;
	case IAVF_FDIR_FLOW_IPV4_ESP:
		return ESP_V4_FLOW;
	case IAVF_FDIR_FLOW_IPV4_OTHER:
		return IPV4_USER_FLOW;
	default:
		/* 0 is undefined ethtool flow */
		return 0;
	}
}

/**
 * iavf_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
 * @eth: Ethtool flow type to be converted
 *
 * Returns flow enum
 */
static enum iavf_fdir_flow_type iavf_ethtool_flow_to_fltr(int eth)
{
	switch (eth) {
	case TCP_V4_FLOW:
		return IAVF_FDIR_FLOW_IPV4_TCP;
	case UDP_V4_FLOW:
		return IAVF_FDIR_FLOW_IPV4_UDP;
	case SCTP_V4_FLOW:
		return IAVF_FDIR_FLOW_IPV4_SCTP;
	case AH_V4_FLOW:
		return IAVF_FDIR_FLOW_IPV4_AH;
	case ESP_V4_FLOW:
		return IAVF_FDIR_FLOW_IPV4_ESP;
	case IPV4_USER_FLOW:
		return IAVF_FDIR_FLOW_IPV4_OTHER;
	default:
		return IAVF_FDIR_FLOW_NONE;
	}
}

/**
 * iavf_get_ethtool_fdir_entry - fill ethtool structure with Flow Director filter data
 * @adapter: the VF adapter structure that contains filter list
 * @cmd: ethtool command data structure to receive the filter data
 *
 * Returns 0 as expected for success by ethtool
 */
static int
iavf_get_ethtool_fdir_entry(struct iavf_adapter *adapter,
			    struct ethtool_rxnfc *cmd)
{
	struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
	struct iavf_fdir_fltr *rule = NULL;
	int ret = 0;

	if (!FDIR_FLTR_SUPPORT(adapter))
		return -EOPNOTSUPP;

	spin_lock_bh(&adapter->fdir_fltr_lock);

	rule = iavf_find_fdir_fltr_by_loc(adapter, fsp->location);
	if (!rule) {
		ret = -EINVAL;
		goto release_lock;
	}

	fsp->flow_type = iavf_fltr_to_ethtool_flow(rule->flow_type);

	memset(&fsp->m_u, 0, sizeof(fsp->m_u));
	memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));

	switch (fsp->flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW:
	case SCTP_V4_FLOW:
		fsp->h_u.tcp_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
		fsp->h_u.tcp_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
		fsp->h_u.tcp_ip4_spec.psrc = rule->ip_data.src_port;
		fsp->h_u.tcp_ip4_spec.pdst = rule->ip_data.dst_port;
		fsp->h_u.tcp_ip4_spec.tos = rule->ip_data.tos;
		fsp->m_u.tcp_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
		fsp->m_u.tcp_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
		fsp->m_u.tcp_ip4_spec.psrc = rule->ip_mask.src_port;
		fsp->m_u.tcp_ip4_spec.pdst = rule->ip_mask.dst_port;
		fsp->m_u.tcp_ip4_spec.tos = rule->ip_mask.tos;
		break;
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		fsp->h_u.ah_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
		fsp->h_u.ah_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
		fsp->h_u.ah_ip4_spec.spi = rule->ip_data.spi;
		fsp->h_u.ah_ip4_spec.tos = rule->ip_data.tos;
		fsp->m_u.ah_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
		fsp->m_u.ah_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
		fsp->m_u.ah_ip4_spec.spi = rule->ip_mask.spi;
		fsp->m_u.ah_ip4_spec.tos = rule->ip_mask.tos;
		break;
	case IPV4_USER_FLOW:
		fsp->h_u.usr_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
		fsp->h_u.usr_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
		fsp->h_u.usr_ip4_spec.l4_4_bytes = rule->ip_data.l4_header;
		fsp->h_u.usr_ip4_spec.tos = rule->ip_data.tos;
		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
		fsp->h_u.usr_ip4_spec.proto = rule->ip_data.proto;
		fsp->m_u.usr_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
		fsp->m_u.usr_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
		fsp->m_u.usr_ip4_spec.l4_4_bytes = rule->ip_mask.l4_header;
		fsp->m_u.usr_ip4_spec.tos = rule->ip_mask.tos;
		fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
		fsp->m_u.usr_ip4_spec.proto = rule->ip_mask.proto;
		break;
	default:
		ret = -EINVAL;
		break;
	}

	if (rule->action == VIRTCHNL_ACTION_DROP)
		fsp->ring_cookie = RX_CLS_FLOW_DISC;
	else
		fsp->ring_cookie = rule->q_index;

release_lock:
	spin_unlock_bh(&adapter->fdir_fltr_lock);
	return ret;
}

/**
 * iavf_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
 * @adapter: the VF adapter structure containing the filter list
 * @cmd: ethtool command data structure
 * @rule_locs: ethtool array passed in from OS to receive filter IDs
 *
 * Returns 0 as expected for success by ethtool
 */
static int
iavf_get_fdir_fltr_ids(struct iavf_adapter *adapter, struct ethtool_rxnfc *cmd,
		       u32 *rule_locs)
{
	struct iavf_fdir_fltr *fltr;
	unsigned int cnt = 0;
	int val = 0;

	if (!FDIR_FLTR_SUPPORT(adapter))
		return -EOPNOTSUPP;

	cmd->data = IAVF_MAX_FDIR_FILTERS;

	spin_lock_bh(&adapter->fdir_fltr_lock);

	list_for_each_entry(fltr, &adapter->fdir_list_head, list) {
		if (cnt == cmd->rule_cnt) {
			val = -EMSGSIZE;
			goto release_lock;
		}
		rule_locs[cnt] = fltr->loc;
		cnt++;
	}

release_lock:
	spin_unlock_bh(&adapter->fdir_fltr_lock);
	if (!val)
		cmd->rule_cnt = cnt;

	return val;
}

/**
 * iavf_add_fdir_fltr_info - Set the input set for Flow Director filter
 * @adapter: pointer to the VF adapter structure
 * @fsp: pointer to ethtool Rx flow specification
 * @fltr: filter structure
 */
static int
iavf_add_fdir_fltr_info(struct iavf_adapter *adapter, struct ethtool_rx_flow_spec *fsp,
			struct iavf_fdir_fltr *fltr)
{
	u32 flow_type, q_index = 0;
	enum virtchnl_action act;

	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
		act = VIRTCHNL_ACTION_DROP;
	} else {
		q_index = fsp->ring_cookie;
		if (q_index >= adapter->num_active_queues)
			return -EINVAL;

		act = VIRTCHNL_ACTION_QUEUE;
	}

	fltr->action = act;
	fltr->loc = fsp->location;
	fltr->q_index = q_index;

	flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
	fltr->flow_type = iavf_ethtool_flow_to_fltr(flow_type);

	switch (flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW:
	case SCTP_V4_FLOW:
		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.tcp_ip4_spec.ip4src;
		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
		fltr->ip_data.src_port = fsp->h_u.tcp_ip4_spec.psrc;
		fltr->ip_data.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
		fltr->ip_data.tos = fsp->h_u.tcp_ip4_spec.tos;
		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.tcp_ip4_spec.ip4src;
		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.tcp_ip4_spec.ip4dst;
		fltr->ip_mask.src_port = fsp->m_u.tcp_ip4_spec.psrc;
		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
		fltr->ip_mask.tos = fsp->m_u.tcp_ip4_spec.tos;
		break;
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.ah_ip4_spec.ip4src;
		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.ah_ip4_spec.ip4dst;
		fltr->ip_data.spi = fsp->h_u.ah_ip4_spec.spi;
		fltr->ip_data.tos = fsp->h_u.ah_ip4_spec.tos;
		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.ah_ip4_spec.ip4src;
		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.ah_ip4_spec.ip4dst;
		fltr->ip_mask.spi = fsp->m_u.ah_ip4_spec.spi;
		fltr->ip_mask.tos = fsp->m_u.ah_ip4_spec.tos;
		break;
	case IPV4_USER_FLOW:
		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.usr_ip4_spec.ip4src;
		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.usr_ip4_spec.ip4dst;
		fltr->ip_data.l4_header = fsp->h_u.usr_ip4_spec.l4_4_bytes;
		fltr->ip_data.tos = fsp->h_u.usr_ip4_spec.tos;
		fltr->ip_data.proto = fsp->h_u.usr_ip4_spec.proto;
		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.usr_ip4_spec.ip4src;
		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.usr_ip4_spec.ip4dst;
		fltr->ip_mask.l4_header = fsp->m_u.usr_ip4_spec.l4_4_bytes;
		fltr->ip_mask.tos = fsp->m_u.usr_ip4_spec.tos;
		fltr->ip_mask.proto = fsp->m_u.usr_ip4_spec.proto;
		break;
	default:
		/* not doing un-parsed flow types */
		return -EINVAL;
	}

	if (iavf_fdir_is_dup_fltr(adapter, fltr))
		return -EEXIST;

	return iavf_fill_fdir_add_msg(adapter, fltr);
}

/**
 * iavf_add_fdir_ethtool - add Flow Director filter
 * @adapter: pointer to the VF adapter structure
 * @cmd: command to add Flow Director filter
 *
 * Returns 0 on success and negative values for failure
 */
static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rxnfc *cmd)
{
	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
	struct iavf_fdir_fltr *fltr;
	int count = 50;
	int err;

	if (!FDIR_FLTR_SUPPORT(adapter))
		return -EOPNOTSUPP;

	if (fsp->flow_type & FLOW_MAC_EXT)
		return -EINVAL;

	if (adapter->fdir_active_fltr >= IAVF_MAX_FDIR_FILTERS) {
		dev_err(&adapter->pdev->dev,
			"Unable to add Flow Director filter because VF reached the limit of max allowed filters (%u)\n",
			IAVF_MAX_FDIR_FILTERS);
		return -ENOSPC;
	}

	spin_lock_bh(&adapter->fdir_fltr_lock);
	if (iavf_find_fdir_fltr_by_loc(adapter, fsp->location)) {
		dev_err(&adapter->pdev->dev, "Failed to add Flow Director filter, it already exists\n");
		spin_unlock_bh(&adapter->fdir_fltr_lock);
		return -EEXIST;
	}
	spin_unlock_bh(&adapter->fdir_fltr_lock);

	fltr = kzalloc(sizeof(*fltr), GFP_KERNEL);
	if (!fltr)
		return -ENOMEM;

	while (test_and_set_bit(__IAVF_IN_CRITICAL_TASK,
				&adapter->crit_section)) {
		if (--count == 0) {
			kfree(fltr);
			return -EINVAL;
		}
		udelay(1);
	}

	err = iavf_add_fdir_fltr_info(adapter, fsp, fltr);
	if (err)
		goto ret;

	spin_lock_bh(&adapter->fdir_fltr_lock);
	iavf_fdir_list_add_fltr(adapter, fltr);
	adapter->fdir_active_fltr++;
	fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
	adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
	spin_unlock_bh(&adapter->fdir_fltr_lock);

	mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);

ret:
	if (err && fltr)
		kfree(fltr);

	clear_bit(__IAVF_IN_CRITICAL_TASK, &adapter->crit_section);
	return err;
}

/**
 * iavf_del_fdir_ethtool - delete Flow Director filter
 * @adapter: pointer to the VF adapter structure
 * @cmd: command to delete Flow Director filter
 *
 * Returns 0 on success and negative values for failure
 */
static int iavf_del_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rxnfc *cmd)
{
	struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
	struct iavf_fdir_fltr *fltr = NULL;
	int err = 0;

	if (!FDIR_FLTR_SUPPORT(adapter))
		return -EOPNOTSUPP;

	spin_lock_bh(&adapter->fdir_fltr_lock);
	fltr = iavf_find_fdir_fltr_by_loc(adapter, fsp->location);
	if (fltr) {
		if (fltr->state == IAVF_FDIR_FLTR_ACTIVE) {
			fltr->state = IAVF_FDIR_FLTR_DEL_REQUEST;
			adapter->aq_required |= IAVF_FLAG_AQ_DEL_FDIR_FILTER;
		} else {
			err = -EBUSY;
		}
	} else if (adapter->fdir_active_fltr) {
		err = -EINVAL;
	}
	spin_unlock_bh(&adapter->fdir_fltr_lock);

	if (fltr && fltr->state == IAVF_FDIR_FLTR_DEL_REQUEST)
		mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);

	return err;
}

/**
 * iavf_set_rxnfc - command to set Rx flow rules.
 * @netdev: network interface device structure
 * @cmd: ethtool rxnfc command
 *
 * Returns 0 for success and negative values for errors
 */
static int iavf_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
{
	struct iavf_adapter *adapter = netdev_priv(netdev);
	int ret = -EOPNOTSUPP;

	switch (cmd->cmd) {
	case ETHTOOL_SRXCLSRLINS:
		ret = iavf_add_fdir_ethtool(adapter, cmd);
		break;
	case ETHTOOL_SRXCLSRLDEL:
		ret = iavf_del_fdir_ethtool(adapter, cmd);
		break;
	default:
		break;
	}

	return ret;
}

/**
 * iavf_get_rxnfc - command to get RX flow classification rules
 * @netdev: network interface device structure
@@ -846,6 +1236,19 @@ static int iavf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
		cmd->data = adapter->num_active_queues;
		ret = 0;
		break;
	case ETHTOOL_GRXCLSRLCNT:
		if (!FDIR_FLTR_SUPPORT(adapter))
			break;
		cmd->rule_cnt = adapter->fdir_active_fltr;
		cmd->data = IAVF_MAX_FDIR_FILTERS;
		ret = 0;
		break;
	case ETHTOOL_GRXCLSRULE:
		ret = iavf_get_ethtool_fdir_entry(adapter, cmd);
		break;
	case ETHTOOL_GRXCLSRLALL:
		ret = iavf_get_fdir_fltr_ids(adapter, cmd, (u32 *)rule_locs);
		break;
	case ETHTOOL_GRXFH:
		netdev_info(netdev,
			    "RSS hash info is not available to vf, use pf.\n");
@@ -1025,6 +1428,7 @@ static const struct ethtool_ops iavf_ethtool_ops = {
	.set_coalesce		= iavf_set_coalesce,
	.get_per_queue_coalesce = iavf_get_per_queue_coalesce,
	.set_per_queue_coalesce = iavf_set_per_queue_coalesce,
	.set_rxnfc		= iavf_set_rxnfc,
	.get_rxnfc		= iavf_get_rxnfc,
	.get_rxfh_indir_size	= iavf_get_rxfh_indir_size,
	.get_rxfh		= iavf_get_rxfh,
+428 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020, Intel Corporation. */

/* flow director ethtool support for iavf */

#include "iavf.h"

/**
 * iavf_fill_fdir_ip4_hdr - fill the IPv4 protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the IPv4 protocol header is set successfully
 */
static int
iavf_fill_fdir_ip4_hdr(struct iavf_fdir_fltr *fltr,
		       struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	struct iphdr *iph = (struct iphdr *)hdr->buffer;

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);

	if (fltr->ip_mask.tos == U8_MAX) {
		iph->tos = fltr->ip_data.tos;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DSCP);
	}

	if (fltr->ip_mask.proto == U8_MAX) {
		iph->protocol = fltr->ip_data.proto;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, PROT);
	}

	if (fltr->ip_mask.v4_addrs.src_ip == htonl(U32_MAX)) {
		iph->saddr = fltr->ip_data.v4_addrs.src_ip;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
	}

	if (fltr->ip_mask.v4_addrs.dst_ip == htonl(U32_MAX)) {
		iph->daddr = fltr->ip_data.v4_addrs.dst_ip;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
	}

	return 0;
}

/**
 * iavf_fill_fdir_tcp_hdr - fill the TCP protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the TCP protocol header is set successfully
 */
static int
iavf_fill_fdir_tcp_hdr(struct iavf_fdir_fltr *fltr,
		       struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	struct tcphdr *tcph = (struct tcphdr *)hdr->buffer;

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);

	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
		tcph->source = fltr->ip_data.src_port;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, SRC_PORT);
	}

	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
		tcph->dest = fltr->ip_data.dst_port;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, DST_PORT);
	}

	return 0;
}

/**
 * iavf_fill_fdir_udp_hdr - fill the UDP protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the UDP protocol header is set successfully
 */
static int
iavf_fill_fdir_udp_hdr(struct iavf_fdir_fltr *fltr,
		       struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	struct udphdr *udph = (struct udphdr *)hdr->buffer;

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);

	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
		udph->source = fltr->ip_data.src_port;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, SRC_PORT);
	}

	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
		udph->dest = fltr->ip_data.dst_port;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, DST_PORT);
	}

	return 0;
}

/**
 * iavf_fill_fdir_sctp_hdr - fill the SCTP protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the SCTP protocol header is set successfully
 */
static int
iavf_fill_fdir_sctp_hdr(struct iavf_fdir_fltr *fltr,
			struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	struct sctphdr *sctph = (struct sctphdr *)hdr->buffer;

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);

	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
		sctph->source = fltr->ip_data.src_port;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, SRC_PORT);
	}

	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
		sctph->dest = fltr->ip_data.dst_port;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, DST_PORT);
	}

	return 0;
}

/**
 * iavf_fill_fdir_ah_hdr - fill the AH protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the AH protocol header is set successfully
 */
static int
iavf_fill_fdir_ah_hdr(struct iavf_fdir_fltr *fltr,
		      struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	struct ip_auth_hdr *ah = (struct ip_auth_hdr *)hdr->buffer;

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, AH);

	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
		ah->spi = fltr->ip_data.spi;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, AH, SPI);
	}

	return 0;
}

/**
 * iavf_fill_fdir_esp_hdr - fill the ESP protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the ESP protocol header is set successfully
 */
static int
iavf_fill_fdir_esp_hdr(struct iavf_fdir_fltr *fltr,
		       struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)hdr->buffer;

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);

	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
		esph->spi = fltr->ip_data.spi;
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
	}

	return 0;
}

/**
 * iavf_fill_fdir_l4_hdr - fill the L4 protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the L4 protocol header is set successfully
 */
static int
iavf_fill_fdir_l4_hdr(struct iavf_fdir_fltr *fltr,
		      struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr;
	__be32 *l4_4_data;

	if (!fltr->ip_mask.proto) /* IPv4/IPv6 header only */
		return 0;

	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
	l4_4_data = (__be32 *)hdr->buffer;

	/* L2TPv3 over IP with 'Session ID' */
	if (fltr->ip_data.proto == 115 && fltr->ip_mask.l4_header == htonl(U32_MAX)) {
		VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, L2TPV3);
		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, L2TPV3, SESS_ID);

		*l4_4_data = fltr->ip_data.l4_header;
	} else {
		return -EOPNOTSUPP;
	}

	return 0;
}

/**
 * iavf_fill_fdir_eth_hdr - fill the Ethernet protocol header
 * @fltr: Flow Director filter data structure
 * @proto_hdrs: Flow Director protocol headers data structure
 *
 * Returns 0 if the Ethernet protocol header is set successfully
 */
static int
iavf_fill_fdir_eth_hdr(struct iavf_fdir_fltr *fltr,
		       struct virtchnl_proto_hdrs *proto_hdrs)
{
	struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];

	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ETH);

	return 0;
}

/**
 * iavf_fill_fdir_add_msg - fill the Flow Director filter into virtchnl message
 * @adapter: pointer to the VF adapter structure
 * @fltr: Flow Director filter data structure
 *
 * Returns 0 if the add Flow Director virtchnl message is filled successfully
 */
int iavf_fill_fdir_add_msg(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
{
	struct virtchnl_fdir_add *vc_msg = &fltr->vc_add_msg;
	struct virtchnl_proto_hdrs *proto_hdrs;
	int err;

	proto_hdrs = &vc_msg->rule_cfg.proto_hdrs;

	err = iavf_fill_fdir_eth_hdr(fltr, proto_hdrs); /* L2 always exists */
	if (err)
		return err;

	switch (fltr->flow_type) {
	case IAVF_FDIR_FLOW_IPV4_TCP:
		err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
		      iavf_fill_fdir_tcp_hdr(fltr, proto_hdrs);
		break;
	case IAVF_FDIR_FLOW_IPV4_UDP:
		err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
		      iavf_fill_fdir_udp_hdr(fltr, proto_hdrs);
		break;
	case IAVF_FDIR_FLOW_IPV4_SCTP:
		err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
		      iavf_fill_fdir_sctp_hdr(fltr, proto_hdrs);
		break;
	case IAVF_FDIR_FLOW_IPV4_AH:
		err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
		      iavf_fill_fdir_ah_hdr(fltr, proto_hdrs);
		break;
	case IAVF_FDIR_FLOW_IPV4_ESP:
		err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
		      iavf_fill_fdir_esp_hdr(fltr, proto_hdrs);
		break;
	case IAVF_FDIR_FLOW_IPV4_OTHER:
		err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
		      iavf_fill_fdir_l4_hdr(fltr, proto_hdrs);
		break;
	default:
		err = -EINVAL;
		break;
	}

	if (err)
		return err;

	vc_msg->vsi_id = adapter->vsi.id;
	vc_msg->rule_cfg.action_set.count = 1;
	vc_msg->rule_cfg.action_set.actions[0].type = fltr->action;
	vc_msg->rule_cfg.action_set.actions[0].act_conf.queue.index = fltr->q_index;

	return 0;
}

/**
 * iavf_fdir_flow_proto_name - get the flow protocol name
 * @flow_type: Flow Director filter flow type
 **/
static const char *iavf_fdir_flow_proto_name(enum iavf_fdir_flow_type flow_type)
{
	switch (flow_type) {
	case IAVF_FDIR_FLOW_IPV4_TCP:
		return "TCP";
	case IAVF_FDIR_FLOW_IPV4_UDP:
		return "UDP";
	case IAVF_FDIR_FLOW_IPV4_SCTP:
		return "SCTP";
	case IAVF_FDIR_FLOW_IPV4_AH:
		return "AH";
	case IAVF_FDIR_FLOW_IPV4_ESP:
		return "ESP";
	case IAVF_FDIR_FLOW_IPV4_OTHER:
		return "Other";
	default:
		return NULL;
	}
}

/**
 * iavf_print_fdir_fltr
 * @adapter: adapter structure
 * @fltr: Flow Director filter to print
 *
 * Print the Flow Director filter
 **/
void iavf_print_fdir_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
{
	const char *proto = iavf_fdir_flow_proto_name(fltr->flow_type);

	if (!proto)
		return;

	switch (fltr->flow_type) {
	case IAVF_FDIR_FLOW_IPV4_TCP:
	case IAVF_FDIR_FLOW_IPV4_UDP:
	case IAVF_FDIR_FLOW_IPV4_SCTP:
		dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: dst_port %hu src_port %hu\n",
			 fltr->loc,
			 &fltr->ip_data.v4_addrs.dst_ip,
			 &fltr->ip_data.v4_addrs.src_ip,
			 proto,
			 ntohs(fltr->ip_data.dst_port),
			 ntohs(fltr->ip_data.src_port));
		break;
	case IAVF_FDIR_FLOW_IPV4_AH:
	case IAVF_FDIR_FLOW_IPV4_ESP:
		dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: SPI %u\n",
			 fltr->loc,
			 &fltr->ip_data.v4_addrs.dst_ip,
			 &fltr->ip_data.v4_addrs.src_ip,
			 proto,
			 ntohl(fltr->ip_data.spi));
		break;
	case IAVF_FDIR_FLOW_IPV4_OTHER:
		dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 proto: %u L4_bytes: 0x%x\n",
			 fltr->loc,
			 &fltr->ip_data.v4_addrs.dst_ip,
			 &fltr->ip_data.v4_addrs.src_ip,
			 fltr->ip_data.proto,
			 ntohl(fltr->ip_data.l4_header));
		break;
	default:
		break;
	}
}

/**
 * iavf_fdir_is_dup_fltr - test if filter is already in list
 * @adapter: pointer to the VF adapter structure
 * @fltr: Flow Director filter data structure
 *
 * Returns true if the filter is found in the list
 */
bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
{
	struct iavf_fdir_fltr *tmp;
	bool ret = false;

	list_for_each_entry(tmp, &adapter->fdir_list_head, list) {
		if (tmp->flow_type != fltr->flow_type)
			continue;

		if (!memcmp(&tmp->ip_data, &fltr->ip_data,
			    sizeof(fltr->ip_data))) {
			ret = true;
			break;
		}
	}

	return ret;
}

/**
 * iavf_find_fdir_fltr_by_loc - find filter with location
 * @adapter: pointer to the VF adapter structure
 * @loc: location to find.
 *
 * Returns pointer to Flow Director filter if found or null
 */
struct iavf_fdir_fltr *iavf_find_fdir_fltr_by_loc(struct iavf_adapter *adapter, u32 loc)
{
	struct iavf_fdir_fltr *rule;

	list_for_each_entry(rule, &adapter->fdir_list_head, list)
		if (rule->loc == loc)
			return rule;

	return NULL;
}

/**
 * iavf_fdir_list_add_fltr - add a new node to the flow director filter list
 * @adapter: pointer to the VF adapter structure
 * @fltr: filter node to add to structure
 */
void iavf_fdir_list_add_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
{
	struct iavf_fdir_fltr *rule, *parent = NULL;

	list_for_each_entry(rule, &adapter->fdir_list_head, list) {
		if (rule->loc >= fltr->loc)
			break;
		parent = rule;
	}

	if (parent)
		list_add(&fltr->list, &parent->list);
	else
		list_add(&fltr->list, &adapter->fdir_list_head);
}
+40 −0

File changed.

Preview size limit exceeded, changes collapsed.

+8 −0

File changed.

Preview size limit exceeded, changes collapsed.