Commit 2dce2390 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Tony Nguyen says:

====================
ice: iavf: add support for TC U32 filters on VFs

Ahmed Zaki says:

The Intel Ethernet 800 Series is designed with a pipeline that has
an on-chip programmable capability called Dynamic Device Personalization
(DDP). A DDP package is loaded by the driver during probe time. The DDP
package programs functionality in both the parser and switching blocks in
the pipeline, allowing dynamic support for new and existing protocols.
Once the pipeline is configured, the driver can identify the protocol and
apply any HW action in different stages, for example, direct packets to
desired hardware queues (flow director), queue groups or drop.

Patches 1-8 introduce a DDP package parser API that enables different
pipeline stages in the driver to learn the HW parser capabilities from
the DDP package that is downloaded to HW. The parser library takes raw
packet patterns and masks (in binary) indicating the packet protocol fields
to be matched and generates the final HW profiles that can be applied at
the required stage. With this API, raw flow filtering for FDIR or RSS
could be done on new protocols or headers without any driver or Kernel
updates (only need to update the DDP package). These patches were submitted
before [1] but were not accepted mainly due to lack of a user.

Patches 9-11 extend the virtchnl support to allow the VF to request raw
flow director filters. Upon receiving the raw FDIR filter request, the PF
driver allocates and runs a parser lib instance and generates the hardware
profile definitions required to program the FDIR stage. These were also
submitted before [2].

Finally, patches 12 and 13 add TC U32 filter support to the iavf driver.
Using the parser API, the ice driver runs the raw patterns sent by the
user and then adds a new profile to the FDIR stage associated with the VF's
VSI. Refer to examples in patch 13 commit message.

[1]: https://lore.kernel.org/netdev/20230904021455.3944605-1-junfeng.guo@intel.com/
[2]: https://lore.kernel.org/intel-wired-lan/20230818064703.154183-1-junfeng.guo@intel.com/

* '40GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  iavf: add support for offloading tc U32 cls filters
  iavf: refactor add/del FDIR filters
  ice: enable FDIR filters from raw binary patterns for VFs
  ice: add method to disable FDIR SWAP option
  virtchnl: support raw packet in protocol header
  ice: add API for parser profile initialization
  ice: add UDP tunnels support to the parser
  ice: support turning on/off the parser's double vlan mode
  ice: add parser execution main loop
  ice: add parser internal helper functions
  ice: add debugging functions for the parser sections
  ice: parse and init various DDP parser sections
  ice: add parser create and destroy skeleton
====================

Link: https://patch.msgid.link/20240813222249.3708070-1-anthony.l.nguyen@intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 795b1aa8 623122ac
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <net/udp.h>
#include <net/tc_act/tc_gact.h>
#include <net/tc_act/tc_mirred.h>
#include <net/tc_act/tc_skbedit.h>

#include "iavf_type.h"
#include <linux/avf/virtchnl.h>
@@ -393,6 +394,8 @@ struct iavf_adapter {
			     VIRTCHNL_VF_OFFLOAD_VLAN_V2)
#define CRC_OFFLOAD_ALLOWED(_a) ((_a)->vf_res->vf_cap_flags & \
				 VIRTCHNL_VF_OFFLOAD_CRC)
#define TC_U32_SUPPORT(_a) ((_a)->vf_res->vf_cap_flags & \
			    VIRTCHNL_VF_OFFLOAD_TC_U32)
