diff mbox series

[net-next,17/19] iecm: implement cloud filters

Message ID 20220128001009.721392-18-alan.brady@intel.com
State Changes Requested
Headers show
Series [net-next,01/19] virtchnl: Add new virtchnl2 ops | expand

Commit Message

Alan Brady Jan. 28, 2022, 12:10 a.m. UTC
This gives iecm the ability to deal with cloud filters and other traffic
classes.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 900 +++++++++++++++++-
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  68 ++
 drivers/net/ethernet/intel/include/iecm.h     |  25 +
 3 files changed, 992 insertions(+), 1 deletion(-)

Comments

Alexander Lobakin Jan. 28, 2022, 7:38 p.m. UTC | #1
From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:07 -0800

> This gives iecm the ability to deal with cloud filters and other traffic
> classes.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 900 +++++++++++++++++-
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  68 ++
>  drivers/net/ethernet/intel/include/iecm.h     |  25 +
>  3 files changed, 992 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index 35c0cbc42ebe..d11413cb438c 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -43,9 +43,16 @@ static int iecm_get_vport_index(struct iecm_adapter *adapter,
>   */
>  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
>  {
> +	struct iecm_channel_config *ch_config;
>  	bool ena;
>  
>  	switch (feature) {
> +	case NETIF_F_HW_TC:
> +		ch_config = &vport->adapter->config_data.ch_config;
> +		ena = (vport->netdev->features & feature) &&
> +			(ch_config->num_tc > IECM_START_CHNL_TC) &&
> +			(ch_config->tc_running);
> +		break;
>  	default:
>  		ena = vport->netdev->features & feature;
>  		break;
> @@ -53,6 +60,23 @@ bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
>  	return ena;
>  }
>  
> +/**
> + * iecm_is_adq_v2_ena - Determine whether ADQ V2 is enabled
> + * @vport: virtual port struct
> + *
> + * This function returns true based on negotiated capability ADQ_V2
> + * if set and ADQ enabled
> + */
> +static bool iecm_is_adq_v2_ena(struct iecm_vport *vport)
> +{
> +	/* iecm_is_feature_ena tells if the netdev flag is set and adq is
> +	 * enabled
> +	 */
> +	return (iecm_is_feature_ena(vport, NETIF_F_HW_TC) &&
> +		iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> +				VIRTCHNL2_CAP_ADQ));

The outermost braces are redundant.

> +}
> +
>  /**
>   * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
>   * @adapter: pointer to adapter
> @@ -946,6 +970,28 @@ static int iecm_get_free_slot(void *array, int size, int curr)
>  	return next;
>  }
>  
> +/**
> + * iecm_remove_cloud_filters - Remove all cloud filters
> + * @vport: vport structure
> + */
> +static void iecm_remove_cloud_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +	if (!list_empty(&cf_config->cloud_filter_list)) {
> +		struct iecm_cloud_filter *cf;
> +
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> +			cf->remove = true;
> +		}

One-liner, braces are redundant.

> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		iecm_send_add_del_cloud_filter_msg(vport, false);
> +	}

	if (list_empty())
		return;

-1 level.

> +}
> +
>  /**
>   * iecm_remove_vlan_filters - Remove all vlan filters
>   * @vport: vport structure
> @@ -1044,8 +1090,14 @@ static void iecm_vport_stop(struct iecm_vport *vport)
>  	if (test_and_clear_bit(__IECM_DEL_QUEUES,
>  			       vport->adapter->flags))
>  		iecm_send_delete_queues_msg(vport);
> -	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
> +	/* In function reset/rmmod path we call unregister_netdev which
> +	 * internally calls delete cloud filters. We remove cloud filters only
> +	 * when the interface goes down
> +	 */
> +	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
> +		iecm_remove_cloud_filters(vport);
>  		iecm_remove_vlan_filters(vport);
> +	}
>  
>  	iecm_remove_fdir_filters(vport);
>  
> @@ -1258,6 +1310,28 @@ static void iecm_restore_vlans(struct iecm_vport *vport)
>  		iecm_set_all_vlans(vport);
>  }
>  
> +/**
> + * iecm_restore_cloud_filters - Restore cloud filters
> + * @vport: vport structure
> + */
> +static void iecm_restore_cloud_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +	if (!list_empty(&cf_config->cloud_filter_list)) {
> +		struct iecm_cloud_filter *cf;
> +
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> +			cf->add = true;
> +		}

Same here for braces.

> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		iecm_send_add_del_cloud_filter_msg(vport, true);
> +	}

Same here for reducing indent level.

> +}
> +
>  /**
>   * iecm_restore_fdir_filters - Restore all Flow Director filters
>   * @vport: vport structure
> @@ -1302,6 +1376,10 @@ static void iecm_restore_features(struct iecm_vport *vport)
>  			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
>  	}
>  
> +	/* Restore cloud filters if ADQ is enabled */
> +	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
> +		iecm_restore_cloud_filters(vport);
> +
>  	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
>  		iecm_restore_fdir_filters(vport);
>  }
> @@ -2088,6 +2166,8 @@ int iecm_probe(struct pci_dev *pdev,
>  	spin_lock_init(&adapter->vlan_list_lock);
>  	spin_lock_init(&adapter->adv_rss_list_lock);
>  	spin_lock_init(&adapter->fdir_fltr_list_lock);
> +	INIT_LIST_HEAD(&adapter->config_data.cf_config.cloud_filter_list);
> +	INIT_LIST_HEAD(&adapter->config_data.cf_config.block_cb_list);
>  	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
>  	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
>  	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> @@ -2389,6 +2469,810 @@ static int iecm_offload_txtime(struct iecm_vport *vport,
>  	return -EOPNOTSUPP;
>  }
>  
> +/**
> + * iecm_is_vlan_tc_filter_allowed - allowed to add tc-filter using VLAN
> + * @vport: vport structure
> + * @vlan: VLAN to verify
> + *
> + * Using specified "vlan" ID, there must be active VLAN filter in VF's
> + * MAC-VLAN filter list.
> + */
> +static bool
> +iecm_is_vlan_tc_filter_allowed(struct iecm_vport *vport,
> +			       struct iecm_vlan *vlan)
> +{
> +	struct iecm_vlan_filter *f;
> +	bool allowed;
> +
> +	spin_lock_bh(&vport->adapter->vlan_list_lock);
> +	f = iecm_find_vlan(vport, vlan);
> +	allowed = (f && !f->add && !f->remove);

Redundant braces here.

> +	spin_unlock_bh(&vport->adapter->vlan_list_lock);
> +	return allowed;
> +}
> +
> +/**
> + * iecm_is_mac_tc_filter_allowed - allowed to add tc-filter using MAC addr
> + * @vport: vport structure
> + * @macaddr: MAC address
> + *
> + * Using specified MAC address, there must be active MAC filter in
> + * MAC filter list.
> + */
> +static bool
> +iecm_is_mac_tc_filter_allowed(struct iecm_vport *vport, const u8 *macaddr)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_mac_filter *f;
> +	bool allowed;
> +
> +	spin_lock_bh(&adapter->mac_filter_list_lock);
> +	f = iecm_find_mac_filter(vport, macaddr);
> +	allowed = (f && !f->add && !f->remove);

Same here.

> +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> +	return allowed;
> +}
> +
> +/**
> + * iecm_parse_keyid - Parse keyid
> + * @rule: Flow rule structure
> + * @field_flags: Cloud filter flags
> + */
> +static void  iecm_parse_keyid(struct flow_rule *rule, u8 *field_flags)
> +{
> +	struct flow_match_enc_keyid match;
> +
> +	flow_rule_match_enc_keyid(rule, &match);
> +
> +	if (match.mask->keyid != 0)
> +		*field_flags |= IECM_CLOUD_FIELD_TEN_ID;
> +}
> +
> +/**
> + * iecm_parse_flow_type - Parse flow type based on L2 and L3 protocols
> + * @vport: vport structure
> + * @rule: rule from user
> + * @cf: Structure for the virtchnl filter
> + * @filter: Structure for the cloud filter
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_flow_type(struct iecm_vport *vport,
> +		     struct flow_rule *rule, struct virtchnl_filter *cf,
> +		     struct iecm_cloud_filter *filter)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	enum virtchnl_flow_type flow_type;
> +	struct flow_match_basic match;
> +	u16 n_proto_mask = 0;
> +	u16 n_proto_key = 0;
> +	u16 n_proto = 0;
> +	u8 ip_proto = 0;
> +
> +	flow_rule_match_basic(rule, &match);
> +
> +	n_proto_key = ntohs(match.key->n_proto);
> +	n_proto_mask = ntohs(match.mask->n_proto);
> +
> +	if (n_proto_key == ETH_P_ALL) {
> +		n_proto_key = 0;
> +		n_proto_mask = 0;
> +	}

	n_proto_key = ntohs();
	if (n_proto_key != ETH_P_ALL)
		n_proto_mask = ntohs();

> +	n_proto = n_proto_key & n_proto_mask;
> +	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
> +		return -EINVAL;
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
> +		if (match.key->ip_proto != IPPROTO_TCP &&
> +		    match.key->ip_proto != IPPROTO_UDP) {
> +			dev_err(&adapter->pdev->dev,
> +				"Only TCP or UDP transport is supported\n");
> +			return -EINVAL;
> +		}
> +	} else if (match.key->ip_proto != IPPROTO_TCP) {
> +		dev_err(&adapter->pdev->dev,
> +			"Only TCP transport is supported\n");
> +		return -EINVAL;
> +	}
> +	ip_proto = match.key->ip_proto;
> +
> +	/* determine VIRTCHNL flow_type based on L3 and L4 protocol */
> +	if (n_proto == ETH_P_IP)
> +		flow_type = (ip_proto == IPPROTO_TCP) ?
> +			     VIRTCHNL_TCP_V4_FLOW :
> +			     VIRTCHNL_UDP_V4_FLOW;
> +	else
> +		flow_type = (ip_proto == IPPROTO_TCP) ?
> +			     VIRTCHNL_TCP_V6_FLOW :
> +			     VIRTCHNL_UDP_V6_FLOW;
> +	cf->flow_type = flow_type;
> +	filter->f.flow_type = flow_type;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_ether_header - Parse ethernet header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_ether_header(struct iecm_vport *vport, u8 *field_flags,
> +			struct virtchnl_l4_spec *d_spec,
> +			struct virtchnl_l4_spec *m_spec,
> +			struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_eth_addrs match;
> +	bool adv_adq_ena;
> +
> +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				      VIRTCHNL2_CAP_ADQ);
> +
> +	flow_rule_match_eth_addrs(rule, &match);
> +
> +	/* use is_broadcast and is_zero to check for all 0xf or 0 */
> +	if (!is_zero_ether_addr(match.mask->dst)) {
> +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->dst)) {
> +			*field_flags |= IECM_CLOUD_FIELD_OMAC;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ether dest mask %pM\n",
> +				match.mask->dst);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (!is_zero_ether_addr(match.mask->src)) {
> +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->src)) {
> +			*field_flags |= IECM_CLOUD_FIELD_IMAC;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ether src mask %pM\n",
> +				match.mask->src);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (!is_zero_ether_addr(match.key->dst)) {
> +		if (!iecm_is_mac_tc_filter_allowed(adapter->vports[0],
> +						   match.key->dst)) {
> +			dev_err(&adapter->pdev->dev,
> +				"Dest MAC %pM doesn't belong to this device\n",
> +				match.key->dst);
> +			return -EINVAL;
> +		}
> +
> +		if (is_valid_ether_addr(match.key->dst) ||
> +		    is_multicast_ether_addr(match.key->dst)) {
> +			/* set the mask if a valid dst_mac address */
> +			if (adv_adq_ena)
> +				ether_addr_copy(m_spec->dst_mac,
> +						match.mask->dst);
> +			else
> +				eth_broadcast_addr(m_spec->dst_mac);
> +			ether_addr_copy(d_spec->dst_mac,
> +					match.key->dst);
> +		}
> +	}
> +
> +	if (!is_zero_ether_addr(match.key->src))
> +		if (is_valid_ether_addr(match.key->src) ||
> +		    is_multicast_ether_addr(match.key->src)) {
> +			/* set the mask if a valid src_mac address */
> +			if (adv_adq_ena) {
> +				ether_addr_copy(m_spec->src_mac,
> +						match.mask->src);
> +			} else {
> +				eth_broadcast_addr(m_spec->src_mac);
> +			}
> +			ether_addr_copy(d_spec->src_mac,
> +					match.key->src);
> +		}

All previous ifs had braces, and it way okay since we have multiple
nested if-elses, but here we don't have them.
Please either add them here or remove all the way above to keep the
code style consistent.

> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_vlan_header - Parse vlan header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_vlan_header(struct iecm_vport *vport, u8 *field_flags,
> +		       struct virtchnl_l4_spec *d_spec,
> +		       struct virtchnl_l4_spec *m_spec,
> +		       struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_vlan match;
> +
> +	flow_rule_match_vlan(rule, &match);
> +	if (match.mask->vlan_id) {
> +		u16 vid = match.key->vlan_id & VLAN_VID_MASK;
> +		struct iecm_vlan vlan;
> +
> +		vlan = IECM_VLAN(vid, ETH_P_8021Q);
> +
> +		if (match.mask->vlan_id != VLAN_VID_MASK) {
> +			dev_err(&adapter->pdev->dev, "Bad vlan mask %u\n",
> +				match.mask->vlan_id);
> +			return -EINVAL;
> +		}
> +		if (!iecm_is_vlan_tc_filter_allowed(vport, &vlan)) {
> +			dev_err(&adapter->pdev->dev,
> +				"VLAN %u doesn't belong to this VF\n",
> +				vid);
> +			return -EINVAL;
> +		}
> +		*field_flags |= IECM_CLOUD_FIELD_IVLAN;
> +		m_spec->vlan_id = cpu_to_be16(match.mask->vlan_id);
> +		d_spec->vlan_id = cpu_to_be16(match.key->vlan_id);
> +	}

	if (!vlan_id)
		return 0;

-1 level.

> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_ipv4_header - Parse ipv4 header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_ipv4_header(struct iecm_vport *vport, u8 *field_flags,
> +		       struct virtchnl_l4_spec *d_spec,
> +		       struct virtchnl_l4_spec *m_spec,
> +		       struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_ipv4_addrs match;
> +	bool adv_adq_ena;
> +
> +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				      VIRTCHNL2_CAP_ADQ);
> +
> +	flow_rule_match_ipv4_addrs(rule, &match);
> +
> +	if (*field_flags & IECM_CLOUD_FIELD_TEN_ID) {
> +		dev_info(&adapter->pdev->dev,
> +			 "Tenant id not allowed for ip filter\n");
> +		return -EINVAL;
> +	}
> +
> +	if (match.mask->dst) {
> +		if (adv_adq_ena || match.mask->dst == cpu_to_be32(0xffffffff)) {
> +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ip dst mask 0x%08x\n",
> +				be32_to_cpu(match.mask->dst));
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (match.mask->src) {
> +		if (adv_adq_ena || match.mask->src == cpu_to_be32(0xffffffff)) {
> +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
> +				be32_to_cpu(match.mask->dst));
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (match.key->dst) {
> +		if (adv_adq_ena)
> +			m_spec->dst_ip[0] = match.mask->dst;
> +		else
> +			m_spec->dst_ip[0] = cpu_to_be32(0xffffffff);
> +		d_spec->dst_ip[0] = match.key->dst;
> +	}
> +
> +	if (match.key->src) {
> +		if (adv_adq_ena)
> +			m_spec->src_ip[0] = match.mask->src;
> +		else
> +			m_spec->src_ip[0] = cpu_to_be32(0xffffffff);
> +		d_spec->src_ip[0] = match.key->src;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_ipv6_header - Parse ipv6 header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_ipv6_header(struct iecm_vport *vport, u8 *field_flags,
> +		       struct virtchnl_l4_spec *d_spec,
> +		       struct virtchnl_l4_spec *m_spec,
> +		       struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_ipv6_addrs match;
> +	int i;
> +
> +	flow_rule_match_ipv6_addrs(rule, &match);
> +
> +	/* validate mask, make sure it is not IPV6_ADDR_ANY */
> +	if (ipv6_addr_any(&match.mask->dst)) {
> +		dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
> +			IPV6_ADDR_ANY);
> +		return -EINVAL;
> +	}
> +
> +	/* src and dest IPv6 address should not be LOOPBACK
> +	 * (0:0:0:0:0:0:0:1) which can be represented as ::1
> +	 */
> +	if (ipv6_addr_loopback(&match.key->dst) ||
> +	    ipv6_addr_loopback(&match.key->src)) {
> +		dev_err(&adapter->pdev->dev,
> +			"ipv6 addr should not be loopback\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!ipv6_addr_any(&match.mask->dst) ||
> +	    !ipv6_addr_any(&match.mask->src))
> +		*field_flags |= IECM_CLOUD_FIELD_IIP;
> +
> +	/* copy dest IPv6 mask and address */
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
> +		memcpy(&m_spec->dst_ip, &match.mask->dst.s6_addr32,
> +		       sizeof(m_spec->dst_ip));
> +	} else {
> +		for (i = 0; i < 4; i++)
> +			m_spec->dst_ip[i] = cpu_to_be32(0xffffffff);
> +	}

One-liners, no braces needed for both `if` and `else`.

> +	memcpy(&d_spec->dst_ip, &match.key->dst.s6_addr32,
> +	       sizeof(d_spec->dst_ip));
> +
> +	/* copy source IPv6 mask and address */
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
> +		memcpy(&m_spec->src_ip, &match.mask->src.s6_addr32,
> +		       sizeof(m_spec->src_ip));
> +	} else {
> +		for (i = 0; i < 4; i++)
> +			m_spec->src_ip[i] = cpu_to_be32(0xffffffff);
> +	}

Same here.

> +	memcpy(&d_spec->src_ip, &match.key->src.s6_addr32,
> +	       sizeof(d_spec->src_ip));
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_l4_header - Parse l4 header fields
> + * @vport: vport structure
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_l4_header(struct iecm_vport *vport,
> +		     struct virtchnl_l4_spec *d_spec,
> +		     struct virtchnl_l4_spec *m_spec,
> +		     struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_ports match;
> +
> +	flow_rule_match_ports(rule, &match);
> +
> +	if (match.key->dst) {
> +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    match.mask->dst == cpu_to_be16(0xffff)) {
> +			m_spec->dst_port = match.mask->dst;
> +			d_spec->dst_port = match.key->dst;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad dst port mask %u\n",
> +				be16_to_cpu(match.mask->dst));
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (match.key->src) {
> +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    match.mask->src == cpu_to_be16(0xffff)) {
> +			m_spec->src_port = match.mask->src;
> +			d_spec->src_port = match.key->src;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad src port mask %u\n",
> +				be16_to_cpu(match.mask->src));
> +			return -EINVAL;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_cls_flower - Parse tc flower filters provided by kernel
> + * @vport: vport structure
> + * @f: pointer to struct flow_cls_offload
> + * @filter: pointer to cloud filter structure
> + */
> +static int iecm_parse_cls_flower(struct iecm_vport *vport,
> +				 struct flow_cls_offload *f,
> +				 struct iecm_cloud_filter *filter)
> +{
> +	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl_l4_spec *d_spec, *m_spec;
> +	struct virtchnl_filter *cf = &filter->f;
> +	struct flow_dissector *dissector;
> +	u8 field_flags = 0;
> +	u16 addr_type = 0;
> +	int err;
> +
> +	dissector = rule->match.dissector;
> +	if (dissector->used_keys &
> +	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
> +	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
> +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
> +	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
> +	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
> +		dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n",
> +			dissector->used_keys);
> +		return -EOPNOTSUPP;
> +	}
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
> +		iecm_parse_keyid(rule, &field_flags);
> +
> +	/* even though following code refers as "tcp_sec", it is not
> +	 * just for TCP but a generic struct representing
> +	 * L2, L3 + L4 fields if specified
> +	 */
> +	m_spec = &cf->mask.tcp_spec;
> +	d_spec = &cf->data.tcp_spec;
> +
> +	/* determine flow type, TCP/UDP_V4[6]_FLOW based on
> +	 * L2 proto (aka ETH proto) and L3 proto (aka IP_PROTO)
> +	 */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
> +		err = iecm_parse_flow_type(vport, rule, cf, filter);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process Ethernet header fields */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
> +		err = iecm_parse_ether_header(vport, &field_flags,
> +					      d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process VLAN header for single VLAN (type could be S/C-tag) */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
> +		err = iecm_parse_vlan_header(vport, &field_flags,
> +					     d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
> +		struct flow_match_control match;
> +
> +		flow_rule_match_control(rule, &match);
> +		addr_type = match.key->addr_type;
> +	}
> +
> +	/* process IPv4 header */
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
> +		err = iecm_parse_ipv4_header(vport, &field_flags,
> +					     d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process IPv6 header */
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
> +		err = iecm_parse_ipv6_header(vport, &field_flags,
> +					     d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process L4 header, supported L4 protocols are TCP and UDP */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
> +		err = iecm_parse_l4_header(vport, d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +	cf->field_flags = field_flags;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_handle_tclass - Forward to a traffic class on the device
> + * @vport: vport structure
> + * @tc: traffic class index on the device
> + * @filter: pointer to cloud filter structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_handle_tclass(struct iecm_vport *vport, int tc,
> +			      struct iecm_cloud_filter *filter)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (tc == 0)
> +		return 0;
> +	if ((!iecm_is_adq_v2_ena(vport)) &&
> +	    !filter->f.data.tcp_spec.dst_port) {
> +		dev_err(&adapter->pdev->dev,
> +			"Specify destination port to redirect to traffic class other than TC0\n");
> +		return -EINVAL;
> +	}
> +	/* redirect to a traffic class on the same device */
> +	filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
> +	filter->f.action_meta = tc;
> +	return 0;
> +}
> +
> +/* iecm_find_cf - Find the cloud filter in the list
> + * @vport: vport structure
> + * @cookie: filter specific cookie
> + *
> + * Returns pointer to the filter object or NULL. Must be called while holding
> + * cloud_filter_list_lock.
> + */
> +static struct iecm_cloud_filter *iecm_find_cf(struct iecm_vport *vport,
> +					      unsigned long *cookie)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter *filter = NULL;
> +
> +	if (!cookie)
> +		return NULL;
> +
> +	list_for_each_entry(filter,
> +			    &adapter->config_data.cf_config.cloud_filter_list,
> +			    list) {
> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
> +			return filter;
> +	}

Redundant braces round single statement.

> +	return NULL;
> +}
> +
> +/**
> + * iecm_configure_clsflower - Add tc flower filters
> + * @vport: vport structure
> + * @cls_flower: Pointer to struct flow_cls_offload
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_configure_clsflower(struct iecm_vport *vport,
> +				    struct flow_cls_offload *cls_flower)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_user_config_data *config_data;
> +	struct iecm_cloud_filter *filter = NULL;
> +	int err;
> +	int tc;
> +
> +	config_data = &adapter->config_data;
> +	tc = tc_classid_to_hwtc(vport->netdev, cls_flower->classid);
> +	if (tc < 0) {
> +		dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
> +		return -EINVAL;
> +	}
> +
> +#define IECM_MAX_CLOUD_ADQ_FILTERS	128
> +
> +	if (config_data->cf_config.num_cloud_filters >=
> +						IECM_MAX_CLOUD_ADQ_FILTERS) {
> +		dev_err(&adapter->pdev->dev,
> +			"Unable to add filter (action is forward to TC), reached max allowed filters (%u)\n",
> +			IECM_MAX_CLOUD_ADQ_FILTERS);
> +		return -ENOSPC;
> +	}
> +
> +	/* bail out here if filter already exists */
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> +	if (filter) {
> +		filter->remove = false;
> +		dev_err(&adapter->pdev->dev, "Failed to add TC Flower filter, it already exists\n");
> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		return -EEXIST;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> +	if (!filter)
> +		return -ENOMEM;
> +
> +	filter->cookie = cls_flower->cookie;
> +
> +	/* set the mask to all zeroes to begin with */
> +	memset(&filter->f.mask.tcp_spec, 0, sizeof(struct virtchnl_l4_spec));
> +
> +	/* start out with flow type and eth type IPv4 to begin with */
> +	filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
> +	err = iecm_parse_cls_flower(vport, cls_flower, filter);
> +	if (err)
> +		goto error;
> +
> +	err = iecm_handle_tclass(vport, tc, filter);
> +	if (err)
> +		goto error;
> +
> +	/* add filter to the list */
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	list_add_tail(&filter->list, &config_data->cf_config.cloud_filter_list);
> +	filter->add = true;
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +	err = iecm_send_add_del_cloud_filter_msg(vport, true);
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	/* We have to find it again in case another thread has already
> +	 * deleted and kfreed it.
> +	 */
> +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> +	if (filter && err) {
> +		list_del(&filter->list);
> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		goto error;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +	config_data->cf_config.num_cloud_filters++;
> +error:
> +	if (err)
> +		kfree(filter);
> +	return err;
> +}
> +
> +/**
> + * iecm_delete_clsflower - Remove tc flower filters
> + * @vport: vport structure
> + * @cls_flower: Pointer to struct flow_cls_offload
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_delete_clsflower(struct iecm_vport *vport,
> +				 struct flow_cls_offload *cls_flower)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter *filter = NULL;
> +	int err = 0;
> +
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> +	if (filter) {
> +		filter->remove = true;
> +		adapter->config_data.cf_config.num_cloud_filters--;
> +	} else if (adapter->config_data.cf_config.num_cloud_filters) {
> +		/* "num_cloud_filters" can become zero if egress qdisc is
> +		 * detached as per design, driver deletes related filters
> +		 * when qdisc is detached to avoid stale filters, hence
> +		 * num_cloud_filters can become zero. But since netdev
> +		 * layer doesn't know that filters are deleted by driver
> +		 * implictly when egress qdisc is deleted, it sees filters
> +		 * being present and "in_hw". User can request delete
> +		 * of specific filter of detach ingress qdisc - in either of
> +		 * those operation, filter(s) won't be found in driver cache,
> +		 * hence instead of returning, let this function return SUCCESS
> +		 * Returning of err as -EINVAL is only applicable when
> +		 * unable to find filter and num_cloud_filters is non-zero
> +		 */
> +		err = -EINVAL;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +	if (filter) {
> +		err = iecm_send_add_del_cloud_filter_msg(vport, false);
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		/* It can happen that asynchronously the filter was already
> +		 * deleted from the list. Make sure it's still there and marked
> +		 * for remove under spinlock before actually trying to delete
> +		 * from list.
> +		 */
> +		filter = iecm_find_cf(vport, &cls_flower->cookie);
> +		if (filter) {
> +			list_del(&filter->list);
> +			kfree(filter);
> +		}
> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +	}

	if (!filter)
		return err;

	err = ...

-1 level.

> +	return err;
> +}
> +
> +/**
> + * iecm_setup_tc_cls_flower - flower classifier offloads
> + * @vport: vport structure
> + * @cls_flower: pointer to struct flow_cls_offload
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_setup_tc_cls_flower(struct iecm_vport *vport,
> +				    struct flow_cls_offload *cls_flower)
> +{
> +	if (cls_flower->common.chain_index)
> +		return -EOPNOTSUPP;
> +
> +	switch (cls_flower->command) {
> +	case FLOW_CLS_REPLACE:
> +		return iecm_configure_clsflower(vport, cls_flower);
> +	case FLOW_CLS_DESTROY:
> +		return iecm_delete_clsflower(vport, cls_flower);
> +	case FLOW_CLS_STATS:
> +		return -EOPNOTSUPP;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +/**
> + * iecm_setup_tc_block_cb - block callback for tc
> + * @type: type of offload
> + * @type_data: offload data
> + * @cb_priv: Private adapter structure
> + *
> + * This function is the block callback for traffic classes
> + * Return 0 on success, negative on failure
> + **/
> +static int iecm_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
> +				  void *cb_priv)
> +{
> +	switch (type) {
> +	case TC_SETUP_CLSFLOWER:
> +		return iecm_setup_tc_cls_flower((struct iecm_vport *)cb_priv,
> +						(struct flow_cls_offload *)
> +						 type_data);

Just dereference them in separate variables and they will fit into
one line. There shouldn't be any spaces or line breaks after a cast.

> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +/**
> + * iecm_del_all_cloud_filters - delete all cloud filters on the traffic classes
> + * @vport: vport structure
> + *
> + * This function will loop through the list of cloud filters and deletes them.
> + **/
> +static void iecm_del_all_cloud_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +	struct iecm_cloud_filter *cf, *cftmp;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	list_for_each_entry_safe(cf, cftmp,
> +				 &cf_config->cloud_filter_list,
> +				 list) {
> +		list_del(&cf->list);
> +		kfree(cf);
> +		cf_config->num_cloud_filters--;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +}
> +
>  /**
>   * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
>   * @vport: vport structure
> @@ -2596,6 +3480,7 @@ static int __iecm_setup_tc(struct iecm_vport *vport, void *type_data)
>  			netif_tx_stop_all_queues(netdev);
>  			netif_tx_disable(netdev);
>  			ret = iecm_send_disable_channels_msg(vport);
> +			iecm_del_all_cloud_filters(vport);
>  			netif_tx_start_all_queues(netdev);
>  			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags) &&
>  			    !ret) {
> @@ -2709,8 +3594,10 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>  {
>  	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
>  	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
>  	int err = 0;
>  
> +	cf_config = &adapter->config_data.cf_config;
>  	switch (type) {
>  	case TC_SETUP_QDISC_ETF:
>  		if (iecm_is_queue_model_split(vport->txq_model))
> @@ -2720,6 +3607,17 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>  					     type_data);
>  		break;
>  	case TC_SETUP_BLOCK:
> +		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				    VIRTCHNL2_CAP_ADQ)) {
> +			err =
> +			flow_block_cb_setup_simple((struct flow_block_offload *)
> +						    type_data,
> +						   &cf_config->block_cb_list,
> +						   iecm_setup_tc_block_cb,
> +						   vport, vport, true);
> +		}

Invert the condition, and there'll be no line wraps (esp. if you
assign casted @type_data into a separate var).

>  		break;
>  	case TC_SETUP_QDISC_MQPRIO:
>  		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index 5601846b4674..94af45c36866 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -2731,6 +2731,74 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_add_del_cloud_filter_msg: Send add/del cloud filter message
> + * @vport: vport structure
> + * @add: True to add, false to delete cloud filter
> + *
> + * Request the CP/PF to add/del cloud filters as specified by the user via
> + * tc tool
> + *
> + * Return 0 on success, negative on failure
> + **/
> +int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +	struct iecm_cloud_filter *cf;
> +	struct virtchnl_filter f;
> +	int len = 0, err = 0;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +
> +	while (true) {
> +		bool process_fltr = false;
> +
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> +			if (add && cf->add) {
> +				process_fltr = true;
> +				cf->add = false;
> +				f = cf->f;
> +				break;
> +			} else if (!add && cf->remove) {
> +				process_fltr = true;
> +				cf->remove = false;
> +				f = cf->f;
> +				break;
> +			}
> +		}

Redundant braces.

> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +		/* Don't send mailbox message when there are no filters to add/del */
> +		if (!process_fltr)
> +			goto error;
> +
> +		if (add) {
> +			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_CLOUD_FILTER,
> +					       len, (u8 *)&f);
> +			if (err)
> +				goto error;
> +
> +			err = iecm_wait_for_event(adapter, IECM_VC_ADD_CLOUD_FILTER,
> +						  IECM_VC_ADD_CLOUD_FILTER_ERR);
> +		} else {
> +			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_CLOUD_FILTER,
> +					       len, (u8 *)&f);
> +			if (err)
> +				goto error;
> +
> +			err =
> +			iecm_min_wait_for_event(adapter, IECM_VC_DEL_CLOUD_FILTER,
> +						IECM_VC_DEL_CLOUD_FILTER_ERR);

Too long lines here.

> +		}
> +		if (err)
> +			break;
> +	}
> +error:
> +	return err;
> +}
> +
>  /**
>   * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
>   * @vport: vport structure
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index b0785684cc63..0aab41cf982c 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -403,6 +403,28 @@ enum iecm_user_flags {
>  	__IECM_USER_FLAGS_NBITS,
>  };
>  
> +#define IECM_CLOUD_FIELD_OMAC		BIT(0)
> +#define IECM_CLOUD_FIELD_IMAC		BIT(1)
> +#define IECM_CLOUD_FIELD_IVLAN		BIT(2)
> +#define IECM_CLOUD_FIELD_TEN_ID		BIT(3)
> +#define IECM_CLOUD_FIELD_IIP		BIT(4)
> +
> +#define IECM_START_CHNL_TC		1
> +
> +struct iecm_cloud_filter {
> +	struct list_head list;
> +	struct virtchnl_filter f;
> +	unsigned long cookie;
> +	bool remove;		/* filter needs to be deleted */
> +	bool add;		/* filter needs to be added */
> +};
> +
> +struct iecm_cloud_filter_config {
> +	struct list_head block_cb_list;		/* need to pass this to stack */
> +	struct list_head cloud_filter_list;
> +	u16 num_cloud_filters;
> +};
> +
>  struct iecm_channel_config {
>  	struct virtchnl_channel_info ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
>  	bool tc_running;
> @@ -536,6 +558,7 @@ struct iecm_ptype_state {
>  	bool outer_frag;
>  	u8 tunnel_state;
>  };
> +

Please move this newline into the patch where iecm_ptype_state was
introduced, it doesn't belong here.

>  /* User defined configuration values */
>  struct iecm_user_config_data {
>  	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
> @@ -550,6 +573,7 @@ struct iecm_user_config_data {
>  	struct list_head vlan_filter_list;
>  	struct list_head adv_rss_list;
>  	struct iecm_fdir_fltr_config fdir_config;
> +	struct iecm_cloud_filter_config cf_config;
>  	struct iecm_channel_config ch_config;
>  };
>  
> @@ -853,6 +877,7 @@ void iecm_set_ethtool_ops(struct net_device *netdev);
>  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
>  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
>  int iecm_set_promiscuous(struct iecm_adapter *adapter);
> +int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add);
>  int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
>  int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
>  int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> -- 
> 2.33.0

Thanks,
Al
Alan Brady Feb. 3, 2022, 2:53 a.m. UTC | #2
> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 11:38 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan@lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement
> cloud filters
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:07 -0800
> 
> > This gives iecm the ability to deal with cloud filters and other
> > traffic classes.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 900
> +++++++++++++++++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  68 ++
> >  drivers/net/ethernet/intel/include/iecm.h     |  25 +
> >  3 files changed, 992 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index 35c0cbc42ebe..d11413cb438c 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -43,9 +43,16 @@ static int iecm_get_vport_index(struct
> iecm_adapter *adapter,
> >   */
> >  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> > feature)  {
> > +	struct iecm_channel_config *ch_config;
> >  	bool ena;
> >
> >  	switch (feature) {
> > +	case NETIF_F_HW_TC:
> > +		ch_config = &vport->adapter->config_data.ch_config;
> > +		ena = (vport->netdev->features & feature) &&
> > +			(ch_config->num_tc > IECM_START_CHNL_TC) &&
> > +			(ch_config->tc_running);
> > +		break;
> >  	default:
> >  		ena = vport->netdev->features & feature;
> >  		break;
> > @@ -53,6 +60,23 @@ bool iecm_is_feature_ena(struct iecm_vport
> *vport, netdev_features_t feature)
> >  	return ena;
> >  }
> >
> > +/**
> > + * iecm_is_adq_v2_ena - Determine whether ADQ V2 is enabled
> > + * @vport: virtual port struct
> > + *
> > + * This function returns true based on negotiated capability ADQ_V2
> > + * if set and ADQ enabled
> > + */
> > +static bool iecm_is_adq_v2_ena(struct iecm_vport *vport) {
> > +	/* iecm_is_feature_ena tells if the netdev flag is set and adq is
> > +	 * enabled
> > +	 */
> > +	return (iecm_is_feature_ena(vport, NETIF_F_HW_TC) &&
> > +		iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> > +				VIRTCHNL2_CAP_ADQ));
> 
> The outermost braces are redundant.
> 

Will fix.

> > +}
> > +
> >  /**
> >   * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
> >   * @adapter: pointer to adapter
> > @@ -946,6 +970,28 @@ static int iecm_get_free_slot(void *array, int
> size, int curr)
> >  	return next;
> >  }
> >
> > +/**
> > + * iecm_remove_cloud_filters - Remove all cloud filters
> > + * @vport: vport structure
> > + */
> > +static void iecm_remove_cloud_filters(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +	if (!list_empty(&cf_config->cloud_filter_list)) {
> > +		struct iecm_cloud_filter *cf;
> > +
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> > +			cf->remove = true;
> > +		}
> 
> One-liner, braces are redundant.
> 
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		iecm_send_add_del_cloud_filter_msg(vport, false);
> > +	}
> 
> 	if (list_empty())
> 		return;
> 
> -1 level.
> 
> > +}
> > +
> >  /**
> >   * iecm_remove_vlan_filters - Remove all vlan filters
> >   * @vport: vport structure
> > @@ -1044,8 +1090,14 @@ static void iecm_vport_stop(struct
> iecm_vport *vport)
> >  	if (test_and_clear_bit(__IECM_DEL_QUEUES,
> >  			       vport->adapter->flags))
> >  		iecm_send_delete_queues_msg(vport);
> > -	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
> > +	/* In function reset/rmmod path we call unregister_netdev which
> > +	 * internally calls delete cloud filters. We remove cloud filters only
> > +	 * when the interface goes down
> > +	 */
> > +	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
> > +		iecm_remove_cloud_filters(vport);
> >  		iecm_remove_vlan_filters(vport);
> > +	}
> >
> >  	iecm_remove_fdir_filters(vport);
> >
> > @@ -1258,6 +1310,28 @@ static void iecm_restore_vlans(struct
> iecm_vport *vport)
> >  		iecm_set_all_vlans(vport);
> >  }
> >
> > +/**
> > + * iecm_restore_cloud_filters - Restore cloud filters
> > + * @vport: vport structure
> > + */
> > +static void iecm_restore_cloud_filters(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +	if (!list_empty(&cf_config->cloud_filter_list)) {
> > +		struct iecm_cloud_filter *cf;
> > +
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> > +			cf->add = true;
> > +		}
> 
> Same here for braces.
> 

Will fix.

> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		iecm_send_add_del_cloud_filter_msg(vport, true);
> > +	}
> 
> Same here for reducing indent level.
> 
> > +}
> > +
> >  /**
> >   * iecm_restore_fdir_filters - Restore all Flow Director filters
> >   * @vport: vport structure
> > @@ -1302,6 +1376,10 @@ static void iecm_restore_features(struct
> iecm_vport *vport)
> >  			dev_info(&adapter->pdev->dev, "Failed to restore
> promiscuous settings\n");
> >  	}
> >
> > +	/* Restore cloud filters if ADQ is enabled */
> > +	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
> > +		iecm_restore_cloud_filters(vport);
> > +
> >  	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> >  		iecm_restore_fdir_filters(vport);
> >  }
> > @@ -2088,6 +2166,8 @@ int iecm_probe(struct pci_dev *pdev,
> >  	spin_lock_init(&adapter->vlan_list_lock);
> >  	spin_lock_init(&adapter->adv_rss_list_lock);
> >  	spin_lock_init(&adapter->fdir_fltr_list_lock);
> > +	INIT_LIST_HEAD(&adapter->config_data.cf_config.cloud_filter_list);
> > +	INIT_LIST_HEAD(&adapter->config_data.cf_config.block_cb_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> > @@ -2389,6 +2469,810 @@ static int iecm_offload_txtime(struct
> iecm_vport *vport,
> >  	return -EOPNOTSUPP;
> >  }
> >
> > +/**
> > + * iecm_is_vlan_tc_filter_allowed - allowed to add tc-filter using
> > +VLAN
> > + * @vport: vport structure
> > + * @vlan: VLAN to verify
> > + *
> > + * Using specified "vlan" ID, there must be active VLAN filter in
> > +VF's
> > + * MAC-VLAN filter list.
> > + */
> > +static bool
> > +iecm_is_vlan_tc_filter_allowed(struct iecm_vport *vport,
> > +			       struct iecm_vlan *vlan)
> > +{
> > +	struct iecm_vlan_filter *f;
> > +	bool allowed;
> > +
> > +	spin_lock_bh(&vport->adapter->vlan_list_lock);
> > +	f = iecm_find_vlan(vport, vlan);
> > +	allowed = (f && !f->add && !f->remove);
> 
> Redundant braces here.
> 

Will fix.

> > +	spin_unlock_bh(&vport->adapter->vlan_list_lock);
> > +	return allowed;
> > +}
> > +
> > +/**
> > + * iecm_is_mac_tc_filter_allowed - allowed to add tc-filter using MAC
> > +addr
> > + * @vport: vport structure
> > + * @macaddr: MAC address
> > + *
> > + * Using specified MAC address, there must be active MAC filter in
> > + * MAC filter list.
> > + */
> > +static bool
> > +iecm_is_mac_tc_filter_allowed(struct iecm_vport *vport, const u8
> > +*macaddr) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_mac_filter *f;
> > +	bool allowed;
> > +
> > +	spin_lock_bh(&adapter->mac_filter_list_lock);
> > +	f = iecm_find_mac_filter(vport, macaddr);
> > +	allowed = (f && !f->add && !f->remove);
> 
> Same here.
> 
> > +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +	return allowed;
> > +}
> > +
> > +/**
> > + * iecm_parse_keyid - Parse keyid
> > + * @rule: Flow rule structure
> > + * @field_flags: Cloud filter flags
> > + */
> > +static void  iecm_parse_keyid(struct flow_rule *rule, u8
> > +*field_flags) {
> > +	struct flow_match_enc_keyid match;
> > +
> > +	flow_rule_match_enc_keyid(rule, &match);
> > +
> > +	if (match.mask->keyid != 0)
> > +		*field_flags |= IECM_CLOUD_FIELD_TEN_ID; }
> > +
> > +/**
> > + * iecm_parse_flow_type - Parse flow type based on L2 and L3
> > +protocols
> > + * @vport: vport structure
> > + * @rule: rule from user
> > + * @cf: Structure for the virtchnl filter
> > + * @filter: Structure for the cloud filter
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_flow_type(struct iecm_vport *vport,
> > +		     struct flow_rule *rule, struct virtchnl_filter *cf,
> > +		     struct iecm_cloud_filter *filter) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	enum virtchnl_flow_type flow_type;
> > +	struct flow_match_basic match;
> > +	u16 n_proto_mask = 0;
> > +	u16 n_proto_key = 0;
> > +	u16 n_proto = 0;
> > +	u8 ip_proto = 0;
> > +
> > +	flow_rule_match_basic(rule, &match);
> > +
> > +	n_proto_key = ntohs(match.key->n_proto);
> > +	n_proto_mask = ntohs(match.mask->n_proto);
> > +
> > +	if (n_proto_key == ETH_P_ALL) {
> > +		n_proto_key = 0;
> > +		n_proto_mask = 0;
> > +	}
> 
> 	n_proto_key = ntohs();
> 	if (n_proto_key != ETH_P_ALL)
> 		n_proto_mask = ntohs();
> 

Will fix

> > +	n_proto = n_proto_key & n_proto_mask;
> > +	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
> > +		return -EINVAL;
> > +
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ)) {
> > +		if (match.key->ip_proto != IPPROTO_TCP &&
> > +		    match.key->ip_proto != IPPROTO_UDP) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"Only TCP or UDP transport is
> supported\n");
> > +			return -EINVAL;
> > +		}
> > +	} else if (match.key->ip_proto != IPPROTO_TCP) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Only TCP transport is supported\n");
> > +		return -EINVAL;
> > +	}
> > +	ip_proto = match.key->ip_proto;
> > +
> > +	/* determine VIRTCHNL flow_type based on L3 and L4 protocol */
> > +	if (n_proto == ETH_P_IP)
> > +		flow_type = (ip_proto == IPPROTO_TCP) ?
> > +			     VIRTCHNL_TCP_V4_FLOW :
> > +			     VIRTCHNL_UDP_V4_FLOW;
> > +	else
> > +		flow_type = (ip_proto == IPPROTO_TCP) ?
> > +			     VIRTCHNL_TCP_V6_FLOW :
> > +			     VIRTCHNL_UDP_V6_FLOW;
> > +	cf->flow_type = flow_type;
> > +	filter->f.flow_type = flow_type;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_ether_header - Parse ethernet header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_ether_header(struct iecm_vport *vport, u8 *field_flags,
> > +			struct virtchnl_l4_spec *d_spec,
> > +			struct virtchnl_l4_spec *m_spec,
> > +			struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_eth_addrs match;
> > +	bool adv_adq_ena;
> > +
> > +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				      VIRTCHNL2_CAP_ADQ);
> > +
> > +	flow_rule_match_eth_addrs(rule, &match);
> > +
> > +	/* use is_broadcast and is_zero to check for all 0xf or 0 */
> > +	if (!is_zero_ether_addr(match.mask->dst)) {
> > +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask-
> >dst)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_OMAC;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ether dest
> mask %pM\n",
> > +				match.mask->dst);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (!is_zero_ether_addr(match.mask->src)) {
> > +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask-
> >src)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_IMAC;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ether src mask
> %pM\n",
> > +				match.mask->src);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (!is_zero_ether_addr(match.key->dst)) {
> > +		if (!iecm_is_mac_tc_filter_allowed(adapter->vports[0],
> > +						   match.key->dst)) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"Dest MAC %pM doesn't belong to this
> device\n",
> > +				match.key->dst);
> > +			return -EINVAL;
> > +		}
> > +
> > +		if (is_valid_ether_addr(match.key->dst) ||
> > +		    is_multicast_ether_addr(match.key->dst)) {
> > +			/* set the mask if a valid dst_mac address */
> > +			if (adv_adq_ena)
> > +				ether_addr_copy(m_spec->dst_mac,
> > +						match.mask->dst);
> > +			else
> > +				eth_broadcast_addr(m_spec->dst_mac);
> > +			ether_addr_copy(d_spec->dst_mac,
> > +					match.key->dst);
> > +		}
> > +	}
> > +
> > +	if (!is_zero_ether_addr(match.key->src))
> > +		if (is_valid_ether_addr(match.key->src) ||
> > +		    is_multicast_ether_addr(match.key->src)) {
> > +			/* set the mask if a valid src_mac address */
> > +			if (adv_adq_ena) {
> > +				ether_addr_copy(m_spec->src_mac,
> > +						match.mask->src);
> > +			} else {
> > +				eth_broadcast_addr(m_spec->src_mac);
> > +			}
> > +			ether_addr_copy(d_spec->src_mac,
> > +					match.key->src);
> > +		}
> 
> All previous ifs had braces, and it way okay since we have multiple nested if-
> elses, but here we don't have them.
> Please either add them here or remove all the way above to keep the code
> style consistent.
> 