#define VLAN_V2_FILTERING_ALLOWED(_a) \
	(VLAN_V2_ALLOWED((_a)) && \
	 ((_a)->vlan_v2_caps.filtering.filtering_support.outer || \
@@ -437,6 +440,7 @@ struct iavf_adapter {

#define IAVF_MAX_FDIR_FILTERS 128	/* max allowed Flow Director filters */
	u16 fdir_active_fltr;
	u16 raw_fdir_active_fltr;
	struct list_head fdir_list_head;
	spinlock_t fdir_fltr_lock;	/* protect the Flow Director filter list */

@@ -444,6 +448,32 @@ struct iavf_adapter {
	spinlock_t adv_rss_lock;	/* protect the RSS management list */
};

/* Must be called with fdir_fltr_lock lock held */
static inline bool iavf_fdir_max_reached(struct iavf_adapter *adapter)
{
	return adapter->fdir_active_fltr + adapter->raw_fdir_active_fltr >=
			IAVF_MAX_FDIR_FILTERS;
}

static inline void
iavf_inc_fdir_active_fltr(struct iavf_adapter *adapter,
			  struct iavf_fdir_fltr *fltr)
{
	if (iavf_is_raw_fdir(fltr))
		adapter->raw_fdir_active_fltr++;
	else
		adapter->fdir_active_fltr++;
}

static inline void
iavf_dec_fdir_active_fltr(struct iavf_adapter *adapter,
			  struct iavf_fdir_fltr *fltr)
{
	if (iavf_is_raw_fdir(fltr))
		adapter->raw_fdir_active_fltr--;
	else
		adapter->fdir_active_fltr--;
}

/* Ethtool Private Flags */

+9 −50
Original line number Diff line number Diff line
@@ -927,7 +927,7 @@ iavf_get_ethtool_fdir_entry(struct iavf_adapter *adapter,

	spin_lock_bh(&adapter->fdir_fltr_lock);

	rule = iavf_find_fdir_fltr_by_loc(adapter, fsp->location);
	rule = iavf_find_fdir_fltr(adapter, false, fsp->location);
	if (!rule) {
		ret = -EINVAL;
		goto release_lock;
@@ -1072,6 +1072,9 @@ iavf_get_fdir_fltr_ids(struct iavf_adapter *adapter, struct ethtool_rxnfc *cmd,
	spin_lock_bh(&adapter->fdir_fltr_lock);

	list_for_each_entry(fltr, &adapter->fdir_list_head, list) {
		if (iavf_is_raw_fdir(fltr))
			continue;

		if (cnt == cmd->rule_cnt) {
			val = -EMSGSIZE;
			goto release_lock;
@@ -1263,15 +1266,7 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
		return -EINVAL;

	spin_lock_bh(&adapter->fdir_fltr_lock);
	if (adapter->fdir_active_fltr >= IAVF_MAX_FDIR_FILTERS) {
		spin_unlock_bh(&adapter->fdir_fltr_lock);
		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;
	}

	if (iavf_find_fdir_fltr_by_loc(adapter, fsp->location)) {
	if (iavf_find_fdir_fltr(adapter, false, 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;
@@ -1291,23 +1286,10 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
	}

	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++;

	if (adapter->link_up)
		fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
	else
		fltr->state = IAVF_FDIR_FLTR_INACTIVE;
	spin_unlock_bh(&adapter->fdir_fltr_lock);
	if (!err)
		err = iavf_fdir_add_fltr(adapter, fltr);

	if (adapter->link_up)
		iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_FDIR_FILTER);
ret:
	if (err && fltr)
	if (err)
		kfree(fltr);

	mutex_unlock(&adapter->crit_lock);
@@ -1324,34 +1306,11 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
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 (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
		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;
		} else if (fltr->state == IAVF_FDIR_FLTR_INACTIVE) {
			list_del(&fltr->list);
			kfree(fltr);
			adapter->fdir_active_fltr--;
			fltr = NULL;
		} 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)
		iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_DEL_FDIR_FILTER);

	return err;
	return iavf_fdir_del_fltr(adapter, false, fsp->location);
}

/**
+81 −8
Original line number Diff line number Diff line
@@ -796,6 +796,9 @@ bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *

	spin_lock_bh(&adapter->fdir_fltr_lock);
	list_for_each_entry(tmp, &adapter->fdir_list_head, list) {
		if (iavf_is_raw_fdir(fltr))
			continue;

		if (tmp->flow_type != fltr->flow_type)
			continue;

@@ -815,33 +818,52 @@ bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *
}

/**
 * iavf_find_fdir_fltr_by_loc - find filter with location
 * iavf_find_fdir_fltr - find FDIR filter
 * @adapter: pointer to the VF adapter structure
 * @loc: location to find.
 * @is_raw: filter type, is raw (tc u32) or not (ethtool)
 * @data: data to ID the filter, type dependent
 *
 * Returns pointer to Flow Director filter if found or null
 * Returns: pointer to Flow Director filter if found or NULL. Lock must be held.
 */
struct iavf_fdir_fltr *iavf_find_fdir_fltr_by_loc(struct iavf_adapter *adapter, u32 loc)
struct iavf_fdir_fltr *iavf_find_fdir_fltr(struct iavf_adapter *adapter,
					   bool is_raw, u32 data)
{
	struct iavf_fdir_fltr *rule;

	list_for_each_entry(rule, &adapter->fdir_list_head, list)
		if (rule->loc == loc)
	list_for_each_entry(rule, &adapter->fdir_list_head, list) {
		if ((is_raw && rule->cls_u32_handle == data) ||
		    (!is_raw && rule->loc == data))
			return rule;
	}

	return NULL;
}

/**
 * iavf_fdir_list_add_fltr - add a new node to the flow director filter list
 * iavf_fdir_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
 *
 * Return: 0 on success or negative errno on failure.
 */
void iavf_fdir_list_add_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
int iavf_fdir_add_fltr(struct iavf_adapter *adapter,
		       struct iavf_fdir_fltr *fltr)
{
	struct iavf_fdir_fltr *rule, *parent = NULL;

	spin_lock_bh(&adapter->fdir_fltr_lock);
	if (iavf_fdir_max_reached(adapter)) {
		spin_unlock_bh(&adapter->fdir_fltr_lock);
		dev_err(&adapter->pdev->dev,
			"Unable to add Flow Director filter (limit (%u) reached)\n",
			IAVF_MAX_FDIR_FILTERS);
		return -ENOSPC;
	}

	list_for_each_entry(rule, &adapter->fdir_list_head, list) {
		if (iavf_is_raw_fdir(fltr))
			break;

		if (rule->loc >= fltr->loc)
			break;
		parent = rule;
@@ -851,4 +873,55 @@ void iavf_fdir_list_add_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr
		list_add(&fltr->list, &parent->list);
	else
		list_add(&fltr->list, &adapter->fdir_list_head);

	iavf_inc_fdir_active_fltr(adapter, fltr);

	if (adapter->link_up)
		fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
	else
		fltr->state = IAVF_FDIR_FLTR_INACTIVE;
	spin_unlock_bh(&adapter->fdir_fltr_lock);

	if (adapter->link_up)
		iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_FDIR_FILTER);

	return 0;
}

/**
 * iavf_fdir_del_fltr - delete a flow director filter from the list
 * @adapter: pointer to the VF adapter structure
 * @is_raw: filter type, is raw (tc u32) or not (ethtool)
 * @data: data to ID the filter, type dependent
 *
 * Return: 0 on success or negative errno on failure.
 */
int iavf_fdir_del_fltr(struct iavf_adapter *adapter, bool is_raw, u32 data)
{
	struct iavf_fdir_fltr *fltr = NULL;
	int err = 0;

	spin_lock_bh(&adapter->fdir_fltr_lock);
	fltr = iavf_find_fdir_fltr(adapter, is_raw, data);

	if (fltr) {
		if (fltr->state == IAVF_FDIR_FLTR_ACTIVE) {
			fltr->state = IAVF_FDIR_FLTR_DEL_REQUEST;
		} else if (fltr->state == IAVF_FDIR_FLTR_INACTIVE) {
			list_del(&fltr->list);
			iavf_dec_fdir_active_fltr(adapter, fltr);
			kfree(fltr);
			fltr = NULL;
		} else {
			err = -EBUSY;
		}
	} else if (adapter->fdir_active_fltr) {
		err = -EINVAL;
	}

	if (fltr && fltr->state == IAVF_FDIR_FLTR_DEL_REQUEST)
		iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_DEL_FDIR_FILTER);

	spin_unlock_bh(&adapter->fdir_fltr_lock);
	return err;
}
+11 −2
Original line number Diff line number Diff line
@@ -117,17 +117,26 @@ struct iavf_fdir_fltr {

	u32 flow_id;

	u32 cls_u32_handle; /* for FDIR added via tc u32 */
	u32 loc;	/* Rule location inside the flow table */
	u32 q_index;

	struct virtchnl_fdir_add vc_add_msg;
};

static inline bool iavf_is_raw_fdir(struct iavf_fdir_fltr *fltr)
{
	return !fltr->vc_add_msg.rule_cfg.proto_hdrs.count;
}

int iavf_validate_fdir_fltr_masks(struct iavf_adapter *adapter,
				  struct iavf_fdir_fltr *fltr);
int iavf_fill_fdir_add_msg(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
void iavf_print_fdir_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
void iavf_fdir_list_add_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
struct iavf_fdir_fltr *iavf_find_fdir_fltr_by_loc(struct iavf_adapter *adapter, u32 loc);
int iavf_fdir_add_fltr(struct iavf_adapter *adapter,
		       struct iavf_fdir_fltr *fltr);
int iavf_fdir_del_fltr(struct iavf_adapter *adapter, bool is_raw, u32 data);
struct iavf_fdir_fltr *iavf_find_fdir_fltr(struct iavf_adapter *adapter,
					   bool is_raw, u32 data);
#endif /* _IAVF_FDIR_H_ */
+156 −4
Original line number Diff line number Diff line
@@ -4013,7 +4013,7 @@ static int iavf_delete_clsflower(struct iavf_adapter *adapter,

/**
 * iavf_setup_tc_cls_flower - flower classifier offloads
 * @adapter: board private structure
 * @adapter: pointer to iavf adapter structure
 * @cls_flower: pointer to flow_cls_offload struct with flow info
 */
static int iavf_setup_tc_cls_flower(struct iavf_adapter *adapter,
@@ -4031,6 +4031,154 @@ static int iavf_setup_tc_cls_flower(struct iavf_adapter *adapter,
	}
}

/**
 * iavf_add_cls_u32 - Add U32 classifier offloads
 * @adapter: pointer to iavf adapter structure
 * @cls_u32: pointer to tc_cls_u32_offload struct with flow info
 *
 * Return: 0 on success or negative errno on failure.
 */
static int iavf_add_cls_u32(struct iavf_adapter *adapter,
			    struct tc_cls_u32_offload *cls_u32)
{
	struct netlink_ext_ack *extack = cls_u32->common.extack;
	struct virtchnl_fdir_rule *rule_cfg;
	struct virtchnl_filter_action *vact;
	struct virtchnl_proto_hdrs *hdrs;
	struct ethhdr *spec_h, *mask_h;
	const struct tc_action *act;
	struct iavf_fdir_fltr *fltr;
	struct tcf_exts *exts;
	unsigned int q_index;
	int i, status = 0;
	int off_base = 0;

	if (cls_u32->knode.link_handle) {
		NL_SET_ERR_MSG_MOD(extack, "Linking not supported");
		return -EOPNOTSUPP;
	}

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

	rule_cfg = &fltr->vc_add_msg.rule_cfg;
	hdrs = &rule_cfg->proto_hdrs;
	hdrs->count = 0;

	/* The parser lib at the PF expects the packet starting with MAC hdr */
	switch (ntohs(cls_u32->common.protocol)) {
	case ETH_P_802_3:
		break;
	case ETH_P_IP:
		spec_h = (struct ethhdr *)hdrs->raw.spec;
		mask_h = (struct ethhdr *)hdrs->raw.mask;
		spec_h->h_proto = htons(ETH_P_IP);
		mask_h->h_proto = htons(0xFFFF);
		off_base += ETH_HLEN;
		break;
	default:
		NL_SET_ERR_MSG_MOD(extack, "Only 802_3 and ip filter protocols are supported");
		status = -EOPNOTSUPP;
		goto free_alloc;
	}

	for (i = 0; i < cls_u32->knode.sel->nkeys; i++) {
		__be32 val, mask;
		int off;

		off = off_base + cls_u32->knode.sel->keys[i].off;
		val = cls_u32->knode.sel->keys[i].val;
		mask = cls_u32->knode.sel->keys[i].mask;

		if (off >= sizeof(hdrs->raw.spec)) {
			NL_SET_ERR_MSG_MOD(extack, "Input exceeds maximum allowed.");
			status = -EINVAL;
			goto free_alloc;
		}

		memcpy(&hdrs->raw.spec[off], &val, sizeof(val));
		memcpy(&hdrs->raw.mask[off], &mask, sizeof(mask));
		hdrs->raw.pkt_len = off + sizeof(val);
	}

	/* Only one action is allowed */
	rule_cfg->action_set.count = 1;
	vact = &rule_cfg->action_set.actions[0];
	exts = cls_u32->knode.exts;

	tcf_exts_for_each_action(i, act, exts) {
		/* FDIR queue */
		if (is_tcf_skbedit_rx_queue_mapping(act)) {
			q_index = tcf_skbedit_rx_queue_mapping(act);
			if (q_index >= adapter->num_active_queues) {
				status = -EINVAL;
				goto free_alloc;
			}

			vact->type = VIRTCHNL_ACTION_QUEUE;
			vact->act_conf.queue.index = q_index;
			break;
		}

		/* Drop */
		if (is_tcf_gact_shot(act)) {
			vact->type = VIRTCHNL_ACTION_DROP;
			break;
		}

		/* Unsupported action */
		NL_SET_ERR_MSG_MOD(extack, "Unsupported action.");
		status = -EOPNOTSUPP;
		goto free_alloc;
	}

	fltr->vc_add_msg.vsi_id = adapter->vsi.id;
	fltr->cls_u32_handle = cls_u32->knode.handle;
	return iavf_fdir_add_fltr(adapter, fltr);

free_alloc:
	kfree(fltr);
	return status;
}

/**
 * iavf_del_cls_u32 - Delete U32 classifier offloads
 * @adapter: pointer to iavf adapter structure
 * @cls_u32: pointer to tc_cls_u32_offload struct with flow info
 *
 * Return: 0 on success or negative errno on failure.
 */
static int iavf_del_cls_u32(struct iavf_adapter *adapter,
			    struct tc_cls_u32_offload *cls_u32)
{
	return iavf_fdir_del_fltr(adapter, true, cls_u32->knode.handle);
}

/**
 * iavf_setup_tc_cls_u32 - U32 filter offloads
 * @adapter: pointer to iavf adapter structure
 * @cls_u32: pointer to tc_cls_u32_offload struct with flow info
 *
 * Return: 0 on success or negative errno on failure.
 */
static int iavf_setup_tc_cls_u32(struct iavf_adapter *adapter,
				 struct tc_cls_u32_offload *cls_u32)
{
	if (!TC_U32_SUPPORT(adapter) || !FDIR_FLTR_SUPPORT(adapter))
		return -EOPNOTSUPP;

	switch (cls_u32->command) {
	case TC_CLSU32_NEW_KNODE:
	case TC_CLSU32_REPLACE_KNODE:
		return iavf_add_cls_u32(adapter, cls_u32);
	case TC_CLSU32_DELETE_KNODE:
		return iavf_del_cls_u32(adapter, cls_u32);
	default:
		return -EOPNOTSUPP;
	}
}

/**
 * iavf_setup_tc_block_cb - block callback for tc
 * @type: type of offload
@@ -4050,6 +4198,8 @@ static int iavf_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
	switch (type) {
	case TC_SETUP_CLSFLOWER:
		return iavf_setup_tc_cls_flower(cb_priv, type_data);
	case TC_SETUP_CLSU32:
		return iavf_setup_tc_cls_u32(cb_priv, type_data);
	default:
		return -EOPNOTSUPP;
	}
@@ -4332,8 +4482,8 @@ static void iavf_disable_fdir(struct iavf_adapter *adapter)
		    fdir->state == IAVF_FDIR_FLTR_INACTIVE) {
			/* Delete filters not registered in PF */
			list_del(&fdir->list);
			iavf_dec_fdir_active_fltr(adapter, fdir);
			kfree(fdir);
			adapter->fdir_active_fltr--;
		} else if (fdir->state == IAVF_FDIR_FLTR_ADD_PENDING ||
			   fdir->state == IAVF_FDIR_FLTR_DIS_REQUEST ||
			   fdir->state == IAVF_FDIR_FLTR_ACTIVE) {
@@ -4843,9 +4993,11 @@ int iavf_process_config(struct iavf_adapter *adapter)
	/* get HW VLAN features that can be toggled */
	hw_vlan_features = iavf_get_netdev_vlan_hw_features(adapter);

	/* Enable cloud filter if ADQ is supported */
	if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ)
	/* Enable HW TC offload if ADQ or tc U32 is supported */
	if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ ||
	    TC_U32_SUPPORT(adapter))
		hw_features |= NETIF_F_HW_TC;

	if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_USO)
		hw_features |= NETIF_F_GSO_UDP_L4;

Loading