Yes this needs braces will fix.

> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_vlan_header - Parse vlan header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_vlan_header(struct iecm_vport *vport, u8 *field_flags,
> > +		       struct virtchnl_l4_spec *d_spec,
> > +		       struct virtchnl_l4_spec *m_spec,
> > +		       struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_vlan match;
> > +
> > +	flow_rule_match_vlan(rule, &match);
> > +	if (match.mask->vlan_id) {
> > +		u16 vid = match.key->vlan_id & VLAN_VID_MASK;
> > +		struct iecm_vlan vlan;
> > +
> > +		vlan = IECM_VLAN(vid, ETH_P_8021Q);
> > +
> > +		if (match.mask->vlan_id != VLAN_VID_MASK) {
> > +			dev_err(&adapter->pdev->dev, "Bad vlan mask
> %u\n",
> > +				match.mask->vlan_id);
> > +			return -EINVAL;
> > +		}
> > +		if (!iecm_is_vlan_tc_filter_allowed(vport, &vlan)) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"VLAN %u doesn't belong to this VF\n",
> > +				vid);
> > +			return -EINVAL;
> > +		}
> > +		*field_flags |= IECM_CLOUD_FIELD_IVLAN;
> > +		m_spec->vlan_id = cpu_to_be16(match.mask->vlan_id);
> > +		d_spec->vlan_id = cpu_to_be16(match.key->vlan_id);
> > +	}
> 
> 	if (!vlan_id)
> 		return 0;
> 
> -1 level.
> 

Will fix.

> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_ipv4_header - Parse ipv4 header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_ipv4_header(struct iecm_vport *vport, u8 *field_flags,
> > +		       struct virtchnl_l4_spec *d_spec,
> > +		       struct virtchnl_l4_spec *m_spec,
> > +		       struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_ipv4_addrs match;
> > +	bool adv_adq_ena;
> > +
> > +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				      VIRTCHNL2_CAP_ADQ);
> > +
> > +	flow_rule_match_ipv4_addrs(rule, &match);
> > +
> > +	if (*field_flags & IECM_CLOUD_FIELD_TEN_ID) {
> > +		dev_info(&adapter->pdev->dev,
> > +			 "Tenant id not allowed for ip filter\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (match.mask->dst) {
> > +		if (adv_adq_ena || match.mask->dst ==
> cpu_to_be32(0xffffffff)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ip dst mask
> 0x%08x\n",
> > +				be32_to_cpu(match.mask->dst));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (match.mask->src) {
> > +		if (adv_adq_ena || match.mask->src ==
> cpu_to_be32(0xffffffff)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ip src mask
> 0x%08x\n",
> > +				be32_to_cpu(match.mask->dst));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (match.key->dst) {
> > +		if (adv_adq_ena)
> > +			m_spec->dst_ip[0] = match.mask->dst;
> > +		else
> > +			m_spec->dst_ip[0] = cpu_to_be32(0xffffffff);
> > +		d_spec->dst_ip[0] = match.key->dst;
> > +	}
> > +
> > +	if (match.key->src) {
> > +		if (adv_adq_ena)
> > +			m_spec->src_ip[0] = match.mask->src;
> > +		else
> > +			m_spec->src_ip[0] = cpu_to_be32(0xffffffff);
> > +		d_spec->src_ip[0] = match.key->src;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_ipv6_header - Parse ipv6 header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_ipv6_header(struct iecm_vport *vport, u8 *field_flags,
> > +		       struct virtchnl_l4_spec *d_spec,
> > +		       struct virtchnl_l4_spec *m_spec,
> > +		       struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_ipv6_addrs match;
> > +	int i;
> > +
> > +	flow_rule_match_ipv6_addrs(rule, &match);
> > +
> > +	/* validate mask, make sure it is not IPV6_ADDR_ANY */
> > +	if (ipv6_addr_any(&match.mask->dst)) {
> > +		dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask
> 0x%02x\n",
> > +			IPV6_ADDR_ANY);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* src and dest IPv6 address should not be LOOPBACK
> > +	 * (0:0:0:0:0:0:0:1) which can be represented as ::1
> > +	 */
> > +	if (ipv6_addr_loopback(&match.key->dst) ||
> > +	    ipv6_addr_loopback(&match.key->src)) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"ipv6 addr should not be loopback\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!ipv6_addr_any(&match.mask->dst) ||
> > +	    !ipv6_addr_any(&match.mask->src))
> > +		*field_flags |= IECM_CLOUD_FIELD_IIP;
> > +
> > +	/* copy dest IPv6 mask and address */
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ)) {
> > +		memcpy(&m_spec->dst_ip, &match.mask->dst.s6_addr32,
> > +		       sizeof(m_spec->dst_ip));
> > +	} else {
> > +		for (i = 0; i < 4; i++)
> > +			m_spec->dst_ip[i] = cpu_to_be32(0xffffffff);
> > +	}
> 
> One-liners, no braces needed for both `if` and `else`.
> 

Will not fix

> > +	memcpy(&d_spec->dst_ip, &match.key->dst.s6_addr32,
> > +	       sizeof(d_spec->dst_ip));
> > +
> > +	/* copy source IPv6 mask and address */
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ)) {
> > +		memcpy(&m_spec->src_ip, &match.mask->src.s6_addr32,
> > +		       sizeof(m_spec->src_ip));
> > +	} else {
> > +		for (i = 0; i < 4; i++)
> > +			m_spec->src_ip[i] = cpu_to_be32(0xffffffff);
> > +	}
> 
> Same here.
> 

Will not fix.

> > +	memcpy(&d_spec->src_ip, &match.key->src.s6_addr32,
> > +	       sizeof(d_spec->src_ip));
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_l4_header - Parse l4 header fields
> > + * @vport: vport structure
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_l4_header(struct iecm_vport *vport,
> > +		     struct virtchnl_l4_spec *d_spec,
> > +		     struct virtchnl_l4_spec *m_spec,
> > +		     struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_ports match;
> > +
> > +	flow_rule_match_ports(rule, &match);
> > +
> > +	if (match.key->dst) {
> > +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ) ||
> > +		    match.mask->dst == cpu_to_be16(0xffff)) {
> > +			m_spec->dst_port = match.mask->dst;
> > +			d_spec->dst_port = match.key->dst;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad dst port mask
> %u\n",
> > +				be16_to_cpu(match.mask->dst));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (match.key->src) {
> > +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ) ||
> > +		    match.mask->src == cpu_to_be16(0xffff)) {
> > +			m_spec->src_port = match.mask->src;
> > +			d_spec->src_port = match.key->src;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad src port mask
> %u\n",
> > +				be16_to_cpu(match.mask->src));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_cls_flower - Parse tc flower filters provided by kernel
> > + * @vport: vport structure
> > + * @f: pointer to struct flow_cls_offload
> > + * @filter: pointer to cloud filter structure  */ static int
> > +iecm_parse_cls_flower(struct iecm_vport *vport,
> > +				 struct flow_cls_offload *f,
> > +				 struct iecm_cloud_filter *filter) {
> > +	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl_l4_spec *d_spec, *m_spec;
> > +	struct virtchnl_filter *cf = &filter->f;
> > +	struct flow_dissector *dissector;
> > +	u8 field_flags = 0;
> > +	u16 addr_type = 0;
> > +	int err;
> > +
> > +	dissector = rule->match.dissector;
> > +	if (dissector->used_keys &
> > +	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
> > +	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
> > +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
> > +	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
> > +	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
> > +	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
> > +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
> > +	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
> > +		dev_err(&adapter->pdev->dev, "Unsupported key used:
> 0x%x\n",
> > +			dissector->used_keys);
> > +		return -EOPNOTSUPP;
> > +	}
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
> > +		iecm_parse_keyid(rule, &field_flags);
> > +
> > +	/* even though following code refers as "tcp_sec", it is not
> > +	 * just for TCP but a generic struct representing
> > +	 * L2, L3 + L4 fields if specified
> > +	 */
> > +	m_spec = &cf->mask.tcp_spec;
> > +	d_spec = &cf->data.tcp_spec;
> > +
> > +	/* determine flow type, TCP/UDP_V4[6]_FLOW based on
> > +	 * L2 proto (aka ETH proto) and L3 proto (aka IP_PROTO)
> > +	 */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
> > +		err = iecm_parse_flow_type(vport, rule, cf, filter);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process Ethernet header fields */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS))
> {
> > +		err = iecm_parse_ether_header(vport, &field_flags,
> > +					      d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process VLAN header for single VLAN (type could be S/C-tag) */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
> > +		err = iecm_parse_vlan_header(vport, &field_flags,
> > +					     d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
> > +		struct flow_match_control match;
> > +
> > +		flow_rule_match_control(rule, &match);
> > +		addr_type = match.key->addr_type;
> > +	}
> > +
> > +	/* process IPv4 header */
> > +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
> > +		err = iecm_parse_ipv4_header(vport, &field_flags,
> > +					     d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process IPv6 header */
> > +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
> > +		err = iecm_parse_ipv6_header(vport, &field_flags,
> > +					     d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process L4 header, supported L4 protocols are TCP and UDP */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
> > +		err = iecm_parse_l4_header(vport, d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +	cf->field_flags = field_flags;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_handle_tclass - Forward to a traffic class on the device
> > + * @vport: vport structure
> > + * @tc: traffic class index on the device
> > + * @filter: pointer to cloud filter structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_handle_tclass(struct iecm_vport *vport, int tc,
> > +			      struct iecm_cloud_filter *filter) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (tc == 0)
> > +		return 0;
> > +	if ((!iecm_is_adq_v2_ena(vport)) &&
> > +	    !filter->f.data.tcp_spec.dst_port) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Specify destination port to redirect to traffic class
> other than TC0\n");
> > +		return -EINVAL;
> > +	}
> > +	/* redirect to a traffic class on the same device */
> > +	filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
> > +	filter->f.action_meta = tc;
> > +	return 0;
> > +}
> > +
> > +/* iecm_find_cf - Find the cloud filter in the list
> > + * @vport: vport structure
> > + * @cookie: filter specific cookie
> > + *
> > + * Returns pointer to the filter object or NULL. Must be called while
> > +holding
> > + * cloud_filter_list_lock.
> > + */
> > +static struct iecm_cloud_filter *iecm_find_cf(struct iecm_vport *vport,
> > +					      unsigned long *cookie)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter *filter = NULL;
> > +
> > +	if (!cookie)
> > +		return NULL;
> > +
> > +	list_for_each_entry(filter,
> > +			    &adapter->config_data.cf_config.cloud_filter_list,
> > +			    list) {
> > +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
> > +			return filter;
> > +	}
> 
> Redundant braces round single statement.
> 

Will not fix.

> > +	return NULL;
> > +}
> > +
> > +/**
> > + * iecm_configure_clsflower - Add tc flower filters
> > + * @vport: vport structure
> > + * @cls_flower: Pointer to struct flow_cls_offload
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_configure_clsflower(struct iecm_vport *vport,
> > +				    struct flow_cls_offload *cls_flower) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_user_config_data *config_data;
> > +	struct iecm_cloud_filter *filter = NULL;
> > +	int err;
> > +	int tc;
> > +
> > +	config_data = &adapter->config_data;
> > +	tc = tc_classid_to_hwtc(vport->netdev, cls_flower->classid);
> > +	if (tc < 0) {
> > +		dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +#define IECM_MAX_CLOUD_ADQ_FILTERS	128
> > +
> > +	if (config_data->cf_config.num_cloud_filters >=
> > +
> 	IECM_MAX_CLOUD_ADQ_FILTERS) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Unable to add filter (action is forward to TC),
> reached max allowed filters (%u)\n",
> > +			IECM_MAX_CLOUD_ADQ_FILTERS);
> > +		return -ENOSPC;
> > +	}
> > +
> > +	/* bail out here if filter already exists */
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +	if (filter) {
> > +		filter->remove = false;
> > +		dev_err(&adapter->pdev->dev, "Failed to add TC Flower
> filter, it already exists\n");
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		return -EEXIST;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> > +	if (!filter)
> > +		return -ENOMEM;
> > +
> > +	filter->cookie = cls_flower->cookie;
> > +
> > +	/* set the mask to all zeroes to begin with */
> > +	memset(&filter->f.mask.tcp_spec, 0, sizeof(struct
> > +virtchnl_l4_spec));
> > +
> > +	/* start out with flow type and eth type IPv4 to begin with */
> > +	filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
> > +	err = iecm_parse_cls_flower(vport, cls_flower, filter);
> > +	if (err)
> > +		goto error;
> > +
> > +	err = iecm_handle_tclass(vport, tc, filter);
> > +	if (err)
> > +		goto error;
> > +
> > +	/* add filter to the list */
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	list_add_tail(&filter->list, &config_data->cf_config.cloud_filter_list);
> > +	filter->add = true;
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +	err = iecm_send_add_del_cloud_filter_msg(vport, true);
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	/* We have to find it again in case another thread has already
> > +	 * deleted and kfreed it.
> > +	 */
> > +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +	if (filter && err) {
> > +		list_del(&filter->list);
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		goto error;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +	config_data->cf_config.num_cloud_filters++;
> > +error:
> > +	if (err)
> > +		kfree(filter);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_delete_clsflower - Remove tc flower filters
> > + * @vport: vport structure
> > + * @cls_flower: Pointer to struct flow_cls_offload
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_delete_clsflower(struct iecm_vport *vport,
> > +				 struct flow_cls_offload *cls_flower) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter *filter = NULL;
> > +	int err = 0;
> > +
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +	if (filter) {
> > +		filter->remove = true;
> > +		adapter->config_data.cf_config.num_cloud_filters--;
> > +	} else if (adapter->config_data.cf_config.num_cloud_filters) {
> > +		/* "num_cloud_filters" can become zero if egress qdisc is
> > +		 * detached as per design, driver deletes related filters
> > +		 * when qdisc is detached to avoid stale filters, hence
> > +		 * num_cloud_filters can become zero. But since netdev
> > +		 * layer doesn't know that filters are deleted by driver
> > +		 * implictly when egress qdisc is deleted, it sees filters
> > +		 * being present and "in_hw". User can request delete
> > +		 * of specific filter of detach ingress qdisc - in either of
> > +		 * those operation, filter(s) won't be found in driver cache,
> > +		 * hence instead of returning, let this function return
> SUCCESS
> > +		 * Returning of err as -EINVAL is only applicable when
> > +		 * unable to find filter and num_cloud_filters is non-zero
> > +		 */
> > +		err = -EINVAL;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +	if (filter) {
> > +		err = iecm_send_add_del_cloud_filter_msg(vport, false);
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		/* It can happen that asynchronously the filter was already
> > +		 * deleted from the list. Make sure it's still there and
> marked
> > +		 * for remove under spinlock before actually trying to delete
> > +		 * from list.
> > +		 */
> > +		filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +		if (filter) {
> > +			list_del(&filter->list);
> > +			kfree(filter);
> > +		}
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +	}
> 
> 	if (!filter)
> 		return err;
> 
> 	err = ...
> 
> -1 level.
> 

Will fix.

> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_setup_tc_cls_flower - flower classifier offloads
> > + * @vport: vport structure
> > + * @cls_flower: pointer to struct flow_cls_offload
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_setup_tc_cls_flower(struct iecm_vport *vport,
> > +				    struct flow_cls_offload *cls_flower) {
> > +	if (cls_flower->common.chain_index)
> > +		return -EOPNOTSUPP;
> > +
> > +	switch (cls_flower->command) {
> > +	case FLOW_CLS_REPLACE:
> > +		return iecm_configure_clsflower(vport, cls_flower);
> > +	case FLOW_CLS_DESTROY:
> > +		return iecm_delete_clsflower(vport, cls_flower);
> > +	case FLOW_CLS_STATS:
> > +		return -EOPNOTSUPP;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_setup_tc_block_cb - block callback for tc
> > + * @type: type of offload
> > + * @type_data: offload data
> > + * @cb_priv: Private adapter structure
> > + *
> > + * This function is the block callback for traffic classes
> > + * Return 0 on success, negative on failure  **/ static int
> > +iecm_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
> > +				  void *cb_priv)
> > +{
> > +	switch (type) {
> > +	case TC_SETUP_CLSFLOWER:
> > +		return iecm_setup_tc_cls_flower((struct iecm_vport
> *)cb_priv,
> > +						(struct flow_cls_offload *)
> > +						 type_data);
> 
> Just dereference them in separate variables and they will fit into one line.
> There shouldn't be any spaces or line breaks after a cast.
> 

Will take a look.

> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_del_all_cloud_filters - delete all cloud filters on the
> > +traffic classes
> > + * @vport: vport structure
> > + *
> > + * This function will loop through the list of cloud filters and deletes
> them.
> > + **/
> > +static void iecm_del_all_cloud_filters(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +	struct iecm_cloud_filter *cf, *cftmp;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	list_for_each_entry_safe(cf, cftmp,
> > +				 &cf_config->cloud_filter_list,
> > +				 list) {
> > +		list_del(&cf->list);
> > +		kfree(cf);
> > +		cf_config->num_cloud_filters--;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +}
> > +
> >  /**
> >   * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
> >   * @vport: vport structure
> > @@ -2596,6 +3480,7 @@ static int __iecm_setup_tc(struct iecm_vport
> *vport, void *type_data)
> >  			netif_tx_stop_all_queues(netdev);
> >  			netif_tx_disable(netdev);
> >  			ret = iecm_send_disable_channels_msg(vport);
> > +			iecm_del_all_cloud_filters(vport);
> >  			netif_tx_start_all_queues(netdev);
> >  			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter-
> >flags) &&
> >  			    !ret) {
> > @@ -2709,8 +3594,10 @@ static int iecm_setup_tc(struct net_device
> > *netdev, enum tc_setup_type type,  {
> >  	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> >  	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> >  	int err = 0;
> >
> > +	cf_config = &adapter->config_data.cf_config;
> >  	switch (type) {
> >  	case TC_SETUP_QDISC_ETF:
> >  		if (iecm_is_queue_model_split(vport->txq_model))
> > @@ -2720,6 +3607,17 @@ static int iecm_setup_tc(struct net_device
> *netdev, enum tc_setup_type type,
> >  					     type_data);
> >  		break;
> >  	case TC_SETUP_BLOCK:
> > +		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ) ||
> > +		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ)) {
> > +			err =
> > +			flow_block_cb_setup_simple((struct
> flow_block_offload *)
> > +						    type_data,
> > +						   &cf_config->block_cb_list,
> > +						   iecm_setup_tc_block_cb,
> > +						   vport, vport, true);
> > +		}
> 
> Invert the condition, and there'll be no line wraps (esp. if you assign casted
> @type_data into a separate var).
> 

Will fix

> >  		break;
> >  	case TC_SETUP_QDISC_MQPRIO:
> >  		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS, diff --git
> > a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index 5601846b4674..94af45c36866 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -2731,6 +2731,74 @@ static int iecm_send_insert_vlan_msg(struct
> iecm_vport *vport, bool ena)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_add_del_cloud_filter_msg: Send add/del cloud filter
> > +message
> > + * @vport: vport structure
> > + * @add: True to add, false to delete cloud filter
> > + *
> > + * Request the CP/PF to add/del cloud filters as specified by the
> > +user via
> > + * tc tool
> > + *
> > + * Return 0 on success, negative on failure  **/ int
> > +iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool
> > +add) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +	struct iecm_cloud_filter *cf;
> > +	struct virtchnl_filter f;
> > +	int len = 0, err = 0;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +
> > +	while (true) {
> > +		bool process_fltr = false;
> > +
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> > +			if (add && cf->add) {
> > +				process_fltr = true;
> > +				cf->add = false;
> > +				f = cf->f;
> > +				break;
> > +			} else if (!add && cf->remove) {
> > +				process_fltr = true;
> > +				cf->remove = false;
> > +				f = cf->f;
> > +				break;
> > +			}
> > +		}
> 
> Redundant braces.

Will not fix.

> 
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +		/* Don't send mailbox message when there are no filters to
> add/del */
> > +		if (!process_fltr)
> > +			goto error;
> > +
> > +		if (add) {
> > +			err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_ADD_CLOUD_FILTER,
> > +					       len, (u8 *)&f);
> > +			if (err)
> > +				goto error;
> > +
> > +			err = iecm_wait_for_event(adapter,
> IECM_VC_ADD_CLOUD_FILTER,
> > +
> IECM_VC_ADD_CLOUD_FILTER_ERR);
> > +		} else {
> > +			err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_DEL_CLOUD_FILTER,
> > +					       len, (u8 *)&f);
> > +			if (err)
> > +				goto error;
> > +
> > +			err =
> > +			iecm_min_wait_for_event(adapter,
> IECM_VC_DEL_CLOUD_FILTER,
> > +
> 	IECM_VC_DEL_CLOUD_FILTER_ERR);
> 
> Too long lines here.
> 
> > +		}
> > +		if (err)
> > +			break;
> > +	}
> > +error:
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
> >   * @vport: vport structure
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index b0785684cc63..0aab41cf982c 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -403,6 +403,28 @@ enum iecm_user_flags {
> >  	__IECM_USER_FLAGS_NBITS,
> >  };
> >
> > +#define IECM_CLOUD_FIELD_OMAC		BIT(0)
> > +#define IECM_CLOUD_FIELD_IMAC		BIT(1)
> > +#define IECM_CLOUD_FIELD_IVLAN		BIT(2)
> > +#define IECM_CLOUD_FIELD_TEN_ID		BIT(3)
> > +#define IECM_CLOUD_FIELD_IIP		BIT(4)
> > +
> > +#define IECM_START_CHNL_TC		1
> > +
> > +struct iecm_cloud_filter {
> > +	struct list_head list;
> > +	struct virtchnl_filter f;
> > +	unsigned long cookie;
> > +	bool remove;		/* filter needs to be deleted */
> > +	bool add;		/* filter needs to be added */
> > +};
> > +
> > +struct iecm_cloud_filter_config {
> > +	struct list_head block_cb_list;		/* need to pass this to stack
> */
> > +	struct list_head cloud_filter_list;
> > +	u16 num_cloud_filters;
> > +};
> > +
> >  struct iecm_channel_config {
> >  	struct virtchnl_channel_info
> ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
> >  	bool tc_running;
> > @@ -536,6 +558,7 @@ struct iecm_ptype_state {
> >  	bool outer_frag;
> >  	u8 tunnel_state;
> >  };
> > +
> 
> Please move this newline into the patch where iecm_ptype_state was
> introduced, it doesn't belong here.
> 

Will fix

> >  /* User defined configuration values */  struct iecm_user_config_data
> > {
> >  	u32 num_req_tx_qs; /* user requested TX queues through ethtool
> */ @@
> > -550,6 +573,7 @@ struct iecm_user_config_data {
> >  	struct list_head vlan_filter_list;
> >  	struct list_head adv_rss_list;
> >  	struct iecm_fdir_fltr_config fdir_config;
> > +	struct iecm_cloud_filter_config cf_config;
> >  	struct iecm_channel_config ch_config;  };
> >
> > @@ -853,6 +877,7 @@ void iecm_set_ethtool_ops(struct net_device
> > *netdev);  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool
> > ena);  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool
> > add, bool async);  int iecm_set_promiscuous(struct iecm_adapter
> > *adapter);
> > +int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport,
> bool
> > +add);
> >  int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);  int
> > iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);  int
> > iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> > --
> > 2.33.0
> 
> Thanks,
> Al
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 35c0cbc42ebe..d11413cb438c 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -43,9 +43,16 @@  static int iecm_get_vport_index(struct iecm_adapter *adapter,
  */
 bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
 {
+	struct iecm_channel_config *ch_config;
 	bool ena;
 
 	switch (feature) {
+	case NETIF_F_HW_TC:
+		ch_config = &vport->adapter->config_data.ch_config;
+		ena = (vport->netdev->features & feature) &&
+			(ch_config->num_tc > IECM_START_CHNL_TC) &&
+			(ch_config->tc_running);
+		break;
 	default:
 		ena = vport->netdev->features & feature;
 		break;
@@ -53,6 +60,23 @@  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
 	return ena;
 }
 
+/**
+ * iecm_is_adq_v2_ena - Determine whether ADQ V2 is enabled
+ * @vport: virtual port struct
+ *
+ * This function returns true based on negotiated capability ADQ_V2
+ * if set and ADQ enabled
+ */
+static bool iecm_is_adq_v2_ena(struct iecm_vport *vport)
+{
+	/* iecm_is_feature_ena tells if the netdev flag is set and adq is
+	 * enabled
+	 */
+	return (iecm_is_feature_ena(vport, NETIF_F_HW_TC) &&
+		iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
+				VIRTCHNL2_CAP_ADQ));
+}
+
 /**
  * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
  * @adapter: pointer to adapter
@@ -946,6 +970,28 @@  static int iecm_get_free_slot(void *array, int size, int curr)
 	return next;
 }
 
+/**
+ * iecm_remove_cloud_filters - Remove all cloud filters
+ * @vport: vport structure
+ */
+static void iecm_remove_cloud_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+
+	cf_config = &adapter->config_data.cf_config;
+	if (!list_empty(&cf_config->cloud_filter_list)) {
+		struct iecm_cloud_filter *cf;
+
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
+			cf->remove = true;
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		iecm_send_add_del_cloud_filter_msg(vport, false);
+	}
+}
+
 /**
  * iecm_remove_vlan_filters - Remove all vlan filters
  * @vport: vport structure
@@ -1044,8 +1090,14 @@  static void iecm_vport_stop(struct iecm_vport *vport)
 	if (test_and_clear_bit(__IECM_DEL_QUEUES,
 			       vport->adapter->flags))
 		iecm_send_delete_queues_msg(vport);
-	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
+	/* In function reset/rmmod path we call unregister_netdev which
+	 * internally calls delete cloud filters. We remove cloud filters only
+	 * when the interface goes down
+	 */
+	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
+		iecm_remove_cloud_filters(vport);
 		iecm_remove_vlan_filters(vport);
+	}
 
 	iecm_remove_fdir_filters(vport);
 
@@ -1258,6 +1310,28 @@  static void iecm_restore_vlans(struct iecm_vport *vport)
 		iecm_set_all_vlans(vport);
 }
 
+/**
+ * iecm_restore_cloud_filters - Restore cloud filters
+ * @vport: vport structure
+ */
+static void iecm_restore_cloud_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+
+	cf_config = &adapter->config_data.cf_config;
+	if (!list_empty(&cf_config->cloud_filter_list)) {
+		struct iecm_cloud_filter *cf;
+
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
+			cf->add = true;
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		iecm_send_add_del_cloud_filter_msg(vport, true);
+	}
+}
+
 /**
  * iecm_restore_fdir_filters - Restore all Flow Director filters
  * @vport: vport structure
@@ -1302,6 +1376,10 @@  static void iecm_restore_features(struct iecm_vport *vport)
 			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
 	}
 
+	/* Restore cloud filters if ADQ is enabled */
+	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
+		iecm_restore_cloud_filters(vport);
+
 	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
 		iecm_restore_fdir_filters(vport);
 }
@@ -2088,6 +2166,8 @@  int iecm_probe(struct pci_dev *pdev,
 	spin_lock_init(&adapter->vlan_list_lock);
 	spin_lock_init(&adapter->adv_rss_list_lock);
 	spin_lock_init(&adapter->fdir_fltr_list_lock);
+	INIT_LIST_HEAD(&adapter->config_data.cf_config.cloud_filter_list);
+	INIT_LIST_HEAD(&adapter->config_data.cf_config.block_cb_list);
 	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
 	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
 	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
@@ -2389,6 +2469,810 @@  static int iecm_offload_txtime(struct iecm_vport *vport,
 	return -EOPNOTSUPP;
 }
 
+/**
+ * iecm_is_vlan_tc_filter_allowed - allowed to add tc-filter using VLAN
+ * @vport: vport structure
+ * @vlan: VLAN to verify
+ *
+ * Using specified "vlan" ID, there must be active VLAN filter in VF's
+ * MAC-VLAN filter list.
+ */
+static bool
+iecm_is_vlan_tc_filter_allowed(struct iecm_vport *vport,
+			       struct iecm_vlan *vlan)
+{
+	struct iecm_vlan_filter *f;
+	bool allowed;
+
+	spin_lock_bh(&vport->adapter->vlan_list_lock);
+	f = iecm_find_vlan(vport, vlan);
+	allowed = (f && !f->add && !f->remove);
+	spin_unlock_bh(&vport->adapter->vlan_list_lock);
+	return allowed;
+}
+
+/**
+ * iecm_is_mac_tc_filter_allowed - allowed to add tc-filter using MAC addr
+ * @vport: vport structure
+ * @macaddr: MAC address
+ *
+ * Using specified MAC address, there must be active MAC filter in
+ * MAC filter list.
+ */
+static bool
+iecm_is_mac_tc_filter_allowed(struct iecm_vport *vport, const u8 *macaddr)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_mac_filter *f;
+	bool allowed;
+
+	spin_lock_bh(&adapter->mac_filter_list_lock);
+	f = iecm_find_mac_filter(vport, macaddr);
+	allowed = (f && !f->add && !f->remove);
+	spin_unlock_bh(&adapter->mac_filter_list_lock);
+	return allowed;
+}
+
+/**
+ * iecm_parse_keyid - Parse keyid
+ * @rule: Flow rule structure
+ * @field_flags: Cloud filter flags
+ */
+static void  iecm_parse_keyid(struct flow_rule *rule, u8 *field_flags)
+{
+	struct flow_match_enc_keyid match;
+
+	flow_rule_match_enc_keyid(rule, &match);
+
+	if (match.mask->keyid != 0)
+		*field_flags |= IECM_CLOUD_FIELD_TEN_ID;
+}
+
+/**
+ * iecm_parse_flow_type - Parse flow type based on L2 and L3 protocols
+ * @vport: vport structure
+ * @rule: rule from user
+ * @cf: Structure for the virtchnl filter
+ * @filter: Structure for the cloud filter
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_flow_type(struct iecm_vport *vport,
+		     struct flow_rule *rule, struct virtchnl_filter *cf,
+		     struct iecm_cloud_filter *filter)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	enum virtchnl_flow_type flow_type;
+	struct flow_match_basic match;
+	u16 n_proto_mask = 0;
+	u16 n_proto_key = 0;
+	u16 n_proto = 0;
+	u8 ip_proto = 0;
+
+	flow_rule_match_basic(rule, &match);
+
+	n_proto_key = ntohs(match.key->n_proto);
+	n_proto_mask = ntohs(match.mask->n_proto);
+
+	if (n_proto_key == ETH_P_ALL) {
+		n_proto_key = 0;
+		n_proto_mask = 0;
+	}
+	n_proto = n_proto_key & n_proto_mask;
+	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
+		return -EINVAL;
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
+		if (match.key->ip_proto != IPPROTO_TCP &&
+		    match.key->ip_proto != IPPROTO_UDP) {
+			dev_err(&adapter->pdev->dev,
+				"Only TCP or UDP transport is supported\n");
+			return -EINVAL;
+		}
+	} else if (match.key->ip_proto != IPPROTO_TCP) {
+		dev_err(&adapter->pdev->dev,
+			"Only TCP transport is supported\n");
+		return -EINVAL;
+	}
+	ip_proto = match.key->ip_proto;
+
+	/* determine VIRTCHNL flow_type based on L3 and L4 protocol */
+	if (n_proto == ETH_P_IP)
+		flow_type = (ip_proto == IPPROTO_TCP) ?
+			     VIRTCHNL_TCP_V4_FLOW :
+			     VIRTCHNL_UDP_V4_FLOW;
+	else
+		flow_type = (ip_proto == IPPROTO_TCP) ?
+			     VIRTCHNL_TCP_V6_FLOW :
+			     VIRTCHNL_UDP_V6_FLOW;
+	cf->flow_type = flow_type;
+	filter->f.flow_type = flow_type;
+
+	return 0;
+}
+
+/**
+ * iecm_parse_ether_header - Parse ethernet header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_ether_header(struct iecm_vport *vport, u8 *field_flags,
+			struct virtchnl_l4_spec *d_spec,
+			struct virtchnl_l4_spec *m_spec,
+			struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_eth_addrs match;
+	bool adv_adq_ena;
+
+	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				      VIRTCHNL2_CAP_ADQ);
+
+	flow_rule_match_eth_addrs(rule, &match);
+
+	/* use is_broadcast and is_zero to check for all 0xf or 0 */
+	if (!is_zero_ether_addr(match.mask->dst)) {
+		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->dst)) {
+			*field_flags |= IECM_CLOUD_FIELD_OMAC;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ether dest mask %pM\n",
+				match.mask->dst);
+			return -EINVAL;
+		}
+	}
+
+	if (!is_zero_ether_addr(match.mask->src)) {
+		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->src)) {
+			*field_flags |= IECM_CLOUD_FIELD_IMAC;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ether src mask %pM\n",
+				match.mask->src);
+			return -EINVAL;
+		}
+	}
+
+	if (!is_zero_ether_addr(match.key->dst)) {
+		if (!iecm_is_mac_tc_filter_allowed(adapter->vports[0],
+						   match.key->dst)) {
+			dev_err(&adapter->pdev->dev,
+				"Dest MAC %pM doesn't belong to this device\n",
+				match.key->dst);
+			return -EINVAL;
+		}
+
+		if (is_valid_ether_addr(match.key->dst) ||
+		    is_multicast_ether_addr(match.key->dst)) {
+			/* set the mask if a valid dst_mac address */
+			if (adv_adq_ena)
+				ether_addr_copy(m_spec->dst_mac,
+						match.mask->dst);
+			else
+				eth_broadcast_addr(m_spec->dst_mac);
+			ether_addr_copy(d_spec->dst_mac,
+					match.key->dst);
+		}
+	}
+
+	if (!is_zero_ether_addr(match.key->src))
+		if (is_valid_ether_addr(match.key->src) ||
+		    is_multicast_ether_addr(match.key->src)) {
+			/* set the mask if a valid src_mac address */
+			if (adv_adq_ena) {
+				ether_addr_copy(m_spec->src_mac,
+						match.mask->src);
+			} else {
+				eth_broadcast_addr(m_spec->src_mac);
+			}
+			ether_addr_copy(d_spec->src_mac,
+					match.key->src);
+		}
+	return 0;
+}
+
+/**
+ * iecm_parse_vlan_header - Parse vlan header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_vlan_header(struct iecm_vport *vport, u8 *field_flags,
+		       struct virtchnl_l4_spec *d_spec,
+		       struct virtchnl_l4_spec *m_spec,
+		       struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_vlan match;
+
+	flow_rule_match_vlan(rule, &match);
+	if (match.mask->vlan_id) {
+		u16 vid = match.key->vlan_id & VLAN_VID_MASK;
+		struct iecm_vlan vlan;
+
+		vlan = IECM_VLAN(vid, ETH_P_8021Q);
+
+		if (match.mask->vlan_id != VLAN_VID_MASK) {
+			dev_err(&adapter->pdev->dev, "Bad vlan mask %u\n",
+				match.mask->vlan_id);
+			return -EINVAL;
+		}
+		if (!iecm_is_vlan_tc_filter_allowed(vport, &vlan)) {
+			dev_err(&adapter->pdev->dev,
+				"VLAN %u doesn't belong to this VF\n",
+				vid);
+			return -EINVAL;
+		}
+		*field_flags |= IECM_CLOUD_FIELD_IVLAN;
+		m_spec->vlan_id = cpu_to_be16(match.mask->vlan_id);
+		d_spec->vlan_id = cpu_to_be16(match.key->vlan_id);
+	}
+	return 0;
+}
+
+/**
+ * iecm_parse_ipv4_header - Parse ipv4 header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_ipv4_header(struct iecm_vport *vport, u8 *field_flags,
+		       struct virtchnl_l4_spec *d_spec,
+		       struct virtchnl_l4_spec *m_spec,
+		       struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_ipv4_addrs match;
+	bool adv_adq_ena;
+
+	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				      VIRTCHNL2_CAP_ADQ);
+
+	flow_rule_match_ipv4_addrs(rule, &match);
+
+	if (*field_flags & IECM_CLOUD_FIELD_TEN_ID) {
+		dev_info(&adapter->pdev->dev,
+			 "Tenant id not allowed for ip filter\n");
+		return -EINVAL;
+	}
+
+	if (match.mask->dst) {
+		if (adv_adq_ena || match.mask->dst == cpu_to_be32(0xffffffff)) {
+			*field_flags |= IECM_CLOUD_FIELD_IIP;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ip dst mask 0x%08x\n",
+				be32_to_cpu(match.mask->dst));
+			return -EINVAL;
+		}
+	}
+
+	if (match.mask->src) {
+		if (adv_adq_ena || match.mask->src == cpu_to_be32(0xffffffff)) {
+			*field_flags |= IECM_CLOUD_FIELD_IIP;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
+				be32_to_cpu(match.mask->dst));
+			return -EINVAL;
+		}
+	}
+
+	if (match.key->dst) {
+		if (adv_adq_ena)
+			m_spec->dst_ip[0] = match.mask->dst;
+		else
+			m_spec->dst_ip[0] = cpu_to_be32(0xffffffff);
+		d_spec->dst_ip[0] = match.key->dst;
+	}
+
+	if (match.key->src) {
+		if (adv_adq_ena)
+			m_spec->src_ip[0] = match.mask->src;
+		else
+			m_spec->src_ip[0] = cpu_to_be32(0xffffffff);
+		d_spec->src_ip[0] = match.key->src;
+	}
+	return 0;
+}
+
+/**
+ * iecm_parse_ipv6_header - Parse ipv6 header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_ipv6_header(struct iecm_vport *vport, u8 *field_flags,
+		       struct virtchnl_l4_spec *d_spec,
+		       struct virtchnl_l4_spec *m_spec,
+		       struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_ipv6_addrs match;
+	int i;
+
+	flow_rule_match_ipv6_addrs(rule, &match);
+
+	/* validate mask, make sure it is not IPV6_ADDR_ANY */
+	if (ipv6_addr_any(&match.mask->dst)) {
+		dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
+			IPV6_ADDR_ANY);
+		return -EINVAL;
+	}
+
+	/* src and dest IPv6 address should not be LOOPBACK
+	 * (0:0:0:0:0:0:0:1) which can be represented as ::1
+	 */
+	if (ipv6_addr_loopback(&match.key->dst) ||
+	    ipv6_addr_loopback(&match.key->src)) {
+		dev_err(&adapter->pdev->dev,
+			"ipv6 addr should not be loopback\n");
+		return -EINVAL;
+	}
+
+	if (!ipv6_addr_any(&match.mask->dst) ||
+	    !ipv6_addr_any(&match.mask->src))
+		*field_flags |= IECM_CLOUD_FIELD_IIP;
+
+	/* copy dest IPv6 mask and address */
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
+		memcpy(&m_spec->dst_ip, &match.mask->dst.s6_addr32,
+		       sizeof(m_spec->dst_ip));
+	} else {
+		for (i = 0; i < 4; i++)
+			m_spec->dst_ip[i] = cpu_to_be32(0xffffffff);
+	}
+	memcpy(&d_spec->dst_ip, &match.key->dst.s6_addr32,
+	       sizeof(d_spec->dst_ip));
+
+	/* copy source IPv6 mask and address */
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
+		memcpy(&m_spec->src_ip, &match.mask->src.s6_addr32,
+		       sizeof(m_spec->src_ip));
+	} else {
+		for (i = 0; i < 4; i++)
+			m_spec->src_ip[i] = cpu_to_be32(0xffffffff);
+	}
+	memcpy(&d_spec->src_ip, &match.key->src.s6_addr32,
+	       sizeof(d_spec->src_ip));
+
+	return 0;
+}
+
+/**
+ * iecm_parse_l4_header - Parse l4 header fields
+ * @vport: vport structure
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_l4_header(struct iecm_vport *vport,
+		     struct virtchnl_l4_spec *d_spec,
+		     struct virtchnl_l4_spec *m_spec,
+		     struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_ports match;
+
+	flow_rule_match_ports(rule, &match);
+
+	if (match.key->dst) {
+		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    match.mask->dst == cpu_to_be16(0xffff)) {
+			m_spec->dst_port = match.mask->dst;
+			d_spec->dst_port = match.key->dst;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad dst port mask %u\n",
+				be16_to_cpu(match.mask->dst));
+			return -EINVAL;
+		}
+	}
+
+	if (match.key->src) {
+		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    match.mask->src == cpu_to_be16(0xffff)) {
+			m_spec->src_port = match.mask->src;
+			d_spec->src_port = match.key->src;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad src port mask %u\n",
+				be16_to_cpu(match.mask->src));
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/**
+ * iecm_parse_cls_flower - Parse tc flower filters provided by kernel
+ * @vport: vport structure
+ * @f: pointer to struct flow_cls_offload
+ * @filter: pointer to cloud filter structure
+ */
+static int iecm_parse_cls_flower(struct iecm_vport *vport,
+				 struct flow_cls_offload *f,
+				 struct iecm_cloud_filter *filter)
+{
+	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_l4_spec *d_spec, *m_spec;
+	struct virtchnl_filter *cf = &filter->f;
+	struct flow_dissector *dissector;
+	u8 field_flags = 0;
+	u16 addr_type = 0;
+	int err;
+
+	dissector = rule->match.dissector;
+	if (dissector->used_keys &
+	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
+	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
+	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
+	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
+	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
+		dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n",
+			dissector->used_keys);
+		return -EOPNOTSUPP;
+	}
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
+		iecm_parse_keyid(rule, &field_flags);
+
+	/* even though following code refers as "tcp_sec", it is not
+	 * just for TCP but a generic struct representing
+	 * L2, L3 + L4 fields if specified
+	 */
+	m_spec = &cf->mask.tcp_spec;
+	d_spec = &cf->data.tcp_spec;
+
+	/* determine flow type, TCP/UDP_V4[6]_FLOW based on
+	 * L2 proto (aka ETH proto) and L3 proto (aka IP_PROTO)
+	 */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+		err = iecm_parse_flow_type(vport, rule, cf, filter);
+		if (err)
+			return err;
+	}
+
+	/* process Ethernet header fields */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+		err = iecm_parse_ether_header(vport, &field_flags,
+					      d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	/* process VLAN header for single VLAN (type could be S/C-tag) */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+		err = iecm_parse_vlan_header(vport, &field_flags,
+					     d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+		struct flow_match_control match;
+
+		flow_rule_match_control(rule, &match);
+		addr_type = match.key->addr_type;
+	}
+
+	/* process IPv4 header */
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+		err = iecm_parse_ipv4_header(vport, &field_flags,
+					     d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	/* process IPv6 header */
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+		err = iecm_parse_ipv6_header(vport, &field_flags,
+					     d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	/* process L4 header, supported L4 protocols are TCP and UDP */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+		err = iecm_parse_l4_header(vport, d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+	cf->field_flags = field_flags;
+
+	return 0;
+}
+
+/**
+ * iecm_handle_tclass - Forward to a traffic class on the device
+ * @vport: vport structure
+ * @tc: traffic class index on the device
+ * @filter: pointer to cloud filter structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_handle_tclass(struct iecm_vport *vport, int tc,
+			      struct iecm_cloud_filter *filter)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (tc == 0)
+		return 0;
+	if ((!iecm_is_adq_v2_ena(vport)) &&
+	    !filter->f.data.tcp_spec.dst_port) {
+		dev_err(&adapter->pdev->dev,
+			"Specify destination port to redirect to traffic class other than TC0\n");
+		return -EINVAL;
+	}
+	/* redirect to a traffic class on the same device */
+	filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
+	filter->f.action_meta = tc;
+	return 0;
+}
+
+/* iecm_find_cf - Find the cloud filter in the list
+ * @vport: vport structure
+ * @cookie: filter specific cookie
+ *
+ * Returns pointer to the filter object or NULL. Must be called while holding
+ * cloud_filter_list_lock.
+ */
+static struct iecm_cloud_filter *iecm_find_cf(struct iecm_vport *vport,
+					      unsigned long *cookie)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter *filter = NULL;
+
+	if (!cookie)
+		return NULL;
+
+	list_for_each_entry(filter,
+			    &adapter->config_data.cf_config.cloud_filter_list,
+			    list) {
+		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
+			return filter;
+	}
+	return NULL;
+}
+
+/**
+ * iecm_configure_clsflower - Add tc flower filters
+ * @vport: vport structure
+ * @cls_flower: Pointer to struct flow_cls_offload
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_configure_clsflower(struct iecm_vport *vport,
+				    struct flow_cls_offload *cls_flower)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_user_config_data *config_data;
+	struct iecm_cloud_filter *filter = NULL;
+	int err;
+	int tc;
+
+	config_data = &adapter->config_data;
+	tc = tc_classid_to_hwtc(vport->netdev, cls_flower->classid);
+	if (tc < 0) {
+		dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
+		return -EINVAL;
+	}
+
+#define IECM_MAX_CLOUD_ADQ_FILTERS	128
+
+	if (config_data->cf_config.num_cloud_filters >=
+						IECM_MAX_CLOUD_ADQ_FILTERS) {
+		dev_err(&adapter->pdev->dev,
+			"Unable to add filter (action is forward to TC), reached max allowed filters (%u)\n",
+			IECM_MAX_CLOUD_ADQ_FILTERS);
+		return -ENOSPC;
+	}
+
+	/* bail out here if filter already exists */
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	filter = iecm_find_cf(vport, &cls_flower->cookie);
+	if (filter) {
+		filter->remove = false;
+		dev_err(&adapter->pdev->dev, "Failed to add TC Flower filter, it already exists\n");
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		return -EEXIST;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+	if (!filter)
+		return -ENOMEM;
+
+	filter->cookie = cls_flower->cookie;
+
+	/* set the mask to all zeroes to begin with */
+	memset(&filter->f.mask.tcp_spec, 0, sizeof(struct virtchnl_l4_spec));
+
+	/* start out with flow type and eth type IPv4 to begin with */
+	filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
+	err = iecm_parse_cls_flower(vport, cls_flower, filter);
+	if (err)
+		goto error;
+
+	err = iecm_handle_tclass(vport, tc, filter);
+	if (err)
+		goto error;
+
+	/* add filter to the list */
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	list_add_tail(&filter->list, &config_data->cf_config.cloud_filter_list);
+	filter->add = true;
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+	err = iecm_send_add_del_cloud_filter_msg(vport, true);
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	/* We have to find it again in case another thread has already
+	 * deleted and kfreed it.
+	 */
+	filter = iecm_find_cf(vport, &cls_flower->cookie);
+	if (filter && err) {
+		list_del(&filter->list);
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		goto error;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+	config_data->cf_config.num_cloud_filters++;
+error:
+	if (err)
+		kfree(filter);
+	return err;
+}
+
+/**
+ * iecm_delete_clsflower - Remove tc flower filters
+ * @vport: vport structure
+ * @cls_flower: Pointer to struct flow_cls_offload
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_delete_clsflower(struct iecm_vport *vport,
+				 struct flow_cls_offload *cls_flower)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter *filter = NULL;
+	int err = 0;
+
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	filter = iecm_find_cf(vport, &cls_flower->cookie);
+	if (filter) {
+		filter->remove = true;
+		adapter->config_data.cf_config.num_cloud_filters--;
+	} else if (adapter->config_data.cf_config.num_cloud_filters) {
+		/* "num_cloud_filters" can become zero if egress qdisc is
+		 * detached as per design, driver deletes related filters
+		 * when qdisc is detached to avoid stale filters, hence
+		 * num_cloud_filters can become zero. But since netdev
+		 * layer doesn't know that filters are deleted by driver
+		 * implictly when egress qdisc is deleted, it sees filters
+		 * being present and "in_hw". User can request delete
+		 * of specific filter of detach ingress qdisc - in either of
+		 * those operation, filter(s) won't be found in driver cache,
+		 * hence instead of returning, let this function return SUCCESS
+		 * Returning of err as -EINVAL is only applicable when
+		 * unable to find filter and num_cloud_filters is non-zero
+		 */
+		err = -EINVAL;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+	if (filter) {
+		err = iecm_send_add_del_cloud_filter_msg(vport, false);
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		/* It can happen that asynchronously the filter was already
+		 * deleted from the list. Make sure it's still there and marked
+		 * for remove under spinlock before actually trying to delete
+		 * from list.
+		 */
+		filter = iecm_find_cf(vport, &cls_flower->cookie);
+		if (filter) {
+			list_del(&filter->list);
+			kfree(filter);
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+	}
+	return err;
+}
+
+/**
+ * iecm_setup_tc_cls_flower - flower classifier offloads
+ * @vport: vport structure
+ * @cls_flower: pointer to struct flow_cls_offload
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_setup_tc_cls_flower(struct iecm_vport *vport,
+				    struct flow_cls_offload *cls_flower)
+{
+	if (cls_flower->common.chain_index)
+		return -EOPNOTSUPP;
+
+	switch (cls_flower->command) {
+	case FLOW_CLS_REPLACE:
+		return iecm_configure_clsflower(vport, cls_flower);
+	case FLOW_CLS_DESTROY:
+		return iecm_delete_clsflower(vport, cls_flower);
+	case FLOW_CLS_STATS:
+		return -EOPNOTSUPP;
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * iecm_setup_tc_block_cb - block callback for tc
+ * @type: type of offload
+ * @type_data: offload data
+ * @cb_priv: Private adapter structure
+ *
+ * This function is the block callback for traffic classes
+ * Return 0 on success, negative on failure
+ **/
+static int iecm_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
+				  void *cb_priv)
+{
+	switch (type) {
+	case TC_SETUP_CLSFLOWER:
+		return iecm_setup_tc_cls_flower((struct iecm_vport *)cb_priv,
+						(struct flow_cls_offload *)
+						 type_data);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/**
+ * iecm_del_all_cloud_filters - delete all cloud filters on the traffic classes
+ * @vport: vport structure
+ *
+ * This function will loop through the list of cloud filters and deletes them.
+ **/
+static void iecm_del_all_cloud_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+	struct iecm_cloud_filter *cf, *cftmp;
+
+	cf_config = &adapter->config_data.cf_config;
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	list_for_each_entry_safe(cf, cftmp,
+				 &cf_config->cloud_filter_list,
+				 list) {
+		list_del(&cf->list);
+		kfree(cf);
+		cf_config->num_cloud_filters--;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+}
+
 /**
  * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
  * @vport: vport structure
@@ -2596,6 +3480,7 @@  static int __iecm_setup_tc(struct iecm_vport *vport, void *type_data)
 			netif_tx_stop_all_queues(netdev);
 			netif_tx_disable(netdev);
 			ret = iecm_send_disable_channels_msg(vport);
+			iecm_del_all_cloud_filters(vport);
 			netif_tx_start_all_queues(netdev);
 			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags) &&
 			    !ret) {
@@ -2709,8 +3594,10 @@  static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
 {
 	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
 	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
 	int err = 0;
 
+	cf_config = &adapter->config_data.cf_config;
 	switch (type) {
 	case TC_SETUP_QDISC_ETF:
 		if (iecm_is_queue_model_split(vport->txq_model))
@@ -2720,6 +3607,17 @@  static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
 					     type_data);
 		break;
 	case TC_SETUP_BLOCK:
+		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				    VIRTCHNL2_CAP_ADQ)) {
+			err =
+			flow_block_cb_setup_simple((struct flow_block_offload *)
+						    type_data,
+						   &cf_config->block_cb_list,
+						   iecm_setup_tc_block_cb,
+						   vport, vport, true);
+		}
 		break;
 	case TC_SETUP_QDISC_MQPRIO:
 		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index 5601846b4674..94af45c36866 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -2731,6 +2731,74 @@  static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
 	return err;
 }
 
+/**
+ * iecm_send_add_del_cloud_filter_msg: Send add/del cloud filter message
+ * @vport: vport structure
+ * @add: True to add, false to delete cloud filter
+ *
+ * Request the CP/PF to add/del cloud filters as specified by the user via
+ * tc tool
+ *
+ * Return 0 on success, negative on failure
+ **/
+int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+	struct iecm_cloud_filter *cf;
+	struct virtchnl_filter f;
+	int len = 0, err = 0;
+
+	cf_config = &adapter->config_data.cf_config;
+
+	while (true) {
+		bool process_fltr = false;
+
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
+			if (add && cf->add) {
+				process_fltr = true;
+				cf->add = false;
+				f = cf->f;
+				break;
+			} else if (!add && cf->remove) {
+				process_fltr = true;
+				cf->remove = false;
+				f = cf->f;
+				break;
+			}
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+		/* Don't send mailbox message when there are no filters to add/del */
+		if (!process_fltr)
+			goto error;
+
+		if (add) {
+			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_CLOUD_FILTER,
+					       len, (u8 *)&f);
+			if (err)
+				goto error;
+
+			err = iecm_wait_for_event(adapter, IECM_VC_ADD_CLOUD_FILTER,
+						  IECM_VC_ADD_CLOUD_FILTER_ERR);
+		} else {
+			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_CLOUD_FILTER,
+					       len, (u8 *)&f);
+			if (err)
+				goto error;
+
+			err =
+			iecm_min_wait_for_event(adapter, IECM_VC_DEL_CLOUD_FILTER,
+						IECM_VC_DEL_CLOUD_FILTER_ERR);
+		}
+		if (err)
+			break;
+	}
+error:
+	return err;
+}
+
 /**
  * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
  * @vport: vport structure
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index b0785684cc63..0aab41cf982c 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -403,6 +403,28 @@  enum iecm_user_flags {
 	__IECM_USER_FLAGS_NBITS,
 };
 
+#define IECM_CLOUD_FIELD_OMAC		BIT(0)
+#define IECM_CLOUD_FIELD_IMAC		BIT(1)
+#define IECM_CLOUD_FIELD_IVLAN		BIT(2)
+#define IECM_CLOUD_FIELD_TEN_ID		BIT(3)
+#define IECM_CLOUD_FIELD_IIP		BIT(4)
+
+#define IECM_START_CHNL_TC		1
+
+struct iecm_cloud_filter {
+	struct list_head list;
+	struct virtchnl_filter f;
+	unsigned long cookie;
+	bool remove;		/* filter needs to be deleted */
+	bool add;		/* filter needs to be added */
+};
+
+struct iecm_cloud_filter_config {
+	struct list_head block_cb_list;		/* need to pass this to stack */
+	struct list_head cloud_filter_list;
+	u16 num_cloud_filters;
+};
+
 struct iecm_channel_config {
 	struct virtchnl_channel_info ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
 	bool tc_running;
@@ -536,6 +558,7 @@  struct iecm_ptype_state {
 	bool outer_frag;
 	u8 tunnel_state;
 };
+
 /* User defined configuration values */
 struct iecm_user_config_data {
 	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
@@ -550,6 +573,7 @@  struct iecm_user_config_data {
 	struct list_head vlan_filter_list;
 	struct list_head adv_rss_list;
 	struct iecm_fdir_fltr_config fdir_config;
+	struct iecm_cloud_filter_config cf_config;
 	struct iecm_channel_config ch_config;
 };
 
@@ -853,6 +877,7 @@  void iecm_set_ethtool_ops(struct net_device *netdev);
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
 int iecm_set_promiscuous(struct iecm_adapter *adapter);
+int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add);
 int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
 int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
 int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,