All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-01  0:36 ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:36 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

This patch series enables configuring cloud filters in i40e
using the tc/flower classifier. The only tc-filter action
supported is to redirect packets to a traffic class on the
same device. The tc/mirred:redirect action is extended to
accept a traffic class to achieve this.

The cloud filters are added for a VSI and are cleaned up when
the VSI is deleted. The filters that match on L4 ports needs
enhanced admin queue functions with big buffer support for
extended general fields in Add/Remove Cloud filters command.

Example:
# tc qdisc add dev eth0 ingress

# ethtool -K eth0 hw-tc-offload on

# tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1

# tc filter show dev eth0 parent ffff:
filter protocol ip pref 1 flower
filter protocol ip pref 1 flower handle 0x1
  indev eth0
  eth_type ipv4
  ip_proto udp
  dst_ip 192.168.1.1
  dst_port 22
  skip_sw
  in_hw
        action order 1: mirred (Ingress Redirect to device eth0) stolen tc 1
        index 1 ref 1 bind 1
---

Amritha Nambiar (6):
      [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
      [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids
      [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
      [net-next]net: i40e: Admin queue definitions for cloud filters
      [net-next]net: i40e: Clean up of cloud filters
      [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier


 drivers/net/ethernet/intel/i40e/i40e.h            |   58 +
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |  132 +++
 drivers/net/ethernet/intel/i40e/i40e_common.c     |  184 ++++
 drivers/net/ethernet/intel/i40e/i40e_ethtool.c    |    2 
 drivers/net/ethernet/intel/i40e/i40e_main.c       |  983 +++++++++++++++++++++
 drivers/net/ethernet/intel/i40e/i40e_prototype.h  |   19 
 drivers/net/ethernet/intel/i40e/i40e_type.h       |    9 
 include/net/tc_act/tc_mirred.h                    |    7 
 include/uapi/linux/tc_act/tc_mirred.h             |    5 
 net/sched/act_mirred.c                            |   17 
 10 files changed, 1408 insertions(+), 8 deletions(-)

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-01  0:36 ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:36 UTC (permalink / raw)
  To: intel-wired-lan

This patch series enables configuring cloud filters in i40e
using the tc/flower classifier. The only tc-filter action
supported is to redirect packets to a traffic class on the
same device. The tc/mirred:redirect action is extended to
accept a traffic class to achieve this.

The cloud filters are added for a VSI and are cleaned up when
the VSI is deleted. The filters that match on L4 ports needs
enhanced admin queue functions with big buffer support for
extended general fields in Add/Remove Cloud filters command.

Example:
# tc qdisc add dev eth0 ingress

# ethtool -K eth0 hw-tc-offload on

# tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1

# tc filter show dev eth0 parent ffff:
filter protocol ip pref 1 flower
filter protocol ip pref 1 flower handle 0x1
  indev eth0
  eth_type ipv4
  ip_proto udp
  dst_ip 192.168.1.1
  dst_port 22
  skip_sw
  in_hw
        action order 1: mirred (Ingress Redirect to device eth0) stolen tc 1
        index 1 ref 1 bind 1
---

Amritha Nambiar (6):
      [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
      [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids
      [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
      [net-next]net: i40e: Admin queue definitions for cloud filters
      [net-next]net: i40e: Clean up of cloud filters
      [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier


 drivers/net/ethernet/intel/i40e/i40e.h            |   58 +
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |  132 +++
 drivers/net/ethernet/intel/i40e/i40e_common.c     |  184 ++++
 drivers/net/ethernet/intel/i40e/i40e_ethtool.c    |    2 
 drivers/net/ethernet/intel/i40e/i40e_main.c       |  983 +++++++++++++++++++++
 drivers/net/ethernet/intel/i40e/i40e_prototype.h  |   19 
 drivers/net/ethernet/intel/i40e/i40e_type.h       |    9 
 include/net/tc_act/tc_mirred.h                    |    7 
 include/uapi/linux/tc_act/tc_mirred.h             |    5 
 net/sched/act_mirred.c                            |   17 
 10 files changed, 1408 insertions(+), 8 deletions(-)

--

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01  0:37   ` Amritha Nambiar
  -1 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

The Mirred/redirect action is extended to forward to a traffic
class on the device. The traffic class index needs to be
provided in addition to the device's ifindex.

Example:
# tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 include/net/tc_act/tc_mirred.h        |    7 +++++++
 include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
 net/sched/act_mirred.c                |   17 +++++++++++++++++
 3 files changed, 29 insertions(+)

diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
index 604bc31..60058c4 100644
--- a/include/net/tc_act/tc_mirred.h
+++ b/include/net/tc_act/tc_mirred.h
@@ -9,6 +9,8 @@ struct tcf_mirred {
 	int			tcfm_eaction;
 	int			tcfm_ifindex;
 	bool			tcfm_mac_header_xmit;
+	u8			tcfm_tc;
+	u32			flags;
 	struct net_device __rcu	*tcfm_dev;
 	struct list_head	tcfm_list;
 };
@@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
 	return to_mirred(a)->tcfm_ifindex;
 }
 
+static inline int tcf_mirred_tc(const struct tc_action *a)
+{
+	return to_mirred(a)->tcfm_tc;
+}
+
 #endif /* __NET_TC_MIR_H */
diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
index 3d7a2b3..8ff4d76 100644
--- a/include/uapi/linux/tc_act/tc_mirred.h
+++ b/include/uapi/linux/tc_act/tc_mirred.h
@@ -9,6 +9,10 @@
 #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
 #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
 #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
+
+#define MIRRED_F_TC_MAP		0x1
+#define MIRRED_TC_MAP_MAX	0x10
+#define MIRRED_TC_MAP_MASK	0xF
                                                                                 
 struct tc_mirred {
 	tc_gen;
@@ -21,6 +25,7 @@ enum {
 	TCA_MIRRED_TM,
 	TCA_MIRRED_PARMS,
 	TCA_MIRRED_PAD,
+	TCA_MIRRED_TC_MAP,
 	__TCA_MIRRED_MAX
 };
 #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 1b5549a..f9801de 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
 
 static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
 	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
+	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
 };
 
 static unsigned int mirred_net_id;
@@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 	struct tcf_mirred *m;
 	struct net_device *dev;
 	bool exists = false;
+	u8 *tc_map = NULL;
+	u32 flags = 0;
 	int ret;
 
 	if (nla == NULL)
@@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 		return ret;
 	if (tb[TCA_MIRRED_PARMS] == NULL)
 		return -EINVAL;
+
+	if (tb[TCA_MIRRED_TC_MAP]) {
+		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
+		if (*tc_map >= MIRRED_TC_MAP_MAX)
+			return -EINVAL;
+		flags |= MIRRED_F_TC_MAP;
+	}
+
 	parm = nla_data(tb[TCA_MIRRED_PARMS]);
 
 	exists = tcf_hash_check(tn, parm->index, a, bind);
@@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 	ASSERT_RTNL();
 	m->tcf_action = parm->action;
 	m->tcfm_eaction = parm->eaction;
+	m->flags = flags;
 	if (dev != NULL) {
 		m->tcfm_ifindex = parm->ifindex;
 		if (ret != ACT_P_CREATED)
@@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 		dev_hold(dev);
 		rcu_assign_pointer(m->tcfm_dev, dev);
 		m->tcfm_mac_header_xmit = mac_header_xmit;
+		if (flags & MIRRED_F_TC_MAP)
+			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
 	}
 
 	if (ret == ACT_P_CREATED) {
@@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
 
 	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
 		goto nla_put_failure;
+	if ((m->flags & MIRRED_F_TC_MAP) &&
+	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
+		goto nla_put_failure;
 
 	tcf_tm_dump(&t, &m->tcf_tm);
 	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-01  0:37   ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan

The Mirred/redirect action is extended to forward to a traffic
class on the device. The traffic class index needs to be
provided in addition to the device's ifindex.

Example:
# tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 include/net/tc_act/tc_mirred.h        |    7 +++++++
 include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
 net/sched/act_mirred.c                |   17 +++++++++++++++++
 3 files changed, 29 insertions(+)

diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
index 604bc31..60058c4 100644
--- a/include/net/tc_act/tc_mirred.h
+++ b/include/net/tc_act/tc_mirred.h
@@ -9,6 +9,8 @@ struct tcf_mirred {
 	int			tcfm_eaction;
 	int			tcfm_ifindex;
 	bool			tcfm_mac_header_xmit;
+	u8			tcfm_tc;
+	u32			flags;
 	struct net_device __rcu	*tcfm_dev;
 	struct list_head	tcfm_list;
 };
@@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
 	return to_mirred(a)->tcfm_ifindex;
 }
 
+static inline int tcf_mirred_tc(const struct tc_action *a)
+{
+	return to_mirred(a)->tcfm_tc;
+}
+
 #endif /* __NET_TC_MIR_H */
diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
index 3d7a2b3..8ff4d76 100644
--- a/include/uapi/linux/tc_act/tc_mirred.h
+++ b/include/uapi/linux/tc_act/tc_mirred.h
@@ -9,6 +9,10 @@
 #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
 #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
 #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
+
+#define MIRRED_F_TC_MAP		0x1
+#define MIRRED_TC_MAP_MAX	0x10
+#define MIRRED_TC_MAP_MASK	0xF
                                                                                 
 struct tc_mirred {
 	tc_gen;
@@ -21,6 +25,7 @@ enum {
 	TCA_MIRRED_TM,
 	TCA_MIRRED_PARMS,
 	TCA_MIRRED_PAD,
+	TCA_MIRRED_TC_MAP,
 	__TCA_MIRRED_MAX
 };
 #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 1b5549a..f9801de 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
 
 static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
 	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
+	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
 };
 
 static unsigned int mirred_net_id;
@@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 	struct tcf_mirred *m;
 	struct net_device *dev;
 	bool exists = false;
+	u8 *tc_map = NULL;
+	u32 flags = 0;
 	int ret;
 
 	if (nla == NULL)
@@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 		return ret;
 	if (tb[TCA_MIRRED_PARMS] == NULL)
 		return -EINVAL;
+
+	if (tb[TCA_MIRRED_TC_MAP]) {
+		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
+		if (*tc_map >= MIRRED_TC_MAP_MAX)
+			return -EINVAL;
+		flags |= MIRRED_F_TC_MAP;
+	}
+
 	parm = nla_data(tb[TCA_MIRRED_PARMS]);
 
 	exists = tcf_hash_check(tn, parm->index, a, bind);
@@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 	ASSERT_RTNL();
 	m->tcf_action = parm->action;
 	m->tcfm_eaction = parm->eaction;
+	m->flags = flags;
 	if (dev != NULL) {
 		m->tcfm_ifindex = parm->ifindex;
 		if (ret != ACT_P_CREATED)
@@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 		dev_hold(dev);
 		rcu_assign_pointer(m->tcfm_dev, dev);
 		m->tcfm_mac_header_xmit = mac_header_xmit;
+		if (flags & MIRRED_F_TC_MAP)
+			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
 	}
 
 	if (ret == ACT_P_CREATED) {
@@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
 
 	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
 		goto nla_put_failure;
+	if ((m->flags & MIRRED_F_TC_MAP) &&
+	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
+		goto nla_put_failure;
 
 	tcf_tm_dump(&t, &m->tcf_tm);
 	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH 2/6] [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01  0:37   ` Amritha Nambiar
  -1 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

Add mapping of TCs with the seids of the channel VSIs. TC0
will be mapped to the main VSI seid and all other TCs are
mapped to the seid of the channel VSI.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |    1 +
 drivers/net/ethernet/intel/i40e/i40e_main.c |    2 ++
 2 files changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 8852ac0..1391e5d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -738,6 +738,7 @@ struct i40e_vsi {
 	atomic_t next_base_queue;
 
 	struct list_head ch_list;
+	u16 tc_seid_map[I40E_MAX_TRAFFIC_CLASS];
 
 	void *priv;	/* client driver data reference. */
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 370ce9f..1daf95e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -6127,6 +6127,7 @@ static int i40e_configure_queue_channels(struct i40e_vsi *vsi)
 	int ret = 0, i;
 
 	/* Create app vsi with the TCs. Main VSI with TC0 is already set up */
+	vsi->tc_seid_map[0] = vsi->seid;
 	for (i = 1; i < I40E_MAX_TRAFFIC_CLASS; i++)
 		if (vsi->tc_config.enabled_tc & BIT(i)) {
 			ch = kzalloc(sizeof(*ch), GFP_KERNEL);
@@ -6156,6 +6157,7 @@ static int i40e_configure_queue_channels(struct i40e_vsi *vsi)
 					i, ch->num_queue_pairs);
 				goto err_free;
 			}
+			vsi->tc_seid_map[i] = ch->seid;
 		}
 	return ret;
 

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 2/6] [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids
@ 2017-08-01  0:37   ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan

Add mapping of TCs with the seids of the channel VSIs. TC0
will be mapped to the main VSI seid and all other TCs are
mapped to the seid of the channel VSI.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |    1 +
 drivers/net/ethernet/intel/i40e/i40e_main.c |    2 ++
 2 files changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 8852ac0..1391e5d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -738,6 +738,7 @@ struct i40e_vsi {
 	atomic_t next_base_queue;
 
 	struct list_head ch_list;
+	u16 tc_seid_map[I40E_MAX_TRAFFIC_CLASS];
 
 	void *priv;	/* client driver data reference. */
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 370ce9f..1daf95e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -6127,6 +6127,7 @@ static int i40e_configure_queue_channels(struct i40e_vsi *vsi)
 	int ret = 0, i;
 
 	/* Create app vsi with the TCs. Main VSI with TC0 is already set up */
+	vsi->tc_seid_map[0] = vsi->seid;
 	for (i = 1; i < I40E_MAX_TRAFFIC_CLASS; i++)
 		if (vsi->tc_config.enabled_tc & BIT(i)) {
 			ch = kzalloc(sizeof(*ch), GFP_KERNEL);
@@ -6156,6 +6157,7 @@ static int i40e_configure_queue_channels(struct i40e_vsi *vsi)
 					i, ch->num_queue_pairs);
 				goto err_free;
 			}
+			vsi->tc_seid_map[i] = ch->seid;
 		}
 	return ret;
 


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH 3/6] [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01  0:37   ` Amritha Nambiar
  -1 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

Add definitions for L4 filters and switch modes based on cloud filters
modes and extend the set switch config command to include the
additional cloud filter mode.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   34 ++++++++++++++++++++-
 drivers/net/ethernet/intel/i40e/i40e_common.c     |    4 ++
 drivers/net/ethernet/intel/i40e/i40e_ethtool.c    |    2 +
 drivers/net/ethernet/intel/i40e/i40e_main.c       |    2 +
 drivers/net/ethernet/intel/i40e/i40e_prototype.h  |    2 +
 drivers/net/ethernet/intel/i40e/i40e_type.h       |    9 ++++++
 6 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index e2a9ec8..8bba04c 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -773,7 +773,39 @@ struct i40e_aqc_set_switch_config {
 #define I40E_AQ_SET_SWITCH_CFG_PROMISC		0x0001
 #define I40E_AQ_SET_SWITCH_CFG_L2_FILTER	0x0002
 	__le16	valid_flags;
-	u8	reserved[12];
+
+	u8	rsvd6[6];
+
+	/* Next byte is split into following:
+	 * Bit 7 : 0: No action, 1: Switch to mode defined by bits 6:0
+	 * Bit 6: 0 : Destination Port, 1: source port
+	 * Bit 5..4: L4 type
+	 *  0: rsvd
+	 *  1: TCP
+	 *  2: UDP
+	 *  3: Both TCP and UDP
+	 * Bits 3:0 Mode
+	 *  0: default mode
+	 *  1: L4 port only mode
+	 *  2: non-tunneled mode
+	 *  3: tunneled mode
+	 */
+#define I40E_AQ_SET_SWITCH_BIT7_VALID		0x80
+
+#define I40E_AQ_SET_SWITCH_L4_SRC_PORT		0x40
+
+#define I40E_AQ_SET_SWITCH_L4_TYPE_RSVD		0x00
+#define I40E_AQ_SET_SWITCH_L4_TYPE_TCP		0x10
+#define I40E_AQ_SET_SWITCH_L4_TYPE_UDP		0x20
+#define I40E_AQ_SET_SWITCH_L4_TYPE_BOTH		0x30
+
+#define I40E_AQ_SET_SWITCH_MODE_DEFAULT		0x00
+#define I40E_AQ_SET_SWITCH_MODE_L4_PORT		0x01
+#define I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL	0x02
+#define I40E_AQ_SET_SWITCH_MODE_TUNNEL		0x03
+	u8 mode;
+
+	u8	rsvd5[5];
 };
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_switch_config);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index e4e86e0..d0e8138 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -2380,13 +2380,14 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw,
  * @hw: pointer to the hardware structure
  * @flags: bit flag values to set
  * @valid_flags: which bit flags to set
+ * @mode: cloud filter mode
  * @cmd_details: pointer to command details structure or NULL
  *
  * Set switch configuration bits
  **/
 enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
 						u16 flags,
-						u16 valid_flags,
+						u16 valid_flags, u8 mode,
 				struct i40e_asq_cmd_details *cmd_details)
 {
 	struct i40e_aq_desc desc;
@@ -2398,6 +2399,7 @@ enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
 					  i40e_aqc_opc_set_switch_config);
 	scfg->flags = cpu_to_le16(flags);
 	scfg->valid_flags = cpu_to_le16(valid_flags);
+	scfg->mode = mode;
 
 	status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 326fc18..232e066e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -4181,7 +4181,7 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
 			sw_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
 		valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
 		ret = i40e_aq_set_switch_config(&pf->hw, sw_flags, valid_flags,
-						NULL);
+						0, NULL);
 		if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) {
 			dev_info(&pf->pdev->dev,
 				 "couldn't set switch config bits, err %s aq_err %s\n",
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 1daf95e..fdddd74 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12107,7 +12107,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 		u16 valid_flags;
 
 		valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
-		ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags,
+		ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags, 0,
 						NULL);
 		if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) {
 			dev_info(&pf->pdev->dev,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index df613ea..9142d0d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -190,7 +190,7 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw,
 				struct i40e_asq_cmd_details *cmd_details);
 enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
 						u16 flags,
-						u16 valid_flags,
+						u16 valid_flags, u8 mode,
 				struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_request_resource(struct i40e_hw *hw,
 				enum i40e_aq_resources_ids resource,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index fd4bbdd..0cfc89e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -276,6 +276,15 @@ struct i40e_hw_capabilities {
 #define I40E_NVM_IMAGE_TYPE_CLOUD	0x2
 #define I40E_NVM_IMAGE_TYPE_UDP_CLOUD	0x3
 
+	/* Cloud filter modes:
+	 * Mode1: Filter on L4 port only
+	 * Mode2: Filter for non-tunneled traffic
+	 * Mode3: Filter for tunnel traffic
+	 */
+#define I40E_NVM_IMAGE_TYPE_MODE1	0x6
+#define I40E_NVM_IMAGE_TYPE_MODE2	0x7
+#define I40E_NVM_IMAGE_TYPE_MODE3	0x8
+
 	u32  management_mode;
 	u32  mng_protocols_over_mctp;
 #define I40E_MNG_PROTOCOL_PLDM		0x2

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 3/6] [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
@ 2017-08-01  0:37   ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan

Add definitions for L4 filters and switch modes based on cloud filters
modes and extend the set switch config command to include the
additional cloud filter mode.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   34 ++++++++++++++++++++-
 drivers/net/ethernet/intel/i40e/i40e_common.c     |    4 ++
 drivers/net/ethernet/intel/i40e/i40e_ethtool.c    |    2 +
 drivers/net/ethernet/intel/i40e/i40e_main.c       |    2 +
 drivers/net/ethernet/intel/i40e/i40e_prototype.h  |    2 +
 drivers/net/ethernet/intel/i40e/i40e_type.h       |    9 ++++++
 6 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index e2a9ec8..8bba04c 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -773,7 +773,39 @@ struct i40e_aqc_set_switch_config {
 #define I40E_AQ_SET_SWITCH_CFG_PROMISC		0x0001
 #define I40E_AQ_SET_SWITCH_CFG_L2_FILTER	0x0002
 	__le16	valid_flags;
-	u8	reserved[12];
+
+	u8	rsvd6[6];
+
+	/* Next byte is split into following:
+	 * Bit 7 : 0: No action, 1: Switch to mode defined by bits 6:0
+	 * Bit 6: 0 : Destination Port, 1: source port
+	 * Bit 5..4: L4 type
+	 *  0: rsvd
+	 *  1: TCP
+	 *  2: UDP
+	 *  3: Both TCP and UDP
+	 * Bits 3:0 Mode
+	 *  0: default mode
+	 *  1: L4 port only mode
+	 *  2: non-tunneled mode
+	 *  3: tunneled mode
+	 */
+#define I40E_AQ_SET_SWITCH_BIT7_VALID		0x80
+
+#define I40E_AQ_SET_SWITCH_L4_SRC_PORT		0x40
+
+#define I40E_AQ_SET_SWITCH_L4_TYPE_RSVD		0x00
+#define I40E_AQ_SET_SWITCH_L4_TYPE_TCP		0x10
+#define I40E_AQ_SET_SWITCH_L4_TYPE_UDP		0x20
+#define I40E_AQ_SET_SWITCH_L4_TYPE_BOTH		0x30
+
+#define I40E_AQ_SET_SWITCH_MODE_DEFAULT		0x00
+#define I40E_AQ_SET_SWITCH_MODE_L4_PORT		0x01
+#define I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL	0x02
+#define I40E_AQ_SET_SWITCH_MODE_TUNNEL		0x03
+	u8 mode;
+
+	u8	rsvd5[5];
 };
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_switch_config);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index e4e86e0..d0e8138 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -2380,13 +2380,14 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw,
  * @hw: pointer to the hardware structure
  * @flags: bit flag values to set
  * @valid_flags: which bit flags to set
+ * @mode: cloud filter mode
  * @cmd_details: pointer to command details structure or NULL
  *
  * Set switch configuration bits
  **/
 enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
 						u16 flags,
-						u16 valid_flags,
+						u16 valid_flags, u8 mode,
 				struct i40e_asq_cmd_details *cmd_details)
 {
 	struct i40e_aq_desc desc;
@@ -2398,6 +2399,7 @@ enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
 					  i40e_aqc_opc_set_switch_config);
 	scfg->flags = cpu_to_le16(flags);
 	scfg->valid_flags = cpu_to_le16(valid_flags);
+	scfg->mode = mode;
 
 	status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 326fc18..232e066e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -4181,7 +4181,7 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
 			sw_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
 		valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
 		ret = i40e_aq_set_switch_config(&pf->hw, sw_flags, valid_flags,
-						NULL);
+						0, NULL);
 		if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) {
 			dev_info(&pf->pdev->dev,
 				 "couldn't set switch config bits, err %s aq_err %s\n",
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 1daf95e..fdddd74 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12107,7 +12107,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 		u16 valid_flags;
 
 		valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
-		ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags,
+		ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags, 0,
 						NULL);
 		if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) {
 			dev_info(&pf->pdev->dev,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index df613ea..9142d0d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -190,7 +190,7 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw,
 				struct i40e_asq_cmd_details *cmd_details);
 enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
 						u16 flags,
-						u16 valid_flags,
+						u16 valid_flags, u8 mode,
 				struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_request_resource(struct i40e_hw *hw,
 				enum i40e_aq_resources_ids resource,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index fd4bbdd..0cfc89e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -276,6 +276,15 @@ struct i40e_hw_capabilities {
 #define I40E_NVM_IMAGE_TYPE_CLOUD	0x2
 #define I40E_NVM_IMAGE_TYPE_UDP_CLOUD	0x3
 
+	/* Cloud filter modes:
+	 * Mode1: Filter on L4 port only
+	 * Mode2: Filter for non-tunneled traffic
+	 * Mode3: Filter for tunnel traffic
+	 */
+#define I40E_NVM_IMAGE_TYPE_MODE1	0x6
+#define I40E_NVM_IMAGE_TYPE_MODE2	0x7
+#define I40E_NVM_IMAGE_TYPE_MODE3	0x8
+
 	u32  management_mode;
 	u32  mng_protocols_over_mctp;
 #define I40E_MNG_PROTOCOL_PLDM		0x2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01  0:37   ` Amritha Nambiar
  -1 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

Add new admin queue definitions and extended fields for cloud
filter support. Define big buffer for extended general fields
in Add/Remove Cloud filters command.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
Signed-off-by: Store Laura <laura.stroe@intel.com>
Signed-off-by: Iremonger Bernard <bernard.iremonger@intel.com>
Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   98 +++++++++++++++++++++
 1 file changed, 97 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index 8bba04c..9f14305 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -1358,7 +1358,9 @@ struct i40e_aqc_add_remove_cloud_filters {
 #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT	0
 #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK	(0x3FF << \
 					I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT)
-	u8	reserved2[4];
+	u8	big_buffer_flag;
+#define	I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER	1
+	u8	reserved2[3];
 	__le32	addr_high;
 	__le32	addr_low;
 };
@@ -1395,6 +1397,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
 #define I40E_AQC_ADD_CLOUD_FILTER_IMAC			0x000A
 #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC	0x000B
 #define I40E_AQC_ADD_CLOUD_FILTER_IIP			0x000C
+/* 0x0010 to 0x0017 is for custom filters */
+/* flag to be used when adding cloud filter: IP + L4 Port */
+#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT		0x0010
+/* flag to be used when adding cloud filter: Dest MAC + L4 Port */
+#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT		0x0011
+/* flag to be used when adding cloud filter: Dest MAC + VLAN + L4 Port */
+#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT		0x0012
 
 #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE		0x0080
 #define I40E_AQC_ADD_CLOUD_VNK_SHIFT			6
@@ -1429,6 +1438,45 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
 	u8	response_reserved[7];
 };
 
+/* i40e_aqc_add_remove_cloud_filters_element_big_data is used when
+ * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set.
+ */
+struct i40e_aqc_add_remove_cloud_filters_element_big_data {
+	struct i40e_aqc_add_remove_cloud_filters_element_data element;
+	u16     general_fields[32];
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0	0
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1	1
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2	2
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0	3
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1	4
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2	5
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0	6
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1	7
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2	8
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0	9
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1	10
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2	11
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0	12
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1	13
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2	14
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0	15
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1	16
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2	17
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3	18
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4	19
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5	20
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6	21
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7	22
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0	23
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1	24
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2	25
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3	26
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4	27
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5	28
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6	29
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7	30
+};
+
 struct i40e_aqc_remove_cloud_filters_completion {
 	__le16 perfect_ovlan_used;
 	__le16 perfect_ovlan_free;
@@ -1440,6 +1488,54 @@ struct i40e_aqc_remove_cloud_filters_completion {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion);
 
+/* Replace filter Command 0x025F
+ * uses the i40e_aqc_replace_cloud_filters,
+ * and the generic indirect completion structure
+ */
+struct i40e_filter_data {
+	u8 filter_type;
+	u8 input[3];
+};
+
+struct i40e_aqc_replace_cloud_filters_cmd {
+	u8      valid_flags;
+#define I40E_AQC_REPLACE_L1_FILTER		0x0
+#define I40E_AQC_REPLACE_CLOUD_FILTER		0x1
+#define I40E_AQC_GET_CLOUD_FILTERS		0x2
+#define I40E_AQC_MIRROR_CLOUD_FILTER		0x4
+#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER	0x8
+	u8      old_filter_type;
+	u8      new_filter_type;
+	u8      tr_bit;
+	u8      reserved[4];
+	__le32 addr_high;
+	__le32 addr_low;
+};
+
+struct i40e_aqc_replace_cloud_filters_cmd_buf {
+	u8      data[32];
+/* Filter type INPUT codes*/
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX	3
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED	BIT(7)
+
+/* Field Vector offsets */
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA	0
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH	6
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG	7
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN	8
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN	9
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN	10
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY	11
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC	12
+/* big FLU */
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA	14
+/* big FLU */
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA	15
+
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN	37
+	struct i40e_filter_data filters[8];
+};
+
 /* Add Mirror Rule (indirect or direct 0x0260)
  * Delete Mirror Rule (indirect or direct 0x0261)
  * note: some rule types (4,5) do not use an external buffer.

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters
@ 2017-08-01  0:37   ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:37 UTC (permalink / raw)
  To: intel-wired-lan

Add new admin queue definitions and extended fields for cloud
filter support. Define big buffer for extended general fields
in Add/Remove Cloud filters command.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
Signed-off-by: Store Laura <laura.stroe@intel.com>
Signed-off-by: Iremonger Bernard <bernard.iremonger@intel.com>
Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   98 +++++++++++++++++++++
 1 file changed, 97 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index 8bba04c..9f14305 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -1358,7 +1358,9 @@ struct i40e_aqc_add_remove_cloud_filters {
 #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT	0
 #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK	(0x3FF << \
 					I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT)
-	u8	reserved2[4];
+	u8	big_buffer_flag;
+#define	I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER	1
+	u8	reserved2[3];
 	__le32	addr_high;
 	__le32	addr_low;
 };
@@ -1395,6 +1397,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
 #define I40E_AQC_ADD_CLOUD_FILTER_IMAC			0x000A
 #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC	0x000B
 #define I40E_AQC_ADD_CLOUD_FILTER_IIP			0x000C
+/* 0x0010 to 0x0017 is for custom filters */
+/* flag to be used when adding cloud filter: IP + L4 Port */
+#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT		0x0010
+/* flag to be used when adding cloud filter: Dest MAC + L4 Port */
+#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT		0x0011
+/* flag to be used when adding cloud filter: Dest MAC + VLAN + L4 Port */
+#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT		0x0012
 
 #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE		0x0080
 #define I40E_AQC_ADD_CLOUD_VNK_SHIFT			6
@@ -1429,6 +1438,45 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
 	u8	response_reserved[7];
 };
 
+/* i40e_aqc_add_remove_cloud_filters_element_big_data is used when
+ * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set.
+ */
+struct i40e_aqc_add_remove_cloud_filters_element_big_data {
+	struct i40e_aqc_add_remove_cloud_filters_element_data element;
+	u16     general_fields[32];
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0	0
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1	1
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2	2
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0	3
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1	4
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2	5
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0	6
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1	7
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2	8
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0	9
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1	10
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2	11
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0	12
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1	13
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2	14
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0	15
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1	16
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2	17
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3	18
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4	19
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5	20
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6	21
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7	22
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0	23
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1	24
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2	25
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3	26
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4	27
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5	28
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6	29
+#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7	30
+};
+
 struct i40e_aqc_remove_cloud_filters_completion {
 	__le16 perfect_ovlan_used;
 	__le16 perfect_ovlan_free;
@@ -1440,6 +1488,54 @@ struct i40e_aqc_remove_cloud_filters_completion {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion);
 
+/* Replace filter Command 0x025F
+ * uses the i40e_aqc_replace_cloud_filters,
+ * and the generic indirect completion structure
+ */
+struct i40e_filter_data {
+	u8 filter_type;
+	u8 input[3];
+};
+
+struct i40e_aqc_replace_cloud_filters_cmd {
+	u8      valid_flags;
+#define I40E_AQC_REPLACE_L1_FILTER		0x0
+#define I40E_AQC_REPLACE_CLOUD_FILTER		0x1
+#define I40E_AQC_GET_CLOUD_FILTERS		0x2
+#define I40E_AQC_MIRROR_CLOUD_FILTER		0x4
+#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER	0x8
+	u8      old_filter_type;
+	u8      new_filter_type;
+	u8      tr_bit;
+	u8      reserved[4];
+	__le32 addr_high;
+	__le32 addr_low;
+};
+
+struct i40e_aqc_replace_cloud_filters_cmd_buf {
+	u8      data[32];
+/* Filter type INPUT codes*/
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX	3
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED	BIT(7)
+
+/* Field Vector offsets */
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA	0
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH	6
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG	7
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN	8
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN	9
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN	10
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY	11
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC	12
+/* big FLU */
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA	14
+/* big FLU */
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA	15
+
+#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN	37
+	struct i40e_filter_data filters[8];
+};
+
 /* Add Mirror Rule (indirect or direct 0x0260)
  * Delete Mirror Rule (indirect or direct 0x0261)
  * note: some rule types (4,5) do not use an external buffer.


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH 5/6] [net-next]net: i40e: Clean up of cloud filters
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01  0:38   ` Amritha Nambiar
  -1 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:38 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

Introduce the cloud filter datastructure and cleanup of cloud
filters associated with the device.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |   11 +++++++++++
 drivers/net/ethernet/intel/i40e/i40e_main.c |   27 +++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 1391e5d..5c0cad5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -252,6 +252,14 @@ struct i40e_fdir_filter {
 	u32 fd_id;
 };
 
+struct i40e_cloud_filter {
+	struct hlist_node cloud_node;
+	/* cloud filter input set follows */
+	unsigned long cookie;
+	/* filter control */
+	u16 seid;
+};
+
 #define I40E_ETH_P_LLDP			0x88cc
 
 #define I40E_DCB_PRIO_TYPE_STRICT	0
@@ -419,6 +427,9 @@ struct i40e_pf {
 	struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
 	u16 pending_udp_bitmap;
 
+	struct hlist_head cloud_filter_list;
+	u16 num_cloud_filters;
+
 	enum i40e_interrupt_policy int_policy;
 	u16 rx_itr_default;
 	u16 tx_itr_default;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index fdddd74..93f6fe2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -6928,6 +6928,29 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
 }
 
 /**
+ * i40e_cloud_filter_exit - Cleans up the Cloud Filters
+ * @pf: Pointer to PF
+ *
+ * This function destroys the hlist where all the Cloud Filters
+ * filters were saved.
+ **/
+static void i40e_cloud_filter_exit(struct i40e_pf *pf)
+{
+	struct i40e_cloud_filter *cfilter;
+	struct hlist_node *node;
+
+	if (hlist_empty(&pf->cloud_filter_list))
+		return;
+
+	hlist_for_each_entry_safe(cfilter, node,
+				  &pf->cloud_filter_list, cloud_node) {
+		hlist_del(&cfilter->cloud_node);
+		kfree(cfilter);
+	}
+	pf->num_cloud_filters = 0;
+}
+
+/**
  * i40e_close - Disables a network interface
  * @netdev: network interface device structure
  *
@@ -12137,6 +12160,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 			vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]);
 		if (!vsi) {
 			dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n");
+			i40e_cloud_filter_exit(pf);
 			i40e_fdir_teardown(pf);
 			return -EAGAIN;
 		}
@@ -12961,6 +12985,8 @@ static void i40e_remove(struct pci_dev *pdev)
 	if (pf->vsi[pf->lan_vsi])
 		i40e_vsi_release(pf->vsi[pf->lan_vsi]);
 
+	i40e_cloud_filter_exit(pf);
+
 	/* remove attached clients */
 	if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
 		ret_code = i40e_lan_del_device(pf);
@@ -13170,6 +13196,7 @@ static void i40e_shutdown(struct pci_dev *pdev)
 
 	del_timer_sync(&pf->service_timer);
 	cancel_work_sync(&pf->service_task);
+	i40e_cloud_filter_exit(pf);
 	i40e_fdir_teardown(pf);
 
 	/* Client close must be called explicitly here because the timer

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 5/6] [net-next]net: i40e: Clean up of cloud filters
@ 2017-08-01  0:38   ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:38 UTC (permalink / raw)
  To: intel-wired-lan

Introduce the cloud filter datastructure and cleanup of cloud
filters associated with the device.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |   11 +++++++++++
 drivers/net/ethernet/intel/i40e/i40e_main.c |   27 +++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 1391e5d..5c0cad5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -252,6 +252,14 @@ struct i40e_fdir_filter {
 	u32 fd_id;
 };
 
+struct i40e_cloud_filter {
+	struct hlist_node cloud_node;
+	/* cloud filter input set follows */
+	unsigned long cookie;
+	/* filter control */
+	u16 seid;
+};
+
 #define I40E_ETH_P_LLDP			0x88cc
 
 #define I40E_DCB_PRIO_TYPE_STRICT	0
@@ -419,6 +427,9 @@ struct i40e_pf {
 	struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
 	u16 pending_udp_bitmap;
 
+	struct hlist_head cloud_filter_list;
+	u16 num_cloud_filters;
+
 	enum i40e_interrupt_policy int_policy;
 	u16 rx_itr_default;
 	u16 tx_itr_default;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index fdddd74..93f6fe2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -6928,6 +6928,29 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
 }
 
 /**
+ * i40e_cloud_filter_exit - Cleans up the Cloud Filters
+ * @pf: Pointer to PF
+ *
+ * This function destroys the hlist where all the Cloud Filters
+ * filters were saved.
+ **/
+static void i40e_cloud_filter_exit(struct i40e_pf *pf)
+{
+	struct i40e_cloud_filter *cfilter;
+	struct hlist_node *node;
+
+	if (hlist_empty(&pf->cloud_filter_list))
+		return;
+
+	hlist_for_each_entry_safe(cfilter, node,
+				  &pf->cloud_filter_list, cloud_node) {
+		hlist_del(&cfilter->cloud_node);
+		kfree(cfilter);
+	}
+	pf->num_cloud_filters = 0;
+}
+
+/**
  * i40e_close - Disables a network interface
  * @netdev: network interface device structure
  *
@@ -12137,6 +12160,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 			vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]);
 		if (!vsi) {
 			dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n");
+			i40e_cloud_filter_exit(pf);
 			i40e_fdir_teardown(pf);
 			return -EAGAIN;
 		}
@@ -12961,6 +12985,8 @@ static void i40e_remove(struct pci_dev *pdev)
 	if (pf->vsi[pf->lan_vsi])
 		i40e_vsi_release(pf->vsi[pf->lan_vsi]);
 
+	i40e_cloud_filter_exit(pf);
+
 	/* remove attached clients */
 	if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
 		ret_code = i40e_lan_del_device(pf);
@@ -13170,6 +13196,7 @@ static void i40e_shutdown(struct pci_dev *pdev)
 
 	del_timer_sync(&pf->service_timer);
 	cancel_work_sync(&pf->service_task);
+	i40e_cloud_filter_exit(pf);
 	i40e_fdir_teardown(pf);
 
 	/* Client close must be called explicitly here because the timer


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01  0:38   ` Amritha Nambiar
  -1 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:38 UTC (permalink / raw)
  To: intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, amritha.nambiar, netdev,
	mitch.a.williams, alexander.duyck, neerav.parikh,
	sridhar.samudrala, carolyn.wyborny

This patch enables tc-flower based hardware offloads. tc/flower
filter provided by the kernel is configured as driver specific
cloud filter. The patch implements functions and admin queue
commands needed to support cloud filters in the driver and
adds cloud filters to configure these tc-flower filters.

The only action supported is to redirect packets to a traffic class
on the same device.

# tc qdisc add dev eth0 ingress
# ethtool -K eth0 hw-tc-offload on

# tc filter add dev eth0 protocol ip parent ffff:\
  prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
  action mirred ingress redirect dev eth0 tc 0

# tc filter add dev eth0 protocol ip parent ffff:\
  prio 2 flower dst_ip 192.168.3.5/32\
  ip_proto udp dst_port 25 skip_sw indev eth0\
  action mirred ingress redirect dev eth0 tc 1

# tc filter add dev eth0 protocol ipv6 parent ffff:\
  prio 3 flower dst_ip fe8::200:1\
  ip_proto udp dst_port 66 skip_sw indev eth0\
  action mirred ingress redirect dev eth0 tc 2

Delete tc flower filter:
Example:

# tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
# tc filter del dev eth0 parent ffff:

Flow Director Sideband is disabled while configuring cloud filters
via tc-flower.

Unsupported matches when cloud filters are added using enhanced
big buffer cloud filter mode of underlying switch include:
1. source port and source IP
2. Combined MAC address and IP fields.
3. Not specfying L4 port

These filter matches can however be used to redirect traffic to
the main VSI (tc 0) which does not require the enhanced big buffer
cloud filter support.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h           |   46 +
 drivers/net/ethernet/intel/i40e/i40e_common.c    |  180 ++++
 drivers/net/ethernet/intel/i40e/i40e_main.c      |  952 ++++++++++++++++++++++
 drivers/net/ethernet/intel/i40e/i40e_prototype.h |   17 
 4 files changed, 1193 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 5c0cad5..7288265 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -55,6 +55,8 @@
 #include <linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 #include <net/pkt_cls.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_mirred.h>
 #include "i40e_type.h"
 #include "i40e_prototype.h"
 #include "i40e_client.h"
@@ -252,10 +254,51 @@ struct i40e_fdir_filter {
 	u32 fd_id;
 };
 
+#define I40E_CLOUD_FIELD_OMAC	0x01
+#define I40E_CLOUD_FIELD_IMAC	0x02
+#define I40E_CLOUD_FIELD_IVLAN	0x04
+#define I40E_CLOUD_FIELD_TEN_ID	0x08
+#define I40E_CLOUD_FIELD_IIP	0x10
+
+#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
+#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
+#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
+						 I40E_CLOUD_FIELD_IVLAN)
+#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
+						 I40E_CLOUD_FIELD_TEN_ID)
+#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
+						  I40E_CLOUD_FIELD_IMAC | \
+						  I40E_CLOUD_FIELD_TEN_ID)
+#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
+						   I40E_CLOUD_FIELD_IVLAN | \
+						   I40E_CLOUD_FIELD_TEN_ID)
+#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
+
 struct i40e_cloud_filter {
 	struct hlist_node cloud_node;
 	/* cloud filter input set follows */
 	unsigned long cookie;
+	u8 dst_mac[ETH_ALEN];
+	u8 src_mac[ETH_ALEN];
+	__be16 vlan_id;
+	__be32 dst_ip[4];
+	__be32 src_ip[4];
+	u8 dst_ipv6[16];
+	u8 src_ipv6[16];
+	__be16 dst_port;
+	__be16 src_port;
+	/* matter only when IP based filtering is set */
+	bool is_ipv6;
+	/* IPPROTO value */
+	u8 ip_proto;
+	/* L4 port type: src or destination port */
+#define I40E_CLOUD_FILTER_PORT_SRC	0x01
+#define I40E_CLOUD_FILTER_PORT_DEST	0x02
+	u8 port_type;
+	u32 tenant_id;
+	u8 flags;
+#define I40E_CLOUD_TNL_TYPE_NONE	0xff
+	u8 tunnel_type;
 	/* filter control */
 	u16 seid;
 };
@@ -574,6 +617,9 @@ struct i40e_pf {
 	u16 phy_led_val;
 
 	u16 override_q_count;
+	u16 last_sw_conf_flags;
+	u16 last_sw_conf_valid_flags;
+
 };
 
 /**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index d0e8138..bfbe304 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -5269,5 +5269,185 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
 
 	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
 				   track_id, &offset, &info, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_add_cloud_filters
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to add cloud filters from
+ * @filters: Buffer which contains the filters to be added
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Set the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_data are filled
+ * in by the caller of the function.
+ *
+ **/
+enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
+		u16 seid,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	enum i40e_status_code status;
+	u16 buff_len;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_add_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_add_cloud_filters_big_buffer
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to add cloud filters from
+ * @filters: Buffer which contains the filters in big buffer to be added
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Set the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
+ * in by the caller of the function.
+ *
+ **/
+i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
+	u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	i40e_status status;
+	u16 buff_len;
+	int i;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_add_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
+
+	for (i = 0; i < filter_count; i++) {
+		u16 tnl_type;
+		u32 ti;
+
+		tnl_type = (le16_to_cpu(filters[i].element.flags) &
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
+		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
+			ti = le32_to_cpu(filters[i].element.tenant_id);
+			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
+		}
+	}
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_remove_cloud_filters
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to remove cloud filters from
+ * @filters: Buffer which contains the filters to be removed
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Remove the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_data are filled
+ * in by the caller of the function.
+ *
+ **/
+enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
+		u16 seid,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	enum i40e_status_code status;
+	u16 buff_len;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_remove_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_remove_cloud_filters_big_buffer
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to remove cloud filters from
+ * @filters: Buffer which contains the filters in big buffer to be removed
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Remove the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
+ * in by the caller of the function.
+ *
+ **/
+i40e_status i40e_aq_remove_cloud_filters_big_buffer(
+	struct i40e_hw *hw,
+	u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	i40e_status status;
+	u16 buff_len;
+	int i;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_remove_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
+
+	for (i = 0; i < filter_count; i++) {
+		u16 tnl_type;
+		u32 ti;
+
+		tnl_type = (le16_to_cpu(filters[i].element.flags) &
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
+		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
+			ti = le32_to_cpu(filters[i].element.tenant_id);
+			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
+		}
+	}
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
 	return status;
 }
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 93f6fe2..65b284e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -69,6 +69,12 @@ static int i40e_reset(struct i40e_pf *pf);
 static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
 static void i40e_fdir_sb_setup(struct i40e_pf *pf);
 static int i40e_veb_get_bw_info(struct i40e_veb *veb);
+static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
+				     struct i40e_cloud_filter *filter,
+				     bool add);
+static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
+					     struct i40e_cloud_filter *filter,
+					     bool add);
 
 /* i40e_pci_tbl - PCI Device ID Table
  *
@@ -5482,7 +5488,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
  **/
 static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
 {
+	enum i40e_admin_queue_err last_aq_status;
+	struct i40e_cloud_filter *cfilter;
 	struct i40e_channel *ch, *ch_tmp;
+	struct i40e_pf *pf = vsi->back;
+	struct hlist_node *node;
 	int ret, i;
 
 	/* Reset rss size that was stored when reconfiguring rss for
@@ -5523,6 +5533,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
 				 "Failed to reset tx rate for ch->seid %u\n",
 				 ch->seid);
 
+		/* delete cloud filters associated with this channel */
+		hlist_for_each_entry_safe(cfilter, node,
+					  &pf->cloud_filter_list, cloud_node) {
+			if (cfilter->seid != ch->seid)
+				continue;
+
+			hash_del(&cfilter->cloud_node);
+			if (cfilter->dst_port)
+				ret = i40e_add_del_cloud_filter_big_buf(vsi,
+									cfilter,
+									false);
+			else
+				ret = i40e_add_del_cloud_filter(vsi, cfilter,
+								false);
+			last_aq_status = pf->hw.aq.asq_last_status;
+			if (ret)
+				dev_info(&pf->pdev->dev,
+					 "fail to delete cloud filter, err %s aq_err %s\n",
+					 i40e_stat_str(&pf->hw, ret),
+					 i40e_aq_str(&pf->hw, last_aq_status));
+			kfree(cfilter);
+		}
+
 		/* delete VSI from FW */
 		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
 					     NULL);
@@ -6004,6 +6037,131 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
 }
 
 /**
+ * i40e_get_device_capabilities - get device level info about the HW
+ * @pf: the PF struct
+ **/
+static int i40e_get_device_capabilities(struct i40e_pf *pf)
+{
+	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
+	u16 data_size;
+	int buf_len;
+	int err;
+
+	buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
+
+	/* proceed with query device level capabilities */
+	do {
+		cap_buf = kzalloc(buf_len, GFP_KERNEL);
+		if (!cap_buf)
+			return -ENOMEM;
+
+		/* this loads the data into the hw struct for us */
+		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
+					     &data_size,
+					     i40e_aqc_opc_list_dev_capabilities,
+					     NULL);
+		/* data loaded, buffer no longer needed */
+		kfree(cap_buf);
+
+		if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) {
+			/* retry with a larger buffer */
+			buf_len = data_size;
+		} else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) {
+			dev_dbg(&pf->pdev->dev,
+				"device capability discovery failed, err %s aq_err %s\n",
+				i40e_stat_str(&pf->hw, err),
+				i40e_aq_str(&pf->hw,
+					    pf->hw.aq.asq_last_status));
+			return -ENODEV;
+		}
+	} while (err);
+
+	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
+		dev_info(&pf->pdev->dev,
+			 "switch_mode=0x%04x, function_valid=0x%08x\n",
+			 pf->hw.dev_caps.switch_mode,
+			 pf->hw.dev_caps.valid_functions);
+		dev_info(&pf->pdev->dev,
+			 "SR-IOV=%d, num_vfs for all function=%u\n",
+			 pf->hw.dev_caps.sr_iov_1_1, pf->hw.dev_caps.num_vfs);
+		dev_info(&pf->pdev->dev,
+			 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
+			 pf->hw.dev_caps.num_vsis, pf->hw.dev_caps.num_rx_qp,
+			 pf->hw.dev_caps.num_tx_qp);
+	}
+	return 0;
+}
+
+/**
+ * i40e_validate_and_set_switch_mode - sets up switch mode correctly
+ * @vsi: ptr to VSI which has PF backing
+ * @l4type: true for TCP ond false for UDP
+ * @port_type: true if port is destination and false if port is source
+ *
+ * Sets up switch mode correctly if it needs to be changed and perform
+ * what are allowed modes.
+ **/
+static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
+					     bool port_type)
+{
+	u8 mode;
+	struct i40e_pf *pf = vsi->back;
+	struct i40e_hw *hw = &pf->hw;
+	int ret;
+
+	ret = i40e_get_device_capabilities(pf);
+	if (ret)
+		return -EINVAL;
+
+	if (hw->dev_caps.switch_mode) {
+		/* if switch mode is set, support mode2 (non-tunneled for
+		 * cloud filter) for now
+		 */
+#define I40E_SWITCH_MODE_MASK	0xF /* largest cloud filter mode is 0x8 */
+		u32 switch_mode = hw->dev_caps.switch_mode &
+							I40E_SWITCH_MODE_MASK;
+		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
+			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
+				return 0;
+			dev_err(&pf->pdev->dev,
+				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
+				hw->dev_caps.switch_mode);
+			return -EINVAL;
+		}
+	}
+
+	/* port_type: true for destination port and false for source port
+	 * For now, supports only destination port type
+	 */
+	if (!port_type) {
+		dev_err(&pf->pdev->dev, "src port type not supported\n");
+		return -EINVAL;
+	}
+
+	/* Set Bit 7 to be valid */
+	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
+
+	/* Set L4type to both TCP and UDP support */
+	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
+
+	/* Set cloud filter mode */
+	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
+
+	/* Prep mode field for set_switch_config */
+	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
+					pf->last_sw_conf_valid_flags,
+					mode, NULL);
+	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
+		dev_err(&pf->pdev->dev,
+			"couldn't set switch config bits, err %s aq_err %s\n",
+			i40e_stat_str(hw, ret),
+			i40e_aq_str(hw,
+				    hw->aq.asq_last_status));
+
+	return ret;
+}
+
+/**
  * i40e_create_queue_channel - function to create channel
  * @vsi: VSI to be configured
  * @ch: ptr to channel (it contains channel specific params)
@@ -6632,6 +6790,10 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
 	}
 	if (!hw) {
 		pf->flags &= ~I40E_FLAG_TC_MQPRIO;
+		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
+		    (pf->hw.func_caps.fd_filters_best_effort > 0))
+			pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+
 		if (tc->type == TC_SETUP_MQPRIO_EXT)
 			memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
 			       sizeof(*tc->mqprio_qopt));
@@ -6682,6 +6844,11 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
 		       sizeof(*tc->mqprio_qopt));
 		pf->flags |= I40E_FLAG_TC_MQPRIO;
 		pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
+			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+			dev_info(&pf->pdev->dev,
+				 "Disabling ATR in MQPRIO mode\n");
+		}
 		break;
 	default:
 		return -EINVAL;
@@ -6743,10 +6910,724 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
 	return ret;
 }
 
+/**
+ * i40e_set_cld_element - sets cloud filter element data
+ * @filter: cloud filter rule
+ * @cld: ptr to cloud filter element data
+ *
+ * This is helper function to copy data into cloud filter element
+ **/
+static inline void
+i40e_set_cld_element(struct i40e_cloud_filter *filter,
+		     struct i40e_aqc_add_remove_cloud_filters_element_data *cld)
+{
+	u8 *dest_ipaddr;
+	u32 ipaddr;
+	int i;
+
+	memset(cld, 0, sizeof(*cld));
+
+	ether_addr_copy(cld->outer_mac, filter->dst_mac);
+	ether_addr_copy(cld->inner_mac, filter->src_mac);
+
+	if (filter->is_ipv6) {
+		dest_ipaddr = (u8 *)&cld->ipaddr.v6.data;
+		for (i = ARRAY_SIZE(filter->dst_ipv6) - 1; i >= 0; i--) {
+			memcpy(dest_ipaddr, &filter->dst_ipv6[i], 1);
+			dest_ipaddr++;
+		}
+	} else {
+		ipaddr = be32_to_cpu(filter->dst_ip[0]);
+		memcpy(&cld->ipaddr.v4.data, &ipaddr, 4);
+	}
+
+	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
+	cld->tenant_id = cpu_to_le32(filter->tenant_id);
+}
+
+/**
+ * i40e_add_del_cloud_filter - Add/del cloud filter
+ * @vsi: pointer to VSI
+ * @filter: cloud filter rule
+ * @add: if true, add, if false, delete
+ *
+ * Add or delete a cloud filter for a specific flow spec.
+ * Returns 0 if the filter were successfully added.
+ **/
+static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
+				     struct i40e_cloud_filter *filter, bool add)
+{
+	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
+	struct i40e_pf *pf = vsi->back;
+	int ret;
+	static const u16 flag_table[128] = {
+		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
+			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
+		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
+			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
+		[I40E_CLOUD_FILTER_FLAGS_IIP] =
+			I40E_AQC_ADD_CLOUD_FILTER_IIP,
+	};
+
+	if (filter->flags >= ARRAY_SIZE(flag_table))
+		return I40E_ERR_CONFIG;
+
+	/* copy element needed to add cloud filter from filter */
+	i40e_set_cld_element(filter, &cld_filter);
+
+	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
+		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
+					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
+
+	if (filter->is_ipv6)
+		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
+						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
+	else
+		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
+						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
+
+	if (add)
+		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
+						&cld_filter, 1);
+	else
+		ret = i40e_aq_remove_cloud_filters(&pf->hw, filter->seid,
+						   &cld_filter, 1);
+	if (ret)
+		dev_dbg(&pf->pdev->dev,
+			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
+			add ? "add" : "delete", filter->dst_port, ret,
+			pf->hw.aq.asq_last_status);
+
+	dev_info(&pf->pdev->dev,
+		 "%s cloud filter for VSI: %d\n", add ? "Added" : "Deleted",
+		 filter->seid);
+	return ret;
+}
+
+/**
+ * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
+ * @vsi: pointer to VSI
+ * @filter: cloud filter rule
+ * @add: if true, add, if false, delete
+ *
+ * Add or delete a cloud filter for a specific flow spec using big buffer.
+ * Returns 0 if the filter were successfully added.
+ **/
+static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
+					     struct i40e_cloud_filter *filter,
+					     bool add)
+{
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data cld_filter;
+	struct i40e_pf *pf = vsi->back;
+	int ret;
+
+	/* Both (Outer/Inner) valid mac_addr are not supported */
+	if (is_valid_ether_addr(filter->dst_mac) &&
+	    is_valid_ether_addr(filter->src_mac))
+		return -EINVAL;
+
+	/* Make sure port is specified, otherwise bail out, for channel
+	 * specific cloud filter needs 'L4 port' to be non-zero
+	 */
+	if (!filter->dst_port)
+		return -EINVAL;
+
+	/* adding filter using src_port/src_ip is not supported at this stage */
+	if (filter->src_port || filter->src_ip[0])
+		return -EINVAL;
+
+	/* copy element needed to add cloud filter from filter */
+	i40e_set_cld_element(filter, &cld_filter.element);
+
+	if (is_valid_ether_addr(filter->dst_mac) ||
+	    is_valid_ether_addr(filter->src_mac) ||
+	    is_multicast_ether_addr(filter->dst_mac) ||
+	    is_multicast_ether_addr(filter->src_mac)) {
+		/* MAC + IP : unsupported mode */
+		if (filter->dst_ip[0])
+			return -EINVAL;
+
+		/* since we validated that L4 port must be valid before
+		 * we get here, start with respective "flags" value
+		 * and update if vlan is present or not
+		 */
+		cld_filter.element.flags =
+			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
+
+		if (filter->vlan_id) {
+			cld_filter.element.flags =
+			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
+		}
+
+	} else if (filter->dst_ip[0] || filter->is_ipv6) {
+		cld_filter.element.flags =
+				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
+		if (filter->is_ipv6)
+			cld_filter.element.flags |=
+				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
+		else
+			cld_filter.element.flags |=
+				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
+	} else {
+		dev_err(&pf->pdev->dev,
+			"either mac or ip has to be valid for cloud filter\n");
+		return -EINVAL;
+	}
+
+	/* Now copy L4 port in Byte 6..7 in general fields */
+	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
+						be16_to_cpu(filter->dst_port);
+
+	if (add) {
+		bool proto_type, port_type;
+
+		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
+		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
+			     true : false;
+
+		/* For now, src port based cloud filter for channel is not
+		 * supported
+		 */
+		if (!port_type) {
+			dev_err(&pf->pdev->dev,
+				"unsupported port type (src port)\n");
+			return -EOPNOTSUPP;
+		}
+
+		/* Validate current device switch mode, change if necessary */
+		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
+							port_type);
+		if (ret) {
+			dev_err(&pf->pdev->dev,
+				"fail to and set switch mode, ret %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = i40e_aq_add_cloud_filters_big_buffer(&pf->hw,
+							   filter->seid,
+							   &cld_filter, 1);
+	} else {
+		ret = i40e_aq_remove_cloud_filters_big_buffer(&pf->hw,
+							      filter->seid,
+							      &cld_filter, 1);
+	}
+
+	if (ret)
+		dev_dbg(&pf->pdev->dev,
+			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
+			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
+
+	dev_info(&pf->pdev->dev,
+		 "%s cloud filter for VSI: %d, L4 port: %d\n",
+		 add ? "add" : "delete", filter->seid, ntohs(filter->dst_port));
+
+	return ret;
+}
+
+/**
+ * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ * @filter: Pointer to cloud filter structure
+ *
+ **/
+static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
+				 struct tc_cls_flower_offload *f,
+				 struct i40e_cloud_filter *filter)
+{
+	struct i40e_pf *pf = vsi->back;
+	u16 addr_type = 0;
+	u8 field_flags = 0;
+
+	if (f->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_PORTS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
+	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
+		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
+			f->dissector->used_keys);
+		return -EOPNOTSUPP;
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+		struct flow_dissector_key_keyid *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ENC_KEYID,
+						  f->key);
+
+		struct flow_dissector_key_keyid *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ENC_KEYID,
+						  f->mask);
+
+		if (mask->keyid != 0)
+			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
+
+		filter->tenant_id = be32_to_cpu(key->keyid);
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+		struct flow_dissector_key_basic *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_BASIC,
+						  f->key);
+
+		filter->ip_proto = key->ip_proto;
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+		struct flow_dissector_key_eth_addrs *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
+						  f->key);
+
+		struct flow_dissector_key_eth_addrs *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
+						  f->mask);
+
+		/* use is_broadcast and is_zero to check for all 0xf or 0 */
+		if (!is_zero_ether_addr(mask->dst)) {
+			if (is_broadcast_ether_addr(mask->dst)) {
+				field_flags |= I40E_CLOUD_FIELD_OMAC;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
+					mask->dst);
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (!is_zero_ether_addr(mask->src)) {
+			if (is_broadcast_ether_addr(mask->src)) {
+				field_flags |= I40E_CLOUD_FIELD_IMAC;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
+					mask->src);
+				return I40E_ERR_CONFIG;
+			}
+		}
+		ether_addr_copy(filter->dst_mac, key->dst);
+		ether_addr_copy(filter->src_mac, key->src);
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
+		struct flow_dissector_key_vlan *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_VLAN,
+						  f->key);
+		struct flow_dissector_key_vlan *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_VLAN,
+						  f->mask);
+
+		if (mask->vlan_id) {
+			if (mask->vlan_id == VLAN_VID_MASK) {
+				field_flags |= I40E_CLOUD_FIELD_IVLAN;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad vlan mask %u\n",
+					mask->vlan_id);
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		filter->vlan_id = cpu_to_be16(key->vlan_id);
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+		struct flow_dissector_key_control *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_CONTROL,
+						  f->key);
+
+		addr_type = key->addr_type;
+	}
+
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+		struct flow_dissector_key_ipv4_addrs *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+						  f->key);
+		struct flow_dissector_key_ipv4_addrs *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+						  f->mask);
+
+		if (mask->dst) {
+			if (mask->dst == cpu_to_be32(0xffffffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
+					be32_to_cpu(mask->dst));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (mask->src) {
+			if (mask->src == cpu_to_be32(0xffffffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
+					be32_to_cpu(mask->dst));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
+			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
+			return I40E_ERR_CONFIG;
+		}
+		filter->dst_ip[0] = key->dst;
+		filter->src_ip[0] = key->src;
+	}
+
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+		struct flow_dissector_key_ipv6_addrs *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+						  f->key);
+		struct flow_dissector_key_ipv6_addrs *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+						  f->mask);
+
+		/* validate mask, make sure it is not IPV6_ADDR_ANY */
+		if (ipv6_addr_any(&mask->dst)) {
+			dev_err(&pf->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
+				IPV6_ADDR_ANY);
+			return I40E_ERR_CONFIG;
+		}
+
+		/* validate src and dest IPV6 address, make sure they are not
+		 * ANY (0:0:0:0:0:0:0:0) or LOOPBACK (0:0:0:0:0:0:0:1), which
+		 * can be represented as ::1
+		 */
+		if (ipv6_addr_any(&key->dst) || ipv6_addr_loopback(&key->dst)) {
+			dev_err(&pf->pdev->dev,
+				"Bad ipv6 dst addr is ANY or LOOPBACK\n");
+			return I40E_ERR_CONFIG;
+		}
+		if (ipv6_addr_loopback(&key->src)) {
+			dev_err(&pf->pdev->dev,
+				"Bad ipv6 src addr is ANY or LOOPBACK\n");
+			return I40E_ERR_CONFIG;
+		}
+		memcpy(&filter->src_ipv6, &key->src.s6_addr,
+		       ARRAY_SIZE(filter->src_ipv6));
+		memcpy(&filter->dst_ipv6, &key->dst.s6_addr,
+		       ARRAY_SIZE(filter->dst_ipv6));
+
+		/* mark it as IPv6 filter, to be used later */
+		filter->is_ipv6 = true;
+
+		/* and it is IP[4|6] filter type */
+		field_flags |= I40E_CLOUD_FIELD_IIP;
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
+		struct flow_dissector_key_ports *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_PORTS,
+						  f->key);
+		struct flow_dissector_key_ports *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_PORTS,
+						  f->mask);
+
+		if (mask->src) {
+			if (mask->src == cpu_to_be16(0xffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad src port mask %u\n",
+					be16_to_cpu(mask->src));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (mask->dst) {
+			if (mask->dst == cpu_to_be16(0xffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad dst port mask %u\n",
+					be16_to_cpu(mask->dst));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		filter->dst_port = key->dst;
+		filter->src_port = key->src;
+
+		/* For now, only supports destination port*/
+		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
+
+		switch (filter->ip_proto) {
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
+			break;
+		default:
+			dev_err(&pf->pdev->dev,
+				"Only UDP and TCP transport are supported\n");
+			return -EINVAL;
+		}
+	}
+	filter->flags = field_flags;
+	return 0;
+}
+
+/**
+ * i40e_handle_redirect_action: Forward to a traffic class on the device
+ * @vsi: Pointer to VSI
+ * @ifindex: ifindex of the device to forwared to
+ * @tc: traffic class index on the device
+ * @filter: Pointer to cloud filter structure
+ *
+ **/
+static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
+				       struct i40e_cloud_filter *filter)
+{
+	struct i40e_channel *ch, *ch_tmp;
+
+	/* redirect to a traffic class on the same device */
+	if (vsi->netdev->ifindex == ifindex) {
+		if (tc == 0) {
+			filter->seid = vsi->seid;
+			return 0;
+		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
+			if (!filter->dst_port) {
+				dev_err(&vsi->back->pdev->dev,
+					"Specify destination port to redirect to traffic class that is not default\n");
+				return -EINVAL;
+			}
+			if (list_empty(&vsi->ch_list))
+				return -EINVAL;
+			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
+						 list) {
+				if (ch->seid == vsi->tc_seid_map[tc])
+					filter->seid = ch->seid;
+			}
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+/**
+ * i40e_parse_tc_actions - Parse tc actions
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ * @filter: Pointer to cloud filter structure
+ *
+ **/
+static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
+				 struct i40e_cloud_filter *filter)
+{
+	const struct tc_action *a;
+	LIST_HEAD(actions);
+	int err;
+
+	if (tc_no_actions(exts))
+		return -EINVAL;
+
+	tcf_exts_to_list(exts, &actions);
+	list_for_each_entry(a, &actions, list) {
+		/* Drop action */
+		if (is_tcf_gact_shot(a)) {
+			dev_err(&vsi->back->pdev->dev,
+				"Cloud filters do not support the drop action.\n");
+			return -EOPNOTSUPP;
+		}
+
+		/* Redirect to a traffic class on the same device */
+		if (!is_tcf_mirred_egress_redirect(a)) {
+			int ifindex = tcf_mirred_ifindex(a);
+			int tc = tcf_mirred_tc(a);
+
+			err = i40e_handle_redirect_action(vsi, ifindex, tc,
+							  filter);
+			if (err == 0)
+				return err;
+		}
+	}
+	return -EINVAL;
+}
+
+/**
+ * i40e_configure_clsflower - Configure tc flower filters
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ *
+ **/
+static int i40e_configure_clsflower(struct i40e_vsi *vsi,
+				    struct tc_cls_flower_offload *cls_flower)
+{
+	struct i40e_cloud_filter *filter = NULL;
+	struct i40e_pf *pf = vsi->back;
+	int err = 0;
+
+	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
+	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
+		return -EBUSY;
+
+	if (pf->fdir_pf_active_filters ||
+	    (!hlist_empty(&pf->fdir_filter_list))) {
+		dev_err(&vsi->back->pdev->dev,
+			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
+		return -EINVAL;
+	}
+
+	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
+		dev_err(&vsi->back->pdev->dev,
+			"Disable Flow Director Sideband while configuring Cloud filters via tc-flower\n");
+		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+	}
+
+	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+	if (!filter)
+		return -ENOMEM;
+
+	filter->cookie = cls_flower->cookie;
+
+	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
+	if (err < 0)
+		goto err;
+
+	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
+	if (err < 0)
+		goto err;
+
+	/* Add cloud filter */
+	if (filter->dst_port)
+		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
+	else
+		err = i40e_add_del_cloud_filter(vsi, filter, true);
+
+	if (err) {
+		dev_err(&pf->pdev->dev,
+			"Failed to add cloud filter, err %s\n",
+			i40e_stat_str(&pf->hw, err));
+		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
+		goto err;
+	}
+
+	/* add filter to the ordered list */
+	INIT_HLIST_NODE(&filter->cloud_node);
+
+	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
+
+	pf->num_cloud_filters++;
+
+	return err;
+err:
+	kfree(filter);
+	return err;
+}
+
+/**
+ * i40e_find_cloud_filter - Find the could filter in the list
+ * @vsi: Pointer to VSI
+ * @cookie: filter specific cookie
+ *
+ **/
+static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
+							unsigned long *cookie)
+{
+	struct i40e_cloud_filter *filter = NULL;
+	struct hlist_node *node2;
+
+	hlist_for_each_entry_safe(filter, node2,
+				  &vsi->back->cloud_filter_list, cloud_node)
+		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
+			return filter;
+	return NULL;
+}
+
+/**
+ * i40e_delete_clsflower - Remove tc flower filters
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ *
+ **/
+static int i40e_delete_clsflower(struct i40e_vsi *vsi,
+				 struct tc_cls_flower_offload *cls_flower)
+{
+	struct i40e_cloud_filter *filter = NULL;
+	struct i40e_pf *pf = vsi->back;
+	int err = 0;
+
+	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
+
+	if (!filter)
+		return -EINVAL;
+
+	hash_del(&filter->cloud_node);
+
+	if (filter->dst_port)
+		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
+	else
+		err = i40e_add_del_cloud_filter(vsi, filter, false);
+	if (err) {
+		kfree(filter);
+		dev_err(&pf->pdev->dev,
+			"Failed to delete cloud filter, err %s\n",
+			i40e_stat_str(&pf->hw, err));
+		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
+	}
+
+	kfree(filter);
+	pf->num_cloud_filters--;
+
+	if (!pf->num_cloud_filters) {
+		/* Re-enable FD-SB that was disabled while configuring cloud
+		 * filters
+		 */
+		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
+		    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
+			if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
+			      pf->hw.num_partitions > 1))
+				pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+		}
+	}
+
+	return 0;
+}
+
 static int __i40e_setup_tc(struct net_device *netdev, u32 handle,
 			   u32 chain_index, __be16 proto,
 			   struct tc_to_netdev *tc)
 {
+	struct i40e_netdev_priv *np = netdev_priv(netdev);
+	struct i40e_vsi *vsi = np->vsi;
+
+	if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
+	    tc->type == TC_SETUP_CLSFLOWER) {
+		switch (tc->cls_flower->command) {
+		case TC_CLSFLOWER_REPLACE:
+			return i40e_configure_clsflower(vsi, tc->cls_flower);
+		case TC_CLSFLOWER_DESTROY:
+			return i40e_delete_clsflower(vsi, tc->cls_flower);
+		case TC_CLSFLOWER_STATS:
+			return -EOPNOTSUPP;
+		default:
+			return -EINVAL;
+		}
+	}
+
 	return i40e_setup_tc(netdev, tc);
 }
 
@@ -6948,6 +7829,16 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
 		kfree(cfilter);
 	}
 	pf->num_cloud_filters = 0;
+
+	/* Re-enable FD-SB that was disabled while configuring cloud
+	 * filters
+	 */
+	if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
+	    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
+		if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
+		      pf->hw.num_partitions > 1))
+			pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+	}
 }
 
 /**
@@ -8157,6 +9048,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
 }
 
 /**
+ * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
+ * @vsi: PF main vsi
+ * @seid: seid of main or channel VSIs
+ *
+ * Rebuilds cloud filters associated with main VSI and channel VSIs if they
+ * existed before reset
+ **/
+static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
+{
+	struct i40e_cloud_filter *cfilter;
+	struct i40e_pf *pf = vsi->back;
+	struct hlist_node *node;
+	i40e_status ret;
+
+	/* Add cloud filters back if they exist */
+	if (hlist_empty(&pf->cloud_filter_list))
+		return 0;
+
+	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
+				  cloud_node) {
+		if (cfilter->seid != seid)
+			continue;
+
+		if (cfilter->dst_port)
+			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
+								true);
+		else
+			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
+
+		if (ret) {
+			dev_dbg(&pf->pdev->dev,
+				"Failed to rebuild cloud filter, err %s aq_err %s\n",
+				i40e_stat_str(&pf->hw, ret),
+				i40e_aq_str(&pf->hw,
+					    pf->hw.aq.asq_last_status));
+			return ret;
+		}
+	}
+	return 0;
+}
+
+/**
  * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
  * @vsi: PF main vsi
  *
@@ -8193,6 +9126,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
 						I40E_BW_CREDIT_DIVISOR,
 				ch->seid);
 		}
+		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
+		if (ret) {
+			dev_dbg(&vsi->back->pdev->dev,
+				"Failed to rebuild cloud filters for channel VSI %u\n",
+				ch->seid);
+			return ret;
+		}
 	}
 	return 0;
 }
@@ -8476,6 +9416,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
 			goto end_unlock;
 	}
 
+	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
+	if (ret)
+		goto end_unlock;
+
 	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
 	 * for this main VSI if they exist
 	 */
@@ -10846,7 +11790,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
 		netdev->hw_features |= NETIF_F_NTUPLE;
 	hw_features = hw_enc_features		|
 		      NETIF_F_HW_VLAN_CTAG_TX	|
-		      NETIF_F_HW_VLAN_CTAG_RX;
+		      NETIF_F_HW_VLAN_CTAG_RX	|
+		      NETIF_F_HW_TC;
 
 	netdev->hw_features |= hw_features;
 
@@ -12123,8 +13068,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 	*/
 
 	if ((pf->hw.pf_id == 0) &&
-	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
+	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
 		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
+		pf->last_sw_conf_flags = flags;
+	}
 
 	if (pf->hw.pf_id == 0) {
 		u16 valid_flags;
@@ -12140,6 +13087,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 					     pf->hw.aq.asq_last_status));
 			/* not a fatal problem, just keep going */
 		}
+		pf->last_sw_conf_valid_flags = valid_flags;
 	}
 
 	/* first time setup */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index 9142d0d..e24f1ce 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -283,6 +283,23 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
 		struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
 				   struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
+	u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count);
+enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
+		u16 vsi,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count);
+
+enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
+		u16 vsi,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count);
+i40e_status i40e_aq_remove_cloud_filters_big_buffer(
+	struct i40e_hw *hw, u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count);
 i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
 			       struct i40e_lldp_variables *lldp_cfg);
 /* i40e_common */

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-01  0:38   ` Amritha Nambiar
  0 siblings, 0 replies; 58+ messages in thread
From: Amritha Nambiar @ 2017-08-01  0:38 UTC (permalink / raw)
  To: intel-wired-lan

This patch enables tc-flower based hardware offloads. tc/flower
filter provided by the kernel is configured as driver specific
cloud filter. The patch implements functions and admin queue
commands needed to support cloud filters in the driver and
adds cloud filters to configure these tc-flower filters.

The only action supported is to redirect packets to a traffic class
on the same device.

# tc qdisc add dev eth0 ingress
# ethtool -K eth0 hw-tc-offload on

# tc filter add dev eth0 protocol ip parent ffff:\
  prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
  action mirred ingress redirect dev eth0 tc 0

# tc filter add dev eth0 protocol ip parent ffff:\
  prio 2 flower dst_ip 192.168.3.5/32\
  ip_proto udp dst_port 25 skip_sw indev eth0\
  action mirred ingress redirect dev eth0 tc 1

# tc filter add dev eth0 protocol ipv6 parent ffff:\
  prio 3 flower dst_ip fe8::200:1\
  ip_proto udp dst_port 66 skip_sw indev eth0\
  action mirred ingress redirect dev eth0 tc 2

Delete tc flower filter:
Example:

# tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
# tc filter del dev eth0 parent ffff:

Flow Director Sideband is disabled while configuring cloud filters
via tc-flower.

Unsupported matches when cloud filters are added using enhanced
big buffer cloud filter mode of underlying switch include:
1. source port and source IP
2. Combined MAC address and IP fields.
3. Not specfying L4 port

These filter matches can however be used to redirect traffic to
the main VSI (tc 0) which does not require the enhanced big buffer
cloud filter support.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h           |   46 +
 drivers/net/ethernet/intel/i40e/i40e_common.c    |  180 ++++
 drivers/net/ethernet/intel/i40e/i40e_main.c      |  952 ++++++++++++++++++++++
 drivers/net/ethernet/intel/i40e/i40e_prototype.h |   17 
 4 files changed, 1193 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 5c0cad5..7288265 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -55,6 +55,8 @@
 #include <linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 #include <net/pkt_cls.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_mirred.h>
 #include "i40e_type.h"
 #include "i40e_prototype.h"
 #include "i40e_client.h"
@@ -252,10 +254,51 @@ struct i40e_fdir_filter {
 	u32 fd_id;
 };
 
+#define I40E_CLOUD_FIELD_OMAC	0x01
+#define I40E_CLOUD_FIELD_IMAC	0x02
+#define I40E_CLOUD_FIELD_IVLAN	0x04
+#define I40E_CLOUD_FIELD_TEN_ID	0x08
+#define I40E_CLOUD_FIELD_IIP	0x10
+
+#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
+#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
+#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
+						 I40E_CLOUD_FIELD_IVLAN)
+#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
+						 I40E_CLOUD_FIELD_TEN_ID)
+#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
+						  I40E_CLOUD_FIELD_IMAC | \
+						  I40E_CLOUD_FIELD_TEN_ID)
+#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
+						   I40E_CLOUD_FIELD_IVLAN | \
+						   I40E_CLOUD_FIELD_TEN_ID)
+#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
+
 struct i40e_cloud_filter {
 	struct hlist_node cloud_node;
 	/* cloud filter input set follows */
 	unsigned long cookie;
+	u8 dst_mac[ETH_ALEN];
+	u8 src_mac[ETH_ALEN];
+	__be16 vlan_id;
+	__be32 dst_ip[4];
+	__be32 src_ip[4];
+	u8 dst_ipv6[16];
+	u8 src_ipv6[16];
+	__be16 dst_port;
+	__be16 src_port;
+	/* matter only when IP based filtering is set */
+	bool is_ipv6;
+	/* IPPROTO value */
+	u8 ip_proto;
+	/* L4 port type: src or destination port */
+#define I40E_CLOUD_FILTER_PORT_SRC	0x01
+#define I40E_CLOUD_FILTER_PORT_DEST	0x02
+	u8 port_type;
+	u32 tenant_id;
+	u8 flags;
+#define I40E_CLOUD_TNL_TYPE_NONE	0xff
+	u8 tunnel_type;
 	/* filter control */
 	u16 seid;
 };
@@ -574,6 +617,9 @@ struct i40e_pf {
 	u16 phy_led_val;
 
 	u16 override_q_count;
+	u16 last_sw_conf_flags;
+	u16 last_sw_conf_valid_flags;
+
 };
 
 /**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index d0e8138..bfbe304 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -5269,5 +5269,185 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
 
 	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
 				   track_id, &offset, &info, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_add_cloud_filters
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to add cloud filters from
+ * @filters: Buffer which contains the filters to be added
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Set the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_data are filled
+ * in by the caller of the function.
+ *
+ **/
+enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
+		u16 seid,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	enum i40e_status_code status;
+	u16 buff_len;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_add_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_add_cloud_filters_big_buffer
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to add cloud filters from
+ * @filters: Buffer which contains the filters in big buffer to be added
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Set the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
+ * in by the caller of the function.
+ *
+ **/
+i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
+	u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	i40e_status status;
+	u16 buff_len;
+	int i;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_add_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
+
+	for (i = 0; i < filter_count; i++) {
+		u16 tnl_type;
+		u32 ti;
+
+		tnl_type = (le16_to_cpu(filters[i].element.flags) &
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
+		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
+			ti = le32_to_cpu(filters[i].element.tenant_id);
+			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
+		}
+	}
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_remove_cloud_filters
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to remove cloud filters from
+ * @filters: Buffer which contains the filters to be removed
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Remove the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_data are filled
+ * in by the caller of the function.
+ *
+ **/
+enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
+		u16 seid,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	enum i40e_status_code status;
+	u16 buff_len;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_remove_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
+	return status;
+}
+
+/**
+ * i40e_aq_remove_cloud_filters_big_buffer
+ * @hw: pointer to the hardware structure
+ * @seid: VSI seid to remove cloud filters from
+ * @filters: Buffer which contains the filters in big buffer to be removed
+ * @filter_count: number of filters contained in the buffer
+ *
+ * Remove the cloud filters for a given VSI.  The contents of the
+ * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
+ * in by the caller of the function.
+ *
+ **/
+i40e_status i40e_aq_remove_cloud_filters_big_buffer(
+	struct i40e_hw *hw,
+	u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count)
+{
+	struct i40e_aq_desc desc;
+	struct i40e_aqc_add_remove_cloud_filters *cmd =
+	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
+	i40e_status status;
+	u16 buff_len;
+	int i;
+
+	i40e_fill_default_direct_cmd_desc(&desc,
+					  i40e_aqc_opc_remove_cloud_filters);
+
+	buff_len = filter_count * sizeof(*filters);
+	desc.datalen = cpu_to_le16(buff_len);
+	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+	cmd->num_filters = filter_count;
+	cmd->seid = cpu_to_le16(seid);
+	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
+
+	for (i = 0; i < filter_count; i++) {
+		u16 tnl_type;
+		u32 ti;
+
+		tnl_type = (le16_to_cpu(filters[i].element.flags) &
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
+			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
+		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
+			ti = le32_to_cpu(filters[i].element.tenant_id);
+			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
+		}
+	}
+
+	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
+
 	return status;
 }
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 93f6fe2..65b284e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -69,6 +69,12 @@ static int i40e_reset(struct i40e_pf *pf);
 static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
 static void i40e_fdir_sb_setup(struct i40e_pf *pf);
 static int i40e_veb_get_bw_info(struct i40e_veb *veb);
+static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
+				     struct i40e_cloud_filter *filter,
+				     bool add);
+static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
+					     struct i40e_cloud_filter *filter,
+					     bool add);
 
 /* i40e_pci_tbl - PCI Device ID Table
  *
@@ -5482,7 +5488,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
  **/
 static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
 {
+	enum i40e_admin_queue_err last_aq_status;
+	struct i40e_cloud_filter *cfilter;
 	struct i40e_channel *ch, *ch_tmp;
+	struct i40e_pf *pf = vsi->back;
+	struct hlist_node *node;
 	int ret, i;
 
 	/* Reset rss size that was stored when reconfiguring rss for
@@ -5523,6 +5533,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
 				 "Failed to reset tx rate for ch->seid %u\n",
 				 ch->seid);
 
+		/* delete cloud filters associated with this channel */
+		hlist_for_each_entry_safe(cfilter, node,
+					  &pf->cloud_filter_list, cloud_node) {
+			if (cfilter->seid != ch->seid)
+				continue;
+
+			hash_del(&cfilter->cloud_node);
+			if (cfilter->dst_port)
+				ret = i40e_add_del_cloud_filter_big_buf(vsi,
+									cfilter,
+									false);
+			else
+				ret = i40e_add_del_cloud_filter(vsi, cfilter,
+								false);
+			last_aq_status = pf->hw.aq.asq_last_status;
+			if (ret)
+				dev_info(&pf->pdev->dev,
+					 "fail to delete cloud filter, err %s aq_err %s\n",
+					 i40e_stat_str(&pf->hw, ret),
+					 i40e_aq_str(&pf->hw, last_aq_status));
+			kfree(cfilter);
+		}
+
 		/* delete VSI from FW */
 		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
 					     NULL);
@@ -6004,6 +6037,131 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
 }
 
 /**
+ * i40e_get_device_capabilities - get device level info about the HW
+ * @pf: the PF struct
+ **/
+static int i40e_get_device_capabilities(struct i40e_pf *pf)
+{
+	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
+	u16 data_size;
+	int buf_len;
+	int err;
+
+	buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
+
+	/* proceed with query device level capabilities */
+	do {
+		cap_buf = kzalloc(buf_len, GFP_KERNEL);
+		if (!cap_buf)
+			return -ENOMEM;
+
+		/* this loads the data into the hw struct for us */
+		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
+					     &data_size,
+					     i40e_aqc_opc_list_dev_capabilities,
+					     NULL);
+		/* data loaded, buffer no longer needed */
+		kfree(cap_buf);
+
+		if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) {
+			/* retry with a larger buffer */
+			buf_len = data_size;
+		} else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) {
+			dev_dbg(&pf->pdev->dev,
+				"device capability discovery failed, err %s aq_err %s\n",
+				i40e_stat_str(&pf->hw, err),
+				i40e_aq_str(&pf->hw,
+					    pf->hw.aq.asq_last_status));
+			return -ENODEV;
+		}
+	} while (err);
+
+	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
+		dev_info(&pf->pdev->dev,
+			 "switch_mode=0x%04x, function_valid=0x%08x\n",
+			 pf->hw.dev_caps.switch_mode,
+			 pf->hw.dev_caps.valid_functions);
+		dev_info(&pf->pdev->dev,
+			 "SR-IOV=%d, num_vfs for all function=%u\n",
+			 pf->hw.dev_caps.sr_iov_1_1, pf->hw.dev_caps.num_vfs);
+		dev_info(&pf->pdev->dev,
+			 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
+			 pf->hw.dev_caps.num_vsis, pf->hw.dev_caps.num_rx_qp,
+			 pf->hw.dev_caps.num_tx_qp);
+	}
+	return 0;
+}
+
+/**
+ * i40e_validate_and_set_switch_mode - sets up switch mode correctly
+ * @vsi: ptr to VSI which has PF backing
+ * @l4type: true for TCP ond false for UDP
+ * @port_type: true if port is destination and false if port is source
+ *
+ * Sets up switch mode correctly if it needs to be changed and perform
+ * what are allowed modes.
+ **/
+static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
+					     bool port_type)
+{
+	u8 mode;
+	struct i40e_pf *pf = vsi->back;
+	struct i40e_hw *hw = &pf->hw;
+	int ret;
+
+	ret = i40e_get_device_capabilities(pf);
+	if (ret)
+		return -EINVAL;
+
+	if (hw->dev_caps.switch_mode) {
+		/* if switch mode is set, support mode2 (non-tunneled for
+		 * cloud filter) for now
+		 */
+#define I40E_SWITCH_MODE_MASK	0xF /* largest cloud filter mode is 0x8 */
+		u32 switch_mode = hw->dev_caps.switch_mode &
+							I40E_SWITCH_MODE_MASK;
+		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
+			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
+				return 0;
+			dev_err(&pf->pdev->dev,
+				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
+				hw->dev_caps.switch_mode);
+			return -EINVAL;
+		}
+	}
+
+	/* port_type: true for destination port and false for source port
+	 * For now, supports only destination port type
+	 */
+	if (!port_type) {
+		dev_err(&pf->pdev->dev, "src port type not supported\n");
+		return -EINVAL;
+	}
+
+	/* Set Bit 7 to be valid */
+	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
+
+	/* Set L4type to both TCP and UDP support */
+	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
+
+	/* Set cloud filter mode */
+	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
+
+	/* Prep mode field for set_switch_config */
+	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
+					pf->last_sw_conf_valid_flags,
+					mode, NULL);
+	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
+		dev_err(&pf->pdev->dev,
+			"couldn't set switch config bits, err %s aq_err %s\n",
+			i40e_stat_str(hw, ret),
+			i40e_aq_str(hw,
+				    hw->aq.asq_last_status));
+
+	return ret;
+}
+
+/**
  * i40e_create_queue_channel - function to create channel
  * @vsi: VSI to be configured
  * @ch: ptr to channel (it contains channel specific params)
@@ -6632,6 +6790,10 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
 	}
 	if (!hw) {
 		pf->flags &= ~I40E_FLAG_TC_MQPRIO;
+		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
+		    (pf->hw.func_caps.fd_filters_best_effort > 0))
+			pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+
 		if (tc->type == TC_SETUP_MQPRIO_EXT)
 			memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
 			       sizeof(*tc->mqprio_qopt));
@@ -6682,6 +6844,11 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
 		       sizeof(*tc->mqprio_qopt));
 		pf->flags |= I40E_FLAG_TC_MQPRIO;
 		pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
+			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+			dev_info(&pf->pdev->dev,
+				 "Disabling ATR in MQPRIO mode\n");
+		}
 		break;
 	default:
 		return -EINVAL;
@@ -6743,10 +6910,724 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
 	return ret;
 }
 
+/**
+ * i40e_set_cld_element - sets cloud filter element data
+ * @filter: cloud filter rule
+ * @cld: ptr to cloud filter element data
+ *
+ * This is helper function to copy data into cloud filter element
+ **/
+static inline void
+i40e_set_cld_element(struct i40e_cloud_filter *filter,
+		     struct i40e_aqc_add_remove_cloud_filters_element_data *cld)
+{
+	u8 *dest_ipaddr;
+	u32 ipaddr;
+	int i;
+
+	memset(cld, 0, sizeof(*cld));
+
+	ether_addr_copy(cld->outer_mac, filter->dst_mac);
+	ether_addr_copy(cld->inner_mac, filter->src_mac);
+
+	if (filter->is_ipv6) {
+		dest_ipaddr = (u8 *)&cld->ipaddr.v6.data;
+		for (i = ARRAY_SIZE(filter->dst_ipv6) - 1; i >= 0; i--) {
+			memcpy(dest_ipaddr, &filter->dst_ipv6[i], 1);
+			dest_ipaddr++;
+		}
+	} else {
+		ipaddr = be32_to_cpu(filter->dst_ip[0]);
+		memcpy(&cld->ipaddr.v4.data, &ipaddr, 4);
+	}
+
+	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
+	cld->tenant_id = cpu_to_le32(filter->tenant_id);
+}
+
+/**
+ * i40e_add_del_cloud_filter - Add/del cloud filter
+ * @vsi: pointer to VSI
+ * @filter: cloud filter rule
+ * @add: if true, add, if false, delete
+ *
+ * Add or delete a cloud filter for a specific flow spec.
+ * Returns 0 if the filter were successfully added.
+ **/
+static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
+				     struct i40e_cloud_filter *filter, bool add)
+{
+	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
+	struct i40e_pf *pf = vsi->back;
+	int ret;
+	static const u16 flag_table[128] = {
+		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
+			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
+		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
+			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
+		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
+			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
+		[I40E_CLOUD_FILTER_FLAGS_IIP] =
+			I40E_AQC_ADD_CLOUD_FILTER_IIP,
+	};
+
+	if (filter->flags >= ARRAY_SIZE(flag_table))
+		return I40E_ERR_CONFIG;
+
+	/* copy element needed to add cloud filter from filter */
+	i40e_set_cld_element(filter, &cld_filter);
+
+	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
+		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
+					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
+
+	if (filter->is_ipv6)
+		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
+						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
+	else
+		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
+						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
+
+	if (add)
+		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
+						&cld_filter, 1);
+	else
+		ret = i40e_aq_remove_cloud_filters(&pf->hw, filter->seid,
+						   &cld_filter, 1);
+	if (ret)
+		dev_dbg(&pf->pdev->dev,
+			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
+			add ? "add" : "delete", filter->dst_port, ret,
+			pf->hw.aq.asq_last_status);
+
+	dev_info(&pf->pdev->dev,
+		 "%s cloud filter for VSI: %d\n", add ? "Added" : "Deleted",
+		 filter->seid);
+	return ret;
+}
+
+/**
+ * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
+ * @vsi: pointer to VSI
+ * @filter: cloud filter rule
+ * @add: if true, add, if false, delete
+ *
+ * Add or delete a cloud filter for a specific flow spec using big buffer.
+ * Returns 0 if the filter were successfully added.
+ **/
+static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
+					     struct i40e_cloud_filter *filter,
+					     bool add)
+{
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data cld_filter;
+	struct i40e_pf *pf = vsi->back;
+	int ret;
+
+	/* Both (Outer/Inner) valid mac_addr are not supported */
+	if (is_valid_ether_addr(filter->dst_mac) &&
+	    is_valid_ether_addr(filter->src_mac))
+		return -EINVAL;
+
+	/* Make sure port is specified, otherwise bail out, for channel
+	 * specific cloud filter needs 'L4 port' to be non-zero
+	 */
+	if (!filter->dst_port)
+		return -EINVAL;
+
+	/* adding filter using src_port/src_ip is not supported at this stage */
+	if (filter->src_port || filter->src_ip[0])
+		return -EINVAL;
+
+	/* copy element needed to add cloud filter from filter */
+	i40e_set_cld_element(filter, &cld_filter.element);
+
+	if (is_valid_ether_addr(filter->dst_mac) ||
+	    is_valid_ether_addr(filter->src_mac) ||
+	    is_multicast_ether_addr(filter->dst_mac) ||
+	    is_multicast_ether_addr(filter->src_mac)) {
+		/* MAC + IP : unsupported mode */
+		if (filter->dst_ip[0])
+			return -EINVAL;
+
+		/* since we validated that L4 port must be valid before
+		 * we get here, start with respective "flags" value
+		 * and update if vlan is present or not
+		 */
+		cld_filter.element.flags =
+			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
+
+		if (filter->vlan_id) {
+			cld_filter.element.flags =
+			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
+		}
+
+	} else if (filter->dst_ip[0] || filter->is_ipv6) {
+		cld_filter.element.flags =
+				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
+		if (filter->is_ipv6)
+			cld_filter.element.flags |=
+				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
+		else
+			cld_filter.element.flags |=
+				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
+	} else {
+		dev_err(&pf->pdev->dev,
+			"either mac or ip has to be valid for cloud filter\n");
+		return -EINVAL;
+	}
+
+	/* Now copy L4 port in Byte 6..7 in general fields */
+	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
+						be16_to_cpu(filter->dst_port);
+
+	if (add) {
+		bool proto_type, port_type;
+
+		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
+		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
+			     true : false;
+
+		/* For now, src port based cloud filter for channel is not
+		 * supported
+		 */
+		if (!port_type) {
+			dev_err(&pf->pdev->dev,
+				"unsupported port type (src port)\n");
+			return -EOPNOTSUPP;
+		}
+
+		/* Validate current device switch mode, change if necessary */
+		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
+							port_type);
+		if (ret) {
+			dev_err(&pf->pdev->dev,
+				"fail to and set switch mode, ret %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = i40e_aq_add_cloud_filters_big_buffer(&pf->hw,
+							   filter->seid,
+							   &cld_filter, 1);
+	} else {
+		ret = i40e_aq_remove_cloud_filters_big_buffer(&pf->hw,
+							      filter->seid,
+							      &cld_filter, 1);
+	}
+
+	if (ret)
+		dev_dbg(&pf->pdev->dev,
+			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
+			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
+
+	dev_info(&pf->pdev->dev,
+		 "%s cloud filter for VSI: %d, L4 port: %d\n",
+		 add ? "add" : "delete", filter->seid, ntohs(filter->dst_port));
+
+	return ret;
+}
+
+/**
+ * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ * @filter: Pointer to cloud filter structure
+ *
+ **/
+static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
+				 struct tc_cls_flower_offload *f,
+				 struct i40e_cloud_filter *filter)
+{
+	struct i40e_pf *pf = vsi->back;
+	u16 addr_type = 0;
+	u8 field_flags = 0;
+
+	if (f->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_PORTS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
+	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
+		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
+			f->dissector->used_keys);
+		return -EOPNOTSUPP;
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+		struct flow_dissector_key_keyid *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ENC_KEYID,
+						  f->key);
+
+		struct flow_dissector_key_keyid *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ENC_KEYID,
+						  f->mask);
+
+		if (mask->keyid != 0)
+			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
+
+		filter->tenant_id = be32_to_cpu(key->keyid);
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+		struct flow_dissector_key_basic *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_BASIC,
+						  f->key);
+
+		filter->ip_proto = key->ip_proto;
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+		struct flow_dissector_key_eth_addrs *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
+						  f->key);
+
+		struct flow_dissector_key_eth_addrs *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
+						  f->mask);
+
+		/* use is_broadcast and is_zero to check for all 0xf or 0 */
+		if (!is_zero_ether_addr(mask->dst)) {
+			if (is_broadcast_ether_addr(mask->dst)) {
+				field_flags |= I40E_CLOUD_FIELD_OMAC;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
+					mask->dst);
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (!is_zero_ether_addr(mask->src)) {
+			if (is_broadcast_ether_addr(mask->src)) {
+				field_flags |= I40E_CLOUD_FIELD_IMAC;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
+					mask->src);
+				return I40E_ERR_CONFIG;
+			}
+		}
+		ether_addr_copy(filter->dst_mac, key->dst);
+		ether_addr_copy(filter->src_mac, key->src);
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
+		struct flow_dissector_key_vlan *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_VLAN,
+						  f->key);
+		struct flow_dissector_key_vlan *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_VLAN,
+						  f->mask);
+
+		if (mask->vlan_id) {
+			if (mask->vlan_id == VLAN_VID_MASK) {
+				field_flags |= I40E_CLOUD_FIELD_IVLAN;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad vlan mask %u\n",
+					mask->vlan_id);
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		filter->vlan_id = cpu_to_be16(key->vlan_id);
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+		struct flow_dissector_key_control *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_CONTROL,
+						  f->key);
+
+		addr_type = key->addr_type;
+	}
+
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+		struct flow_dissector_key_ipv4_addrs *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+						  f->key);
+		struct flow_dissector_key_ipv4_addrs *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+						  f->mask);
+
+		if (mask->dst) {
+			if (mask->dst == cpu_to_be32(0xffffffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
+					be32_to_cpu(mask->dst));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (mask->src) {
+			if (mask->src == cpu_to_be32(0xffffffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
+					be32_to_cpu(mask->dst));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
+			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
+			return I40E_ERR_CONFIG;
+		}
+		filter->dst_ip[0] = key->dst;
+		filter->src_ip[0] = key->src;
+	}
+
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+		struct flow_dissector_key_ipv6_addrs *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+						  f->key);
+		struct flow_dissector_key_ipv6_addrs *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+						  f->mask);
+
+		/* validate mask, make sure it is not IPV6_ADDR_ANY */
+		if (ipv6_addr_any(&mask->dst)) {
+			dev_err(&pf->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
+				IPV6_ADDR_ANY);
+			return I40E_ERR_CONFIG;
+		}
+
+		/* validate src and dest IPV6 address, make sure they are not
+		 * ANY (0:0:0:0:0:0:0:0) or LOOPBACK (0:0:0:0:0:0:0:1), which
+		 * can be represented as ::1
+		 */
+		if (ipv6_addr_any(&key->dst) || ipv6_addr_loopback(&key->dst)) {
+			dev_err(&pf->pdev->dev,
+				"Bad ipv6 dst addr is ANY or LOOPBACK\n");
+			return I40E_ERR_CONFIG;
+		}
+		if (ipv6_addr_loopback(&key->src)) {
+			dev_err(&pf->pdev->dev,
+				"Bad ipv6 src addr is ANY or LOOPBACK\n");
+			return I40E_ERR_CONFIG;
+		}
+		memcpy(&filter->src_ipv6, &key->src.s6_addr,
+		       ARRAY_SIZE(filter->src_ipv6));
+		memcpy(&filter->dst_ipv6, &key->dst.s6_addr,
+		       ARRAY_SIZE(filter->dst_ipv6));
+
+		/* mark it as IPv6 filter, to be used later */
+		filter->is_ipv6 = true;
+
+		/* and it is IP[4|6] filter type */
+		field_flags |= I40E_CLOUD_FIELD_IIP;
+	}
+
+	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
+		struct flow_dissector_key_ports *key =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_PORTS,
+						  f->key);
+		struct flow_dissector_key_ports *mask =
+			skb_flow_dissector_target(f->dissector,
+						  FLOW_DISSECTOR_KEY_PORTS,
+						  f->mask);
+
+		if (mask->src) {
+			if (mask->src == cpu_to_be16(0xffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad src port mask %u\n",
+					be16_to_cpu(mask->src));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		if (mask->dst) {
+			if (mask->dst == cpu_to_be16(0xffff)) {
+				field_flags |= I40E_CLOUD_FIELD_IIP;
+			} else {
+				dev_err(&pf->pdev->dev, "Bad dst port mask %u\n",
+					be16_to_cpu(mask->dst));
+				return I40E_ERR_CONFIG;
+			}
+		}
+
+		filter->dst_port = key->dst;
+		filter->src_port = key->src;
+
+		/* For now, only supports destination port*/
+		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
+
+		switch (filter->ip_proto) {
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
+			break;
+		default:
+			dev_err(&pf->pdev->dev,
+				"Only UDP and TCP transport are supported\n");
+			return -EINVAL;
+		}
+	}
+	filter->flags = field_flags;
+	return 0;
+}
+
+/**
+ * i40e_handle_redirect_action: Forward to a traffic class on the device
+ * @vsi: Pointer to VSI
+ * @ifindex: ifindex of the device to forwared to
+ * @tc: traffic class index on the device
+ * @filter: Pointer to cloud filter structure
+ *
+ **/
+static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
+				       struct i40e_cloud_filter *filter)
+{
+	struct i40e_channel *ch, *ch_tmp;
+
+	/* redirect to a traffic class on the same device */
+	if (vsi->netdev->ifindex == ifindex) {
+		if (tc == 0) {
+			filter->seid = vsi->seid;
+			return 0;
+		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
+			if (!filter->dst_port) {
+				dev_err(&vsi->back->pdev->dev,
+					"Specify destination port to redirect to traffic class that is not default\n");
+				return -EINVAL;
+			}
+			if (list_empty(&vsi->ch_list))
+				return -EINVAL;
+			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
+						 list) {
+				if (ch->seid == vsi->tc_seid_map[tc])
+					filter->seid = ch->seid;
+			}
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+/**
+ * i40e_parse_tc_actions - Parse tc actions
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ * @filter: Pointer to cloud filter structure
+ *
+ **/
+static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
+				 struct i40e_cloud_filter *filter)
+{
+	const struct tc_action *a;
+	LIST_HEAD(actions);
+	int err;
+
+	if (tc_no_actions(exts))
+		return -EINVAL;
+
+	tcf_exts_to_list(exts, &actions);
+	list_for_each_entry(a, &actions, list) {
+		/* Drop action */
+		if (is_tcf_gact_shot(a)) {
+			dev_err(&vsi->back->pdev->dev,
+				"Cloud filters do not support the drop action.\n");
+			return -EOPNOTSUPP;
+		}
+
+		/* Redirect to a traffic class on the same device */
+		if (!is_tcf_mirred_egress_redirect(a)) {
+			int ifindex = tcf_mirred_ifindex(a);
+			int tc = tcf_mirred_tc(a);
+
+			err = i40e_handle_redirect_action(vsi, ifindex, tc,
+							  filter);
+			if (err == 0)
+				return err;
+		}
+	}
+	return -EINVAL;
+}
+
+/**
+ * i40e_configure_clsflower - Configure tc flower filters
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ *
+ **/
+static int i40e_configure_clsflower(struct i40e_vsi *vsi,
+				    struct tc_cls_flower_offload *cls_flower)
+{
+	struct i40e_cloud_filter *filter = NULL;
+	struct i40e_pf *pf = vsi->back;
+	int err = 0;
+
+	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
+	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
+		return -EBUSY;
+
+	if (pf->fdir_pf_active_filters ||
+	    (!hlist_empty(&pf->fdir_filter_list))) {
+		dev_err(&vsi->back->pdev->dev,
+			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
+		return -EINVAL;
+	}
+
+	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
+		dev_err(&vsi->back->pdev->dev,
+			"Disable Flow Director Sideband while configuring Cloud filters via tc-flower\n");
+		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+	}
+
+	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+	if (!filter)
+		return -ENOMEM;
+
+	filter->cookie = cls_flower->cookie;
+
+	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
+	if (err < 0)
+		goto err;
+
+	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
+	if (err < 0)
+		goto err;
+
+	/* Add cloud filter */
+	if (filter->dst_port)
+		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
+	else
+		err = i40e_add_del_cloud_filter(vsi, filter, true);
+
+	if (err) {
+		dev_err(&pf->pdev->dev,
+			"Failed to add cloud filter, err %s\n",
+			i40e_stat_str(&pf->hw, err));
+		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
+		goto err;
+	}
+
+	/* add filter to the ordered list */
+	INIT_HLIST_NODE(&filter->cloud_node);
+
+	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
+
+	pf->num_cloud_filters++;
+
+	return err;
+err:
+	kfree(filter);
+	return err;
+}
+
+/**
+ * i40e_find_cloud_filter - Find the could filter in the list
+ * @vsi: Pointer to VSI
+ * @cookie: filter specific cookie
+ *
+ **/
+static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
+							unsigned long *cookie)
+{
+	struct i40e_cloud_filter *filter = NULL;
+	struct hlist_node *node2;
+
+	hlist_for_each_entry_safe(filter, node2,
+				  &vsi->back->cloud_filter_list, cloud_node)
+		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
+			return filter;
+	return NULL;
+}
+
+/**
+ * i40e_delete_clsflower - Remove tc flower filters
+ * @vsi: Pointer to VSI
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ *
+ **/
+static int i40e_delete_clsflower(struct i40e_vsi *vsi,
+				 struct tc_cls_flower_offload *cls_flower)
+{
+	struct i40e_cloud_filter *filter = NULL;
+	struct i40e_pf *pf = vsi->back;
+	int err = 0;
+
+	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
+
+	if (!filter)
+		return -EINVAL;
+
+	hash_del(&filter->cloud_node);
+
+	if (filter->dst_port)
+		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
+	else
+		err = i40e_add_del_cloud_filter(vsi, filter, false);
+	if (err) {
+		kfree(filter);
+		dev_err(&pf->pdev->dev,
+			"Failed to delete cloud filter, err %s\n",
+			i40e_stat_str(&pf->hw, err));
+		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
+	}
+
+	kfree(filter);
+	pf->num_cloud_filters--;
+
+	if (!pf->num_cloud_filters) {
+		/* Re-enable FD-SB that was disabled while configuring cloud
+		 * filters
+		 */
+		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
+		    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
+			if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
+			      pf->hw.num_partitions > 1))
+				pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+		}
+	}
+
+	return 0;
+}
+
 static int __i40e_setup_tc(struct net_device *netdev, u32 handle,
 			   u32 chain_index, __be16 proto,
 			   struct tc_to_netdev *tc)
 {
+	struct i40e_netdev_priv *np = netdev_priv(netdev);
+	struct i40e_vsi *vsi = np->vsi;
+
+	if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
+	    tc->type == TC_SETUP_CLSFLOWER) {
+		switch (tc->cls_flower->command) {
+		case TC_CLSFLOWER_REPLACE:
+			return i40e_configure_clsflower(vsi, tc->cls_flower);
+		case TC_CLSFLOWER_DESTROY:
+			return i40e_delete_clsflower(vsi, tc->cls_flower);
+		case TC_CLSFLOWER_STATS:
+			return -EOPNOTSUPP;
+		default:
+			return -EINVAL;
+		}
+	}
+
 	return i40e_setup_tc(netdev, tc);
 }
 
@@ -6948,6 +7829,16 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
 		kfree(cfilter);
 	}
 	pf->num_cloud_filters = 0;
+
+	/* Re-enable FD-SB that was disabled while configuring cloud
+	 * filters
+	 */
+	if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
+	    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
+		if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
+		      pf->hw.num_partitions > 1))
+			pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+	}
 }
 
 /**
@@ -8157,6 +9048,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
 }
 
 /**
+ * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
+ * @vsi: PF main vsi
+ * @seid: seid of main or channel VSIs
+ *
+ * Rebuilds cloud filters associated with main VSI and channel VSIs if they
+ * existed before reset
+ **/
+static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
+{
+	struct i40e_cloud_filter *cfilter;
+	struct i40e_pf *pf = vsi->back;
+	struct hlist_node *node;
+	i40e_status ret;
+
+	/* Add cloud filters back if they exist */
+	if (hlist_empty(&pf->cloud_filter_list))
+		return 0;
+
+	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
+				  cloud_node) {
+		if (cfilter->seid != seid)
+			continue;
+
+		if (cfilter->dst_port)
+			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
+								true);
+		else
+			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
+
+		if (ret) {
+			dev_dbg(&pf->pdev->dev,
+				"Failed to rebuild cloud filter, err %s aq_err %s\n",
+				i40e_stat_str(&pf->hw, ret),
+				i40e_aq_str(&pf->hw,
+					    pf->hw.aq.asq_last_status));
+			return ret;
+		}
+	}
+	return 0;
+}
+
+/**
  * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
  * @vsi: PF main vsi
  *
@@ -8193,6 +9126,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
 						I40E_BW_CREDIT_DIVISOR,
 				ch->seid);
 		}
+		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
+		if (ret) {
+			dev_dbg(&vsi->back->pdev->dev,
+				"Failed to rebuild cloud filters for channel VSI %u\n",
+				ch->seid);
+			return ret;
+		}
 	}
 	return 0;
 }
@@ -8476,6 +9416,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
 			goto end_unlock;
 	}
 
+	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
+	if (ret)
+		goto end_unlock;
+
 	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
 	 * for this main VSI if they exist
 	 */
@@ -10846,7 +11790,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
 		netdev->hw_features |= NETIF_F_NTUPLE;
 	hw_features = hw_enc_features		|
 		      NETIF_F_HW_VLAN_CTAG_TX	|
-		      NETIF_F_HW_VLAN_CTAG_RX;
+		      NETIF_F_HW_VLAN_CTAG_RX	|
+		      NETIF_F_HW_TC;
 
 	netdev->hw_features |= hw_features;
 
@@ -12123,8 +13068,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 	*/
 
 	if ((pf->hw.pf_id == 0) &&
-	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
+	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
 		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
+		pf->last_sw_conf_flags = flags;
+	}
 
 	if (pf->hw.pf_id == 0) {
 		u16 valid_flags;
@@ -12140,6 +13087,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
 					     pf->hw.aq.asq_last_status));
 			/* not a fatal problem, just keep going */
 		}
+		pf->last_sw_conf_valid_flags = valid_flags;
 	}
 
 	/* first time setup */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index 9142d0d..e24f1ce 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -283,6 +283,23 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
 		struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
 				   struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
+	u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count);
+enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
+		u16 vsi,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count);
+
+enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
+		u16 vsi,
+		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
+		u8 filter_count);
+i40e_status i40e_aq_remove_cloud_filters_big_buffer(
+	struct i40e_hw *hw, u16 seid,
+	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
+	u8 filter_count);
 i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
 			       struct i40e_lldp_variables *lldp_cfg);
 /* i40e_common */


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 10:15   ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:15 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz

On 17-07-31 08:36 PM, Amritha Nambiar wrote:
> This patch series enables configuring cloud filters in i40e
> using the tc/flower classifier. The only tc-filter action
> supported is to redirect packets to a traffic class on the
> same device. The tc/mirred:redirect action is extended to
> accept a traffic class to achieve this.
> 
> The cloud filters are added for a VSI and are cleaned up when
> the VSI is deleted. The filters that match on L4 ports needs
> enhanced admin queue functions with big buffer support for
> extended general fields in Add/Remove Cloud filters command.
> 
> Example:
> # tc qdisc add dev eth0 ingress
> 
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 

I think "queue 1" sounds better than "tc 1".
"tc" is  already a keyword in a few places (even within that declaration
above).

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-01 10:15   ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:15 UTC (permalink / raw)
  To: intel-wired-lan

On 17-07-31 08:36 PM, Amritha Nambiar wrote:
> This patch series enables configuring cloud filters in i40e
> using the tc/flower classifier. The only tc-filter action
> supported is to redirect packets to a traffic class on the
> same device. The tc/mirred:redirect action is extended to
> accept a traffic class to achieve this.
> 
> The cloud filters are added for a VSI and are cleaned up when
> the VSI is deleted. The filters that match on L4 ports needs
> enhanced admin queue functions with big buffer support for
> extended general fields in Add/Remove Cloud filters command.
> 
> Example:
> # tc qdisc add dev eth0 ingress
> 
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 

I think "queue 1" sounds better than "tc 1".
"tc" is  already a keyword in a few places (even within that declaration
above).

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 10:22     ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:22 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny

On 17-07-31 08:37 PM, Amritha Nambiar wrote:
> The Mirred/redirect action is extended to forward to a traffic
> class on the device. The traffic class index needs to be
> provided in addition to the device's ifindex.
> 
> Example:
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>   3 files changed, 29 insertions(+)
> 
> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
> index 604bc31..60058c4 100644
> --- a/include/net/tc_act/tc_mirred.h
> +++ b/include/net/tc_act/tc_mirred.h
> @@ -9,6 +9,8 @@ struct tcf_mirred {
>   	int			tcfm_eaction;
>   	int			tcfm_ifindex;
>   	bool			tcfm_mac_header_xmit;
> +	u8			tcfm_tc;
> +	u32			flags;
>   	struct net_device __rcu	*tcfm_dev;
>   	struct list_head	tcfm_list;
>   };
> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>   	return to_mirred(a)->tcfm_ifindex;
>   }
>   
> +static inline int tcf_mirred_tc(const struct tc_action *a)
> +{
> +	return to_mirred(a)->tcfm_tc;
> +}
> +
>   #endif /* __NET_TC_MIR_H */
> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
> index 3d7a2b3..8ff4d76 100644
> --- a/include/uapi/linux/tc_act/tc_mirred.h
> +++ b/include/uapi/linux/tc_act/tc_mirred.h
> @@ -9,6 +9,10 @@
>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
> +
> +#define MIRRED_F_TC_MAP		0x1
> +#define MIRRED_TC_MAP_MAX	0x10

Assuming this is the max number of queues?
Where does this upper bound come from? Is it a spec
or an intel thing? If spec - mentioning which
spec and section would be useful.

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-01 10:22     ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:22 UTC (permalink / raw)
  To: intel-wired-lan

On 17-07-31 08:37 PM, Amritha Nambiar wrote:
> The Mirred/redirect action is extended to forward to a traffic
> class on the device. The traffic class index needs to be
> provided in addition to the device's ifindex.
> 
> Example:
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>   3 files changed, 29 insertions(+)
> 
> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
> index 604bc31..60058c4 100644
> --- a/include/net/tc_act/tc_mirred.h
> +++ b/include/net/tc_act/tc_mirred.h
> @@ -9,6 +9,8 @@ struct tcf_mirred {
>   	int			tcfm_eaction;
>   	int			tcfm_ifindex;
>   	bool			tcfm_mac_header_xmit;
> +	u8			tcfm_tc;
> +	u32			flags;
>   	struct net_device __rcu	*tcfm_dev;
>   	struct list_head	tcfm_list;
>   };
> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>   	return to_mirred(a)->tcfm_ifindex;
>   }
>   
> +static inline int tcf_mirred_tc(const struct tc_action *a)
> +{
> +	return to_mirred(a)->tcfm_tc;
> +}
> +
>   #endif /* __NET_TC_MIR_H */
> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
> index 3d7a2b3..8ff4d76 100644
> --- a/include/uapi/linux/tc_act/tc_mirred.h
> +++ b/include/uapi/linux/tc_act/tc_mirred.h
> @@ -9,6 +9,10 @@
>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
> +
> +#define MIRRED_F_TC_MAP		0x1
> +#define MIRRED_TC_MAP_MAX	0x10

Assuming this is the max number of queues?
Where does this upper bound come from? Is it a spec
or an intel thing? If spec - mentioning which
spec and section would be useful.

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 10:44     ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:44 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny

On 17-07-31 08:37 PM, Amritha Nambiar wrote:
> The Mirred/redirect action is extended to forward to a traffic
> class on the device. The traffic class index needs to be
> provided in addition to the device's ifindex.
> 
> Example:
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>   3 files changed, 29 insertions(+)
> 
> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
> index 604bc31..60058c4 100644
> --- a/include/net/tc_act/tc_mirred.h
> +++ b/include/net/tc_act/tc_mirred.h
> @@ -9,6 +9,8 @@ struct tcf_mirred {
>   	int			tcfm_eaction;
>   	int			tcfm_ifindex;
>   	bool			tcfm_mac_header_xmit;
> +	u8			tcfm_tc;
> +	u32			flags;
>   	struct net_device __rcu	*tcfm_dev;
>   	struct list_head	tcfm_list;
>   };
> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>   	return to_mirred(a)->tcfm_ifindex;
>   }
>   
> +static inline int tcf_mirred_tc(const struct tc_action *a)
> +{
> +	return to_mirred(a)->tcfm_tc;
> +}
> +
>   #endif /* __NET_TC_MIR_H */
> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
> index 3d7a2b3..8ff4d76 100644
> --- a/include/uapi/linux/tc_act/tc_mirred.h
> +++ b/include/uapi/linux/tc_act/tc_mirred.h
> @@ -9,6 +9,10 @@
>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
> +
> +#define MIRRED_F_TC_MAP		0x1
> +#define MIRRED_TC_MAP_MAX	0x10
> +#define MIRRED_TC_MAP_MASK	0xF
>                                                                                   
>   struct tc_mirred {
>   	tc_gen;
> @@ -21,6 +25,7 @@ enum {
>   	TCA_MIRRED_TM,
>   	TCA_MIRRED_PARMS,
>   	TCA_MIRRED_PAD,
> +	TCA_MIRRED_TC_MAP,
>   	__TCA_MIRRED_MAX
>   };
>   #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
> index 1b5549a..f9801de 100644
> --- a/net/sched/act_mirred.c
> +++ b/net/sched/act_mirred.c
> @@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
>   
>   static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
>   	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
> +	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
>   };
>   
>   static unsigned int mirred_net_id;
> @@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   	struct tcf_mirred *m;
>   	struct net_device *dev;
>   	bool exists = false;
> +	u8 *tc_map = NULL;
> +	u32 flags = 0;
>   	int ret;
>   
>   	if (nla == NULL)
> @@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   		return ret;
>   	if (tb[TCA_MIRRED_PARMS] == NULL)
>   		return -EINVAL;
> +
> +	if (tb[TCA_MIRRED_TC_MAP]) {
> +		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
> +		if (*tc_map >= MIRRED_TC_MAP_MAX)
> +			return -EINVAL;
> +		flags |= MIRRED_F_TC_MAP;


> +	}
> +
>   	parm = nla_data(tb[TCA_MIRRED_PARMS]);
>   
>   	exists = tcf_hash_check(tn, parm->index, a, bind);
> @@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   	ASSERT_RTNL();
>   	m->tcf_action = parm->action;
>   	m->tcfm_eaction = parm->eaction;
> +	m->flags = flags;
>   	if (dev != NULL) {
>   		m->tcfm_ifindex = parm->ifindex;
>   		if (ret != ACT_P_CREATED)
> @@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   		dev_hold(dev);
>   		rcu_assign_pointer(m->tcfm_dev, dev);
>   		m->tcfm_mac_header_xmit = mac_header_xmit;
> +		if (flags & MIRRED_F_TC_MAP)
> +			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
>   	}
>   
Is the mask a hardware limit. I dont know how these queues are
allocated - I am assuming each of these "tc queues" maps to a rx
DMA ring?

>   	if (ret == ACT_P_CREATED) {
> @@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
>   
>   	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
>   		goto nla_put_failure;
> +	if ((m->flags & MIRRED_F_TC_MAP) &&
> +	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
> +		goto nla_put_failure;
>

If you have m->tcfm_tc then I dont think you need the flags; so i would
remove it from the struct altogether.

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-01 10:44     ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:44 UTC (permalink / raw)
  To: intel-wired-lan

On 17-07-31 08:37 PM, Amritha Nambiar wrote:
> The Mirred/redirect action is extended to forward to a traffic
> class on the device. The traffic class index needs to be
> provided in addition to the device's ifindex.
> 
> Example:
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>   3 files changed, 29 insertions(+)
> 
> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
> index 604bc31..60058c4 100644
> --- a/include/net/tc_act/tc_mirred.h
> +++ b/include/net/tc_act/tc_mirred.h
> @@ -9,6 +9,8 @@ struct tcf_mirred {
>   	int			tcfm_eaction;
>   	int			tcfm_ifindex;
>   	bool			tcfm_mac_header_xmit;
> +	u8			tcfm_tc;
> +	u32			flags;
>   	struct net_device __rcu	*tcfm_dev;
>   	struct list_head	tcfm_list;
>   };
> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>   	return to_mirred(a)->tcfm_ifindex;
>   }
>   
> +static inline int tcf_mirred_tc(const struct tc_action *a)
> +{
> +	return to_mirred(a)->tcfm_tc;
> +}
> +
>   #endif /* __NET_TC_MIR_H */
> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
> index 3d7a2b3..8ff4d76 100644
> --- a/include/uapi/linux/tc_act/tc_mirred.h
> +++ b/include/uapi/linux/tc_act/tc_mirred.h
> @@ -9,6 +9,10 @@
>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
> +
> +#define MIRRED_F_TC_MAP		0x1
> +#define MIRRED_TC_MAP_MAX	0x10
> +#define MIRRED_TC_MAP_MASK	0xF
>                                                                                   
>   struct tc_mirred {
>   	tc_gen;
> @@ -21,6 +25,7 @@ enum {
>   	TCA_MIRRED_TM,
>   	TCA_MIRRED_PARMS,
>   	TCA_MIRRED_PAD,
> +	TCA_MIRRED_TC_MAP,
>   	__TCA_MIRRED_MAX
>   };
>   #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
> index 1b5549a..f9801de 100644
> --- a/net/sched/act_mirred.c
> +++ b/net/sched/act_mirred.c
> @@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
>   
>   static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
>   	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
> +	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
>   };
>   
>   static unsigned int mirred_net_id;
> @@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   	struct tcf_mirred *m;
>   	struct net_device *dev;
>   	bool exists = false;
> +	u8 *tc_map = NULL;
> +	u32 flags = 0;
>   	int ret;
>   
>   	if (nla == NULL)
> @@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   		return ret;
>   	if (tb[TCA_MIRRED_PARMS] == NULL)
>   		return -EINVAL;
> +
> +	if (tb[TCA_MIRRED_TC_MAP]) {
> +		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
> +		if (*tc_map >= MIRRED_TC_MAP_MAX)
> +			return -EINVAL;
> +		flags |= MIRRED_F_TC_MAP;


> +	}
> +
>   	parm = nla_data(tb[TCA_MIRRED_PARMS]);
>   
>   	exists = tcf_hash_check(tn, parm->index, a, bind);
> @@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   	ASSERT_RTNL();
>   	m->tcf_action = parm->action;
>   	m->tcfm_eaction = parm->eaction;
> +	m->flags = flags;
>   	if (dev != NULL) {
>   		m->tcfm_ifindex = parm->ifindex;
>   		if (ret != ACT_P_CREATED)
> @@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>   		dev_hold(dev);
>   		rcu_assign_pointer(m->tcfm_dev, dev);
>   		m->tcfm_mac_header_xmit = mac_header_xmit;
> +		if (flags & MIRRED_F_TC_MAP)
> +			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
>   	}
>   
Is the mask a hardware limit. I dont know how these queues are
allocated - I am assuming each of these "tc queues" maps to a rx
DMA ring?

>   	if (ret == ACT_P_CREATED) {
> @@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
>   
>   	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
>   		goto nla_put_failure;
> +	if ((m->flags & MIRRED_F_TC_MAP) &&
> +	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
> +		goto nla_put_failure;
>

If you have m->tcfm_tc then I dont think you need the flags; so i would
remove it from the struct altogether.

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 3/6] [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
  2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 10:48     ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:48 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny

On 17-07-31 08:37 PM, Amritha Nambiar wrote:
> Add definitions for L4 filters and switch modes based on cloud filters
> modes and extend the set switch config command to include the
> additional cloud filter mode.
> 

"Cloud filters"? Seriously? Is "enteprise" the next one? ;->

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 3/6] [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
@ 2017-08-01 10:48     ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:48 UTC (permalink / raw)
  To: intel-wired-lan

On 17-07-31 08:37 PM, Amritha Nambiar wrote:
> Add definitions for L4 filters and switch modes based on cloud filters
> modes and extend the set switch config command to include the
> additional cloud filter mode.
> 

"Cloud filters"? Seriously? Is "enteprise" the next one? ;->

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-01  0:38   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 10:56     ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:56 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz

On 17-07-31 08:38 PM, Amritha Nambiar wrote:
> This patch enables tc-flower based hardware offloads. tc/flower
> filter provided by the kernel is configured as driver specific
> cloud filter. The patch implements functions and admin queue
> commands needed to support cloud filters in the driver and
> adds cloud filters to configure these tc-flower filters.
> 
> The only action supported is to redirect packets to a traffic class
> on the same device.
> 
> # tc qdisc add dev eth0 ingress
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff:\
>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 0
> 

Out of curiosity - did you need to say "indev eth0" there?
Also: Is it possible to add an skbmark? Example something like
these that directs two flows to the same queue but different
skb marks:

# tc filter add dev eth0 protocol ip parent ffff: \
   prio 2 flower dst_ip 192.168.3.5/32 \
   ip_proto udp dst_port 2a skip_sw \
   action skbedit mark 11 \
   action mirred ingress redirect dev eth0 tcqueue 1

# tc filter add dev eth0 protocol ip parent ffff: \
     prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
     action skbedit mark 12 \
     action mirred ingress redirect dev eth0 tcqueue 1

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-01 10:56     ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-01 10:56 UTC (permalink / raw)
  To: intel-wired-lan

On 17-07-31 08:38 PM, Amritha Nambiar wrote:
> This patch enables tc-flower based hardware offloads. tc/flower
> filter provided by the kernel is configured as driver specific
> cloud filter. The patch implements functions and admin queue
> commands needed to support cloud filters in the driver and
> adds cloud filters to configure these tc-flower filters.
> 
> The only action supported is to redirect packets to a traffic class
> on the same device.
> 
> # tc qdisc add dev eth0 ingress
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff:\
>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 0
> 

Out of curiosity - did you need to say "indev eth0" there?
Also: Is it possible to add an skbmark? Example something like
these that directs two flows to the same queue but different
skb marks:

# tc filter add dev eth0 protocol ip parent ffff: \
   prio 2 flower dst_ip 192.168.3.5/32 \
   ip_proto udp dst_port 2a skip_sw \
   action skbedit mark 11 \
   action mirred ingress redirect dev eth0 tcqueue 1

# tc filter add dev eth0 protocol ip parent ffff: \
     prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
     action skbedit mark 12 \
     action mirred ingress redirect dev eth0 tcqueue 1

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 11:12     ` Jiri Pirko
  -1 siblings, 0 replies; 58+ messages in thread
From: Jiri Pirko @ 2017-08-01 11:12 UTC (permalink / raw)
  To: Amritha Nambiar
  Cc: intel-wired-lan, jeffrey.t.kirsher, alexander.h.duyck,
	kiran.patil, netdev, mitch.a.williams, alexander.duyck,
	neerav.parikh, sridhar.samudrala, carolyn.wyborny

Tue, Aug 01, 2017 at 02:37:37AM CEST, amritha.nambiar@intel.com wrote:
>The Mirred/redirect action is extended to forward to a traffic
>class on the device. The traffic class index needs to be
>provided in addition to the device's ifindex.
>
>Example:
># tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1

You need to make sure that the current offloaders fill forbid to add
this rule, not just silently ignore the tc value.


>
>Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>---
> include/net/tc_act/tc_mirred.h        |    7 +++++++
> include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
> net/sched/act_mirred.c                |   17 +++++++++++++++++
> 3 files changed, 29 insertions(+)
>
>diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>index 604bc31..60058c4 100644
>--- a/include/net/tc_act/tc_mirred.h
>+++ b/include/net/tc_act/tc_mirred.h
>@@ -9,6 +9,8 @@ struct tcf_mirred {
> 	int			tcfm_eaction;
> 	int			tcfm_ifindex;
> 	bool			tcfm_mac_header_xmit;
>+	u8			tcfm_tc;
>+	u32			flags;
> 	struct net_device __rcu	*tcfm_dev;
> 	struct list_head	tcfm_list;
> };
>@@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
> 	return to_mirred(a)->tcfm_ifindex;
> }
> 
>+static inline int tcf_mirred_tc(const struct tc_action *a)
>+{
>+	return to_mirred(a)->tcfm_tc;
>+}
>+
> #endif /* __NET_TC_MIR_H */
>diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>index 3d7a2b3..8ff4d76 100644
>--- a/include/uapi/linux/tc_act/tc_mirred.h
>+++ b/include/uapi/linux/tc_act/tc_mirred.h
>@@ -9,6 +9,10 @@
> #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
> #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
> #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>+
>+#define MIRRED_F_TC_MAP		0x1
>+#define MIRRED_TC_MAP_MAX	0x10
>+#define MIRRED_TC_MAP_MASK	0xF

I'm completely lost. Why do you have these values here? and in fact one
twice?


>                                                                                 
> struct tc_mirred {
> 	tc_gen;
>@@ -21,6 +25,7 @@ enum {
> 	TCA_MIRRED_TM,
> 	TCA_MIRRED_PARMS,
> 	TCA_MIRRED_PAD,
>+	TCA_MIRRED_TC_MAP,
> 	__TCA_MIRRED_MAX
> };
> #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
>diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
>index 1b5549a..f9801de 100644
>--- a/net/sched/act_mirred.c
>+++ b/net/sched/act_mirred.c
>@@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
> 
> static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
> 	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
>+	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
> };
> 
> static unsigned int mirred_net_id;
>@@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 	struct tcf_mirred *m;
> 	struct net_device *dev;
> 	bool exists = false;
>+	u8 *tc_map = NULL;
>+	u32 flags = 0;
> 	int ret;
> 
> 	if (nla == NULL)
>@@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 		return ret;
> 	if (tb[TCA_MIRRED_PARMS] == NULL)
> 		return -EINVAL;
>+
>+	if (tb[TCA_MIRRED_TC_MAP]) {
>+		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
>+		if (*tc_map >= MIRRED_TC_MAP_MAX)
>+			return -EINVAL;
>+		flags |= MIRRED_F_TC_MAP;
>+	}
>+
> 	parm = nla_data(tb[TCA_MIRRED_PARMS]);
> 
> 	exists = tcf_hash_check(tn, parm->index, a, bind);
>@@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 	ASSERT_RTNL();
> 	m->tcf_action = parm->action;
> 	m->tcfm_eaction = parm->eaction;
>+	m->flags = flags;
> 	if (dev != NULL) {
> 		m->tcfm_ifindex = parm->ifindex;
> 		if (ret != ACT_P_CREATED)
>@@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 		dev_hold(dev);
> 		rcu_assign_pointer(m->tcfm_dev, dev);
> 		m->tcfm_mac_header_xmit = mac_header_xmit;
>+		if (flags & MIRRED_F_TC_MAP)
>+			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
> 	}
> 
> 	if (ret == ACT_P_CREATED) {
>@@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
> 
> 	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
> 		goto nla_put_failure;
>+	if ((m->flags & MIRRED_F_TC_MAP) &&
>+	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
>+		goto nla_put_failure;
> 
> 	tcf_tm_dump(&t, &m->tcf_tm);
> 	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-01 11:12     ` Jiri Pirko
  0 siblings, 0 replies; 58+ messages in thread
From: Jiri Pirko @ 2017-08-01 11:12 UTC (permalink / raw)
  To: intel-wired-lan

Tue, Aug 01, 2017 at 02:37:37AM CEST, amritha.nambiar at intel.com wrote:
>The Mirred/redirect action is extended to forward to a traffic
>class on the device. The traffic class index needs to be
>provided in addition to the device's ifindex.
>
>Example:
># tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1

You need to make sure that the current offloaders fill forbid to add
this rule, not just silently ignore the tc value.


>
>Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>---
> include/net/tc_act/tc_mirred.h        |    7 +++++++
> include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
> net/sched/act_mirred.c                |   17 +++++++++++++++++
> 3 files changed, 29 insertions(+)
>
>diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>index 604bc31..60058c4 100644
>--- a/include/net/tc_act/tc_mirred.h
>+++ b/include/net/tc_act/tc_mirred.h
>@@ -9,6 +9,8 @@ struct tcf_mirred {
> 	int			tcfm_eaction;
> 	int			tcfm_ifindex;
> 	bool			tcfm_mac_header_xmit;
>+	u8			tcfm_tc;
>+	u32			flags;
> 	struct net_device __rcu	*tcfm_dev;
> 	struct list_head	tcfm_list;
> };
>@@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
> 	return to_mirred(a)->tcfm_ifindex;
> }
> 
>+static inline int tcf_mirred_tc(const struct tc_action *a)
>+{
>+	return to_mirred(a)->tcfm_tc;
>+}
>+
> #endif /* __NET_TC_MIR_H */
>diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>index 3d7a2b3..8ff4d76 100644
>--- a/include/uapi/linux/tc_act/tc_mirred.h
>+++ b/include/uapi/linux/tc_act/tc_mirred.h
>@@ -9,6 +9,10 @@
> #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
> #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
> #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>+
>+#define MIRRED_F_TC_MAP		0x1
>+#define MIRRED_TC_MAP_MAX	0x10
>+#define MIRRED_TC_MAP_MASK	0xF

I'm completely lost. Why do you have these values here? and in fact one
twice?


>                                                                                 
> struct tc_mirred {
> 	tc_gen;
>@@ -21,6 +25,7 @@ enum {
> 	TCA_MIRRED_TM,
> 	TCA_MIRRED_PARMS,
> 	TCA_MIRRED_PAD,
>+	TCA_MIRRED_TC_MAP,
> 	__TCA_MIRRED_MAX
> };
> #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
>diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
>index 1b5549a..f9801de 100644
>--- a/net/sched/act_mirred.c
>+++ b/net/sched/act_mirred.c
>@@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
> 
> static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
> 	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
>+	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
> };
> 
> static unsigned int mirred_net_id;
>@@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 	struct tcf_mirred *m;
> 	struct net_device *dev;
> 	bool exists = false;
>+	u8 *tc_map = NULL;
>+	u32 flags = 0;
> 	int ret;
> 
> 	if (nla == NULL)
>@@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 		return ret;
> 	if (tb[TCA_MIRRED_PARMS] == NULL)
> 		return -EINVAL;
>+
>+	if (tb[TCA_MIRRED_TC_MAP]) {
>+		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
>+		if (*tc_map >= MIRRED_TC_MAP_MAX)
>+			return -EINVAL;
>+		flags |= MIRRED_F_TC_MAP;
>+	}
>+
> 	parm = nla_data(tb[TCA_MIRRED_PARMS]);
> 
> 	exists = tcf_hash_check(tn, parm->index, a, bind);
>@@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 	ASSERT_RTNL();
> 	m->tcf_action = parm->action;
> 	m->tcfm_eaction = parm->eaction;
>+	m->flags = flags;
> 	if (dev != NULL) {
> 		m->tcfm_ifindex = parm->ifindex;
> 		if (ret != ACT_P_CREATED)
>@@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
> 		dev_hold(dev);
> 		rcu_assign_pointer(m->tcfm_dev, dev);
> 		m->tcfm_mac_header_xmit = mac_header_xmit;
>+		if (flags & MIRRED_F_TC_MAP)
>+			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
> 	}
> 
> 	if (ret == ACT_P_CREATED) {
>@@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
> 
> 	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
> 		goto nla_put_failure;
>+	if ((m->flags & MIRRED_F_TC_MAP) &&
>+	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
>+		goto nla_put_failure;
> 
> 	tcf_tm_dump(&t, &m->tcf_tm);
> 	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters
  2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 19:16     ` Shannon Nelson
  -1 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

On 7/31/2017 5:37 PM, Amritha Nambiar wrote:
> Add new admin queue definitions and extended fields for cloud
> filter support. Define big buffer for extended general fields
> in Add/Remove Cloud filters command.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
> Signed-off-by: Store Laura <laura.stroe@intel.com>
> Signed-off-by: Iremonger Bernard <bernard.iremonger@intel.com>
> Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   98 +++++++++++++++++++++
>   1 file changed, 97 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> index 8bba04c..9f14305 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h

I see that you're changing the i40e version of this, but not the i40evf 
version.  I understand that these changes are not useful for the VF, but 
are you no longer trying to keep the AdminQ definitions consistent 
between the two?

> @@ -1358,7 +1358,9 @@ struct i40e_aqc_add_remove_cloud_filters {
>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT	0
>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK	(0x3FF << \
>   					I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT)
> -	u8	reserved2[4];
> +	u8	big_buffer_flag;
> +#define	I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER	1
> +	u8	reserved2[3];
>   	__le32	addr_high;
>   	__le32	addr_low;
>   };
> @@ -1395,6 +1397,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>   #define I40E_AQC_ADD_CLOUD_FILTER_IMAC			0x000A
>   #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC	0x000B
>   #define I40E_AQC_ADD_CLOUD_FILTER_IIP			0x000C
> +/* 0x0010 to 0x0017 is for custom filters */
> +/* flag to be used when adding cloud filter: IP + L4 Port */
> +#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT		0x0010
> +/* flag to be used when adding cloud filter: Dest MAC + L4 Port */
> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT		0x0011
> +/* flag to be used when adding cloud filter: Dest MAC + VLAN + L4 Port */
> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT		0x0012
>   
>   #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE		0x0080
>   #define I40E_AQC_ADD_CLOUD_VNK_SHIFT			6
> @@ -1429,6 +1438,45 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>   	u8	response_reserved[7];
>   };

I know you didn't add this struct, but where's the I40E_CHECK_STRUCT_LEN 
check?

>   
> +/* i40e_aqc_add_remove_cloud_filters_element_big_data is used when
> + * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set.
> + */
> +struct i40e_aqc_add_remove_cloud_filters_element_big_data {
> +	struct i40e_aqc_add_remove_cloud_filters_element_data element;
> +	u16     general_fields[32];
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0	0
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1	1
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2	2
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0	3
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1	4
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2	5
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0	6
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1	7
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2	8
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0	9
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1	10
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2	11
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0	12
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1	13
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2	14
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0	15
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1	16
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2	17
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3	18
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4	19
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5	20
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6	21
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7	22
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0	23
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1	24
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2	25
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3	26
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4	27
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5	28
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6	29
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7	30
> +};

Needs the I40E_CHECK_STRUCT_LEN to be consistent with the data checking 
in the rest of this file.

> +
>   struct i40e_aqc_remove_cloud_filters_completion {
>   	__le16 perfect_ovlan_used;
>   	__le16 perfect_ovlan_free;
> @@ -1440,6 +1488,54 @@ struct i40e_aqc_remove_cloud_filters_completion {
>   
>   I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion);
>   
> +/* Replace filter Command 0x025F
> + * uses the i40e_aqc_replace_cloud_filters,
> + * and the generic indirect completion structure
> + */
> +struct i40e_filter_data {
> +	u8 filter_type;
> +	u8 input[3];
> +};

Should have I40E_CHECK_STRUCT_LEN check

> +
> +struct i40e_aqc_replace_cloud_filters_cmd {
> +	u8      valid_flags;
> +#define I40E_AQC_REPLACE_L1_FILTER		0x0
> +#define I40E_AQC_REPLACE_CLOUD_FILTER		0x1
> +#define I40E_AQC_GET_CLOUD_FILTERS		0x2
> +#define I40E_AQC_MIRROR_CLOUD_FILTER		0x4
> +#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER	0x8
> +	u8      old_filter_type;
> +	u8      new_filter_type;
> +	u8      tr_bit;
> +	u8      reserved[4];
> +	__le32 addr_high;
> +	__le32 addr_low;
> +};

Should have I40E_CHECK_CMD_LENGTH check

> +
> +struct i40e_aqc_replace_cloud_filters_cmd_buf {
> +	u8      data[32];
> +/* Filter type INPUT codes*/
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX	3
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED	BIT(7)
> +
> +/* Field Vector offsets */
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA	0
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH	6
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG	7
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN	8
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN	9
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN	10
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY	11
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC	12
> +/* big FLU */
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA	14
> +/* big FLU */
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA	15
> +
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN	37
> +	struct i40e_filter_data filters[8];
> +};

Should have I40E_CHECK_STRUCT_LEN check

> +
>   /* Add Mirror Rule (indirect or direct 0x0260)
>    * Delete Mirror Rule (indirect or direct 0x0261)
>    * note: some rule types (4,5) do not use an external buffer.
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters
@ 2017-08-01 19:16     ` Shannon Nelson
  0 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: intel-wired-lan

On 7/31/2017 5:37 PM, Amritha Nambiar wrote:
> Add new admin queue definitions and extended fields for cloud
> filter support. Define big buffer for extended general fields
> in Add/Remove Cloud filters command.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
> Signed-off-by: Store Laura <laura.stroe@intel.com>
> Signed-off-by: Iremonger Bernard <bernard.iremonger@intel.com>
> Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   98 +++++++++++++++++++++
>   1 file changed, 97 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> index 8bba04c..9f14305 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h

I see that you're changing the i40e version of this, but not the i40evf 
version.  I understand that these changes are not useful for the VF, but 
are you no longer trying to keep the AdminQ definitions consistent 
between the two?

> @@ -1358,7 +1358,9 @@ struct i40e_aqc_add_remove_cloud_filters {
>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT	0
>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK	(0x3FF << \
>   					I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT)
> -	u8	reserved2[4];
> +	u8	big_buffer_flag;
> +#define	I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER	1
> +	u8	reserved2[3];
>   	__le32	addr_high;
>   	__le32	addr_low;
>   };
> @@ -1395,6 +1397,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>   #define I40E_AQC_ADD_CLOUD_FILTER_IMAC			0x000A
>   #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC	0x000B
>   #define I40E_AQC_ADD_CLOUD_FILTER_IIP			0x000C
> +/* 0x0010 to 0x0017 is for custom filters */
> +/* flag to be used when adding cloud filter: IP + L4 Port */
> +#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT		0x0010
> +/* flag to be used when adding cloud filter: Dest MAC + L4 Port */
> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT		0x0011
> +/* flag to be used when adding cloud filter: Dest MAC + VLAN + L4 Port */
> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT		0x0012
>   
>   #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE		0x0080
>   #define I40E_AQC_ADD_CLOUD_VNK_SHIFT			6
> @@ -1429,6 +1438,45 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>   	u8	response_reserved[7];
>   };

I know you didn't add this struct, but where's the I40E_CHECK_STRUCT_LEN 
check?

>   
> +/* i40e_aqc_add_remove_cloud_filters_element_big_data is used when
> + * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set.
> + */
> +struct i40e_aqc_add_remove_cloud_filters_element_big_data {
> +	struct i40e_aqc_add_remove_cloud_filters_element_data element;
> +	u16     general_fields[32];
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0	0
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1	1
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2	2
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0	3
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1	4
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2	5
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0	6
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1	7
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2	8
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0	9
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1	10
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2	11
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0	12
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1	13
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2	14
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0	15
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1	16
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2	17
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3	18
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4	19
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5	20
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6	21
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7	22
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0	23
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1	24
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2	25
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3	26
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4	27
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5	28
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6	29
> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7	30
> +};

Needs the I40E_CHECK_STRUCT_LEN to be consistent with the data checking 
in the rest of this file.

> +
>   struct i40e_aqc_remove_cloud_filters_completion {
>   	__le16 perfect_ovlan_used;
>   	__le16 perfect_ovlan_free;
> @@ -1440,6 +1488,54 @@ struct i40e_aqc_remove_cloud_filters_completion {
>   
>   I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion);
>   
> +/* Replace filter Command 0x025F
> + * uses the i40e_aqc_replace_cloud_filters,
> + * and the generic indirect completion structure
> + */
> +struct i40e_filter_data {
> +	u8 filter_type;
> +	u8 input[3];
> +};

Should have I40E_CHECK_STRUCT_LEN check

> +
> +struct i40e_aqc_replace_cloud_filters_cmd {
> +	u8      valid_flags;
> +#define I40E_AQC_REPLACE_L1_FILTER		0x0
> +#define I40E_AQC_REPLACE_CLOUD_FILTER		0x1
> +#define I40E_AQC_GET_CLOUD_FILTERS		0x2
> +#define I40E_AQC_MIRROR_CLOUD_FILTER		0x4
> +#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER	0x8
> +	u8      old_filter_type;
> +	u8      new_filter_type;
> +	u8      tr_bit;
> +	u8      reserved[4];
> +	__le32 addr_high;
> +	__le32 addr_low;
> +};

Should have I40E_CHECK_CMD_LENGTH check

> +
> +struct i40e_aqc_replace_cloud_filters_cmd_buf {
> +	u8      data[32];
> +/* Filter type INPUT codes*/
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX	3
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED	BIT(7)
> +
> +/* Field Vector offsets */
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA	0
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH	6
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG	7
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN	8
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN	9
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN	10
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY	11
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC	12
> +/* big FLU */
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA	14
> +/* big FLU */
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA	15
> +
> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN	37
> +	struct i40e_filter_data filters[8];
> +};

Should have I40E_CHECK_STRUCT_LEN check

> +
>   /* Add Mirror Rule (indirect or direct 0x0260)
>    * Delete Mirror Rule (indirect or direct 0x0261)
>    * note: some rule types (4,5) do not use an external buffer.
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
  2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 19:16   ` Shannon Nelson
  -1 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

If this is an RFC patchset, then all the patches should have RFC in them.

On 7/31/2017 5:36 PM, Amritha Nambiar wrote:
> This patch series enables configuring cloud filters in i40e
> using the tc/flower classifier. The only tc-filter action
> supported is to redirect packets to a traffic class on the
> same device. The tc/mirred:redirect action is extended to
> accept a traffic class to achieve this.
> 
> The cloud filters are added for a VSI and are cleaned up when
> the VSI is deleted. The filters that match on L4 ports needs
> enhanced admin queue functions with big buffer support for
> extended general fields in Add/Remove Cloud filters command.
> 
> Example:
> # tc qdisc add dev eth0 ingress
> 
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> # tc filter show dev eth0 parent ffff:
> filter protocol ip pref 1 flower
> filter protocol ip pref 1 flower handle 0x1
>    indev eth0
>    eth_type ipv4
>    ip_proto udp
>    dst_ip 192.168.1.1
>    dst_port 22
>    skip_sw
>    in_hw
>          action order 1: mirred (Ingress Redirect to device eth0) stolen tc 1
>          index 1 ref 1 bind 1
> ---
> 
> Amritha Nambiar (6):
>        [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
>        [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids
>        [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
>        [net-next]net: i40e: Admin queue definitions for cloud filters
>        [net-next]net: i40e: Clean up of cloud filters
>        [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
> 
> 
>   drivers/net/ethernet/intel/i40e/i40e.h            |   58 +
>   drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |  132 +++
>   drivers/net/ethernet/intel/i40e/i40e_common.c     |  184 ++++
>   drivers/net/ethernet/intel/i40e/i40e_ethtool.c    |    2
>   drivers/net/ethernet/intel/i40e/i40e_main.c       |  983 +++++++++++++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_prototype.h  |   19
>   drivers/net/ethernet/intel/i40e/i40e_type.h       |    9
>   include/net/tc_act/tc_mirred.h                    |    7
>   include/uapi/linux/tc_act/tc_mirred.h             |    5
>   net/sched/act_mirred.c                            |   17
>   10 files changed, 1408 insertions(+), 8 deletions(-)
> 
> --
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-01 19:16   ` Shannon Nelson
  0 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: intel-wired-lan

If this is an RFC patchset, then all the patches should have RFC in them.

On 7/31/2017 5:36 PM, Amritha Nambiar wrote:
> This patch series enables configuring cloud filters in i40e
> using the tc/flower classifier. The only tc-filter action
> supported is to redirect packets to a traffic class on the
> same device. The tc/mirred:redirect action is extended to
> accept a traffic class to achieve this.
> 
> The cloud filters are added for a VSI and are cleaned up when
> the VSI is deleted. The filters that match on L4 ports needs
> enhanced admin queue functions with big buffer support for
> extended general fields in Add/Remove Cloud filters command.
> 
> Example:
> # tc qdisc add dev eth0 ingress
> 
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> # tc filter show dev eth0 parent ffff:
> filter protocol ip pref 1 flower
> filter protocol ip pref 1 flower handle 0x1
>    indev eth0
>    eth_type ipv4
>    ip_proto udp
>    dst_ip 192.168.1.1
>    dst_port 22
>    skip_sw
>    in_hw
>          action order 1: mirred (Ingress Redirect to device eth0) stolen tc 1
>          index 1 ref 1 bind 1
> ---
> 
> Amritha Nambiar (6):
>        [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
>        [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids
>        [net-next]net: i40e: Extend set switch config command to accept cloud filter mode
>        [net-next]net: i40e: Admin queue definitions for cloud filters
>        [net-next]net: i40e: Clean up of cloud filters
>        [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
> 
> 
>   drivers/net/ethernet/intel/i40e/i40e.h            |   58 +
>   drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |  132 +++
>   drivers/net/ethernet/intel/i40e/i40e_common.c     |  184 ++++
>   drivers/net/ethernet/intel/i40e/i40e_ethtool.c    |    2
>   drivers/net/ethernet/intel/i40e/i40e_main.c       |  983 +++++++++++++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_prototype.h  |   19
>   drivers/net/ethernet/intel/i40e/i40e_type.h       |    9
>   include/net/tc_act/tc_mirred.h                    |    7
>   include/uapi/linux/tc_act/tc_mirred.h             |    5
>   net/sched/act_mirred.c                            |   17
>   10 files changed, 1408 insertions(+), 8 deletions(-)
> 
> --
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH 5/6] [net-next]net: i40e: Clean up of cloud filters
  2017-08-01  0:38   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 19:16     ` Shannon Nelson
  -1 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
> Introduce the cloud filter datastructure and cleanup of cloud
> filters associated with the device.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e.h      |   11 +++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_main.c |   27 +++++++++++++++++++++++++++
>   2 files changed, 38 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
> index 1391e5d..5c0cad5 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -252,6 +252,14 @@ struct i40e_fdir_filter {
>   	u32 fd_id;
>   };
>   
> +struct i40e_cloud_filter {
> +	struct hlist_node cloud_node;
> +	/* cloud filter input set follows */
> +	unsigned long cookie;
> +	/* filter control */
> +	u16 seid;
> +};

This would be cleaner and more readable with the field comments off to 
the side rather than in line with the fields.

> +
>   #define I40E_ETH_P_LLDP			0x88cc
>   
>   #define I40E_DCB_PRIO_TYPE_STRICT	0
> @@ -419,6 +427,9 @@ struct i40e_pf {
>   	struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
>   	u16 pending_udp_bitmap;
>   
> +	struct hlist_head cloud_filter_list;
> +	u16 num_cloud_filters;
> +
>   	enum i40e_interrupt_policy int_policy;
>   	u16 rx_itr_default;
>   	u16 tx_itr_default;
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index fdddd74..93f6fe2 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -6928,6 +6928,29 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
>   }
>   
>   /**
> + * i40e_cloud_filter_exit - Cleans up the Cloud Filters
> + * @pf: Pointer to PF
> + *
> + * This function destroys the hlist where all the Cloud Filters
> + * filters were saved.
> + **/
> +static void i40e_cloud_filter_exit(struct i40e_pf *pf)
> +{
> +	struct i40e_cloud_filter *cfilter;
> +	struct hlist_node *node;
> +
> +	if (hlist_empty(&pf->cloud_filter_list))
> +		return;

Is this check really necessary?  Doesn't hlist_for_each_entry_safe() 
check for this?

> +
> +	hlist_for_each_entry_safe(cfilter, node,
> +				  &pf->cloud_filter_list, cloud_node) {
> +		hlist_del(&cfilter->cloud_node);
> +		kfree(cfilter);
> +	}
> +	pf->num_cloud_filters = 0;
> +}
> +
> +/**
>    * i40e_close - Disables a network interface
>    * @netdev: network interface device structure
>    *
> @@ -12137,6 +12160,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>   			vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]);
>   		if (!vsi) {
>   			dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n");
> +			i40e_cloud_filter_exit(pf);
>   			i40e_fdir_teardown(pf);
>   			return -EAGAIN;
>   		}
> @@ -12961,6 +12985,8 @@ static void i40e_remove(struct pci_dev *pdev)
>   	if (pf->vsi[pf->lan_vsi])
>   		i40e_vsi_release(pf->vsi[pf->lan_vsi]);
>   
> +	i40e_cloud_filter_exit(pf);
> +
>   	/* remove attached clients */
>   	if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
>   		ret_code = i40e_lan_del_device(pf);
> @@ -13170,6 +13196,7 @@ static void i40e_shutdown(struct pci_dev *pdev)
>   
>   	del_timer_sync(&pf->service_timer);
>   	cancel_work_sync(&pf->service_task);
> +	i40e_cloud_filter_exit(pf);
>   	i40e_fdir_teardown(pf);
>   
>   	/* Client close must be called explicitly here because the timer
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 5/6] [net-next]net: i40e: Clean up of cloud filters
@ 2017-08-01 19:16     ` Shannon Nelson
  0 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: intel-wired-lan

On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
> Introduce the cloud filter datastructure and cleanup of cloud
> filters associated with the device.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e.h      |   11 +++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_main.c |   27 +++++++++++++++++++++++++++
>   2 files changed, 38 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
> index 1391e5d..5c0cad5 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -252,6 +252,14 @@ struct i40e_fdir_filter {
>   	u32 fd_id;
>   };
>   
> +struct i40e_cloud_filter {
> +	struct hlist_node cloud_node;
> +	/* cloud filter input set follows */
> +	unsigned long cookie;
> +	/* filter control */
> +	u16 seid;
> +};

This would be cleaner and more readable with the field comments off to 
the side rather than in line with the fields.

> +
>   #define I40E_ETH_P_LLDP			0x88cc
>   
>   #define I40E_DCB_PRIO_TYPE_STRICT	0
> @@ -419,6 +427,9 @@ struct i40e_pf {
>   	struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
>   	u16 pending_udp_bitmap;
>   
> +	struct hlist_head cloud_filter_list;
> +	u16 num_cloud_filters;
> +
>   	enum i40e_interrupt_policy int_policy;
>   	u16 rx_itr_default;
>   	u16 tx_itr_default;
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index fdddd74..93f6fe2 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -6928,6 +6928,29 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
>   }
>   
>   /**
> + * i40e_cloud_filter_exit - Cleans up the Cloud Filters
> + * @pf: Pointer to PF
> + *
> + * This function destroys the hlist where all the Cloud Filters
> + * filters were saved.
> + **/
> +static void i40e_cloud_filter_exit(struct i40e_pf *pf)
> +{
> +	struct i40e_cloud_filter *cfilter;
> +	struct hlist_node *node;
> +
> +	if (hlist_empty(&pf->cloud_filter_list))
> +		return;

Is this check really necessary?  Doesn't hlist_for_each_entry_safe() 
check for this?

> +
> +	hlist_for_each_entry_safe(cfilter, node,
> +				  &pf->cloud_filter_list, cloud_node) {
> +		hlist_del(&cfilter->cloud_node);
> +		kfree(cfilter);
> +	}
> +	pf->num_cloud_filters = 0;
> +}
> +
> +/**
>    * i40e_close - Disables a network interface
>    * @netdev: network interface device structure
>    *
> @@ -12137,6 +12160,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>   			vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]);
>   		if (!vsi) {
>   			dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n");
> +			i40e_cloud_filter_exit(pf);
>   			i40e_fdir_teardown(pf);
>   			return -EAGAIN;
>   		}
> @@ -12961,6 +12985,8 @@ static void i40e_remove(struct pci_dev *pdev)
>   	if (pf->vsi[pf->lan_vsi])
>   		i40e_vsi_release(pf->vsi[pf->lan_vsi]);
>   
> +	i40e_cloud_filter_exit(pf);
> +
>   	/* remove attached clients */
>   	if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
>   		ret_code = i40e_lan_del_device(pf);
> @@ -13170,6 +13196,7 @@ static void i40e_shutdown(struct pci_dev *pdev)
>   
>   	del_timer_sync(&pf->service_timer);
>   	cancel_work_sync(&pf->service_task);
> +	i40e_cloud_filter_exit(pf);
>   	i40e_fdir_teardown(pf);
>   
>   	/* Client close must be called explicitly here because the timer
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-01  0:38   ` [Intel-wired-lan] " Amritha Nambiar
@ 2017-08-01 19:16     ` Shannon Nelson
  -1 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: Amritha Nambiar, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
> This patch enables tc-flower based hardware offloads. tc/flower
> filter provided by the kernel is configured as driver specific
> cloud filter. The patch implements functions and admin queue
> commands needed to support cloud filters in the driver and
> adds cloud filters to configure these tc-flower filters.
> 
> The only action supported is to redirect packets to a traffic class
> on the same device.
> 
> # tc qdisc add dev eth0 ingress
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff:\
>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 0
> 
> # tc filter add dev eth0 protocol ip parent ffff:\
>    prio 2 flower dst_ip 192.168.3.5/32\
>    ip_proto udp dst_port 25 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 1
> 
> # tc filter add dev eth0 protocol ipv6 parent ffff:\
>    prio 3 flower dst_ip fe8::200:1\
>    ip_proto udp dst_port 66 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 2
> 
> Delete tc flower filter:
> Example:
> 
> # tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
> # tc filter del dev eth0 parent ffff:
> 
> Flow Director Sideband is disabled while configuring cloud filters
> via tc-flower.

Only while configuring, or the whole time there is a cloud filter?  This 
is unclear here.

> 
> Unsupported matches when cloud filters are added using enhanced
> big buffer cloud filter mode of underlying switch include:
> 1. source port and source IP
> 2. Combined MAC address and IP fields.
> 3. Not specfying L4 port

s/specfying/specifying/

> 
> These filter matches can however be used to redirect traffic to
> the main VSI (tc 0) which does not require the enhanced big buffer
> cloud filter support.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e.h           |   46 +
>   drivers/net/ethernet/intel/i40e/i40e_common.c    |  180 ++++
>   drivers/net/ethernet/intel/i40e/i40e_main.c      |  952 ++++++++++++++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_prototype.h |   17
>   4 files changed, 1193 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
> index 5c0cad5..7288265 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -55,6 +55,8 @@
>   #include <linux/net_tstamp.h>
>   #include <linux/ptp_clock_kernel.h>
>   #include <net/pkt_cls.h>
> +#include <net/tc_act/tc_gact.h>
> +#include <net/tc_act/tc_mirred.h>
>   #include "i40e_type.h"
>   #include "i40e_prototype.h"
>   #include "i40e_client.h"
> @@ -252,10 +254,51 @@ struct i40e_fdir_filter {
>   	u32 fd_id;
>   };
>   
> +#define I40E_CLOUD_FIELD_OMAC	0x01
> +#define I40E_CLOUD_FIELD_IMAC	0x02
> +#define I40E_CLOUD_FIELD_IVLAN	0x04
> +#define I40E_CLOUD_FIELD_TEN_ID	0x08
> +#define I40E_CLOUD_FIELD_IIP	0x10
> +
> +#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
> +						 I40E_CLOUD_FIELD_IVLAN)
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
> +						 I40E_CLOUD_FIELD_TEN_ID)
> +#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
> +						  I40E_CLOUD_FIELD_IMAC | \
> +						  I40E_CLOUD_FIELD_TEN_ID)
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
> +						   I40E_CLOUD_FIELD_IVLAN | \
> +						   I40E_CLOUD_FIELD_TEN_ID)
> +#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
> +
>   struct i40e_cloud_filter {
>   	struct hlist_node cloud_node;
>   	/* cloud filter input set follows */
>   	unsigned long cookie;
> +	u8 dst_mac[ETH_ALEN];
> +	u8 src_mac[ETH_ALEN];
> +	__be16 vlan_id;
> +	__be32 dst_ip[4];
> +	__be32 src_ip[4];
> +	u8 dst_ipv6[16];
> +	u8 src_ipv6[16];
> +	__be16 dst_port;
> +	__be16 src_port;
> +	/* matter only when IP based filtering is set */
> +	bool is_ipv6;
> +	/* IPPROTO value */
> +	u8 ip_proto;
> +	/* L4 port type: src or destination port */
> +#define I40E_CLOUD_FILTER_PORT_SRC	0x01
> +#define I40E_CLOUD_FILTER_PORT_DEST	0x02
> +	u8 port_type;
> +	u32 tenant_id;
> +	u8 flags;
> +#define I40E_CLOUD_TNL_TYPE_NONE	0xff
> +	u8 tunnel_type;
>   	/* filter control */
>   	u16 seid;
>   };
> @@ -574,6 +617,9 @@ struct i40e_pf {
>   	u16 phy_led_val;
>   
>   	u16 override_q_count;
> +	u16 last_sw_conf_flags;
> +	u16 last_sw_conf_valid_flags;
> +

Unnecessary blank line

>   };
>   
>   /**
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
> index d0e8138..bfbe304 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_common.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
> @@ -5269,5 +5269,185 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
>   
>   	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
>   				   track_id, &offset, &info, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_add_cloud_filters
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to add cloud filters from
> + * @filters: Buffer which contains the filters to be added
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Set the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
> +		u16 seid,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	enum i40e_status_code status;
> +	u16 buff_len;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_add_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_add_cloud_filters_big_buffer
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to add cloud filters from
> + * @filters: Buffer which contains the filters in big buffer to be added
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Set the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
> +	u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	i40e_status status;
> +	u16 buff_len;
> +	int i;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_add_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
> +
> +	for (i = 0; i < filter_count; i++) {
> +		u16 tnl_type;
> +		u32 ti;
> +
> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
> +			ti = le32_to_cpu(filters[i].element.tenant_id);
> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
> +		}

You might want to add a comment to explain this little hack.  I believe 
this is for a bit of firmware weirdness?

> +	}
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_remove_cloud_filters
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to remove cloud filters from
> + * @filters: Buffer which contains the filters to be removed
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Remove the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
> +		u16 seid,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	enum i40e_status_code status;
> +	u16 buff_len;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_remove_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_remove_cloud_filters_big_buffer
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to remove cloud filters from
> + * @filters: Buffer which contains the filters in big buffer to be removed
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Remove the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
> +	struct i40e_hw *hw,
> +	u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	i40e_status status;
> +	u16 buff_len;
> +	int i;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_remove_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
> +
> +	for (i = 0; i < filter_count; i++) {
> +		u16 tnl_type;
> +		u32 ti;
> +
> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
> +			ti = le32_to_cpu(filters[i].element.tenant_id);
> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
> +		}

Same comment

> +	}
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
>   	return status;
>   }
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index 93f6fe2..65b284e 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -69,6 +69,12 @@ static int i40e_reset(struct i40e_pf *pf);
>   static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
>   static void i40e_fdir_sb_setup(struct i40e_pf *pf);
>   static int i40e_veb_get_bw_info(struct i40e_veb *veb);
> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
> +				     struct i40e_cloud_filter *filter,
> +				     bool add);
> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
> +					     struct i40e_cloud_filter *filter,
> +					     bool add);
>   
>   /* i40e_pci_tbl - PCI Device ID Table
>    *
> @@ -5482,7 +5488,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
>    **/
>   static void i40e_remove_queue_channels(struct i40e_vsi *vsi)

Hmmm... I'm looking at net-next code and I don't see this function, or 
anything else with "i40e_channel" or "queue_channel".  What are you 
applying this patch to?  Am I missing something?

>   {
> +	enum i40e_admin_queue_err last_aq_status;
> +	struct i40e_cloud_filter *cfilter;
>   	struct i40e_channel *ch, *ch_tmp;
> +	struct i40e_pf *pf = vsi->back;
> +	struct hlist_node *node;
>   	int ret, i;
>   
>   	/* Reset rss size that was stored when reconfiguring rss for
> @@ -5523,6 +5533,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
>   				 "Failed to reset tx rate for ch->seid %u\n",
>   				 ch->seid);
>   
> +		/* delete cloud filters associated with this channel */
> +		hlist_for_each_entry_safe(cfilter, node,
> +					  &pf->cloud_filter_list, cloud_node) {
> +			if (cfilter->seid != ch->seid)
> +				continue;
> +
> +			hash_del(&cfilter->cloud_node);
> +			if (cfilter->dst_port)
> +				ret = i40e_add_del_cloud_filter_big_buf(vsi,
> +									cfilter,
> +									false);
> +			else
> +				ret = i40e_add_del_cloud_filter(vsi, cfilter,
> +								false);
> +			last_aq_status = pf->hw.aq.asq_last_status;
> +			if (ret)
> +				dev_info(&pf->pdev->dev,
> +					 "fail to delete cloud filter, err %s aq_err %s\n",
> +					 i40e_stat_str(&pf->hw, ret),
> +					 i40e_aq_str(&pf->hw, last_aq_status));
> +			kfree(cfilter);
> +		}
> +
>   		/* delete VSI from FW */
>   		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
>   					     NULL);
> @@ -6004,6 +6037,131 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
>   }
>   
>   /**
> + * i40e_get_device_capabilities - get device level info about the HW
> + * @pf: the PF struct
> + **/
> +static int i40e_get_device_capabilities(struct i40e_pf *pf)

Since this is almost exactly the same as the original 
i40e_get_capabilities(), it would be better to simply add list_type_opc 
to i40e_get_capabilities() parameter list

> +{
> +	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
> +	u16 data_size;
> +	int buf_len;
> +	int err;
> +
> +	buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
> +
> +	/* proceed with query device level capabilities */
> +	do {
> +		cap_buf = kzalloc(buf_len, GFP_KERNEL);
> +		if (!cap_buf)
> +			return -ENOMEM;
> +
> +		/* this loads the data into the hw struct for us */
> +		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
> +					     &data_size,
> +					     i40e_aqc_opc_list_dev_capabilities,
> +					     NULL);
> +		/* data loaded, buffer no longer needed */
> +		kfree(cap_buf);
> +
> +		if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) {
> +			/* retry with a larger buffer */
> +			buf_len = data_size;
> +		} else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) {
> +			dev_dbg(&pf->pdev->dev,
> +				"device capability discovery failed, err %s aq_err %s\n",
> +				i40e_stat_str(&pf->hw, err),
> +				i40e_aq_str(&pf->hw,
> +					    pf->hw.aq.asq_last_status));
> +			return -ENODEV;
> +		}
> +	} while (err);
> +
> +	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
> +		dev_info(&pf->pdev->dev,
> +			 "switch_mode=0x%04x, function_valid=0x%08x\n",
> +			 pf->hw.dev_caps.switch_mode,
> +			 pf->hw.dev_caps.valid_functions);
> +		dev_info(&pf->pdev->dev,
> +			 "SR-IOV=%d, num_vfs for all function=%u\n",
> +			 pf->hw.dev_caps.sr_iov_1_1, pf->hw.dev_caps.num_vfs);
> +		dev_info(&pf->pdev->dev,
> +			 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
> +			 pf->hw.dev_caps.num_vsis, pf->hw.dev_caps.num_rx_qp,
> +			 pf->hw.dev_caps.num_tx_qp);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * i40e_validate_and_set_switch_mode - sets up switch mode correctly
> + * @vsi: ptr to VSI which has PF backing
> + * @l4type: true for TCP ond false for UDP
> + * @port_type: true if port is destination and false if port is source
> + *
> + * Sets up switch mode correctly if it needs to be changed and perform
> + * what are allowed modes.
> + **/
> +static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
> +					     bool port_type)
> +{
> +	u8 mode;
> +	struct i40e_pf *pf = vsi->back;
> +	struct i40e_hw *hw = &pf->hw;
> +	int ret;
> +
> +	ret = i40e_get_device_capabilities(pf);
> +	if (ret)
> +		return -EINVAL;
> +
> +	if (hw->dev_caps.switch_mode) {
> +		/* if switch mode is set, support mode2 (non-tunneled for
> +		 * cloud filter) for now
> +		 */
> +#define I40E_SWITCH_MODE_MASK	0xF /* largest cloud filter mode is 0x8 */

Why isn't this defined in the appropriate .h file?

> +		u32 switch_mode = hw->dev_caps.switch_mode &
> +							I40E_SWITCH_MODE_MASK;
> +		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
> +			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
> +				return 0;
> +			dev_err(&pf->pdev->dev,
> +				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
> +				hw->dev_caps.switch_mode);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* port_type: true for destination port and false for source port
> +	 * For now, supports only destination port type
> +	 */
> +	if (!port_type) {
> +		dev_err(&pf->pdev->dev, "src port type not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Set Bit 7 to be valid */
> +	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
> +
> +	/* Set L4type to both TCP and UDP support */
> +	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
> +
> +	/* Set cloud filter mode */
> +	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
> +
> +	/* Prep mode field for set_switch_config */
> +	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
> +					pf->last_sw_conf_valid_flags,
> +					mode, NULL);
> +	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
> +		dev_err(&pf->pdev->dev,
> +			"couldn't set switch config bits, err %s aq_err %s\n",
> +			i40e_stat_str(hw, ret),
> +			i40e_aq_str(hw,
> +				    hw->aq.asq_last_status));
> +
> +	return ret;
> +}
> +
> +/**
>    * i40e_create_queue_channel - function to create channel
>    * @vsi: VSI to be configured
>    * @ch: ptr to channel (it contains channel specific params)
> @@ -6632,6 +6790,10 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>   	}
>   	if (!hw) {
>   		pf->flags &= ~I40E_FLAG_TC_MQPRIO;
> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
> +		    (pf->hw.func_caps.fd_filters_best_effort > 0))
> +			pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
> +
>   		if (tc->type == TC_SETUP_MQPRIO_EXT)
>   			memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
>   			       sizeof(*tc->mqprio_qopt));
> @@ -6682,6 +6844,11 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>   		       sizeof(*tc->mqprio_qopt));
>   		pf->flags |= I40E_FLAG_TC_MQPRIO;
>   		pf->flags &= ~I40E_FLAG_DCB_ENABLED;
> +		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
> +			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
> +			dev_info(&pf->pdev->dev,
> +				 "Disabling ATR in MQPRIO mode\n");
> +		}
>   		break;
>   	default:
>   		return -EINVAL;
> @@ -6743,10 +6910,724 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>   	return ret;
>   }
>   
> +/**
> + * i40e_set_cld_element - sets cloud filter element data
> + * @filter: cloud filter rule
> + * @cld: ptr to cloud filter element data
> + *
> + * This is helper function to copy data into cloud filter element
> + **/
> +static inline void
> +i40e_set_cld_element(struct i40e_cloud_filter *filter,
> +		     struct i40e_aqc_add_remove_cloud_filters_element_data *cld)
> +{
> +	u8 *dest_ipaddr;
> +	u32 ipaddr;
> +	int i;
> +
> +	memset(cld, 0, sizeof(*cld));
> +
> +	ether_addr_copy(cld->outer_mac, filter->dst_mac);
> +	ether_addr_copy(cld->inner_mac, filter->src_mac);
> +
> +	if (filter->is_ipv6) {
> +		dest_ipaddr = (u8 *)&cld->ipaddr.v6.data;
> +		for (i = ARRAY_SIZE(filter->dst_ipv6) - 1; i >= 0; i--) {
> +			memcpy(dest_ipaddr, &filter->dst_ipv6[i], 1);
> +			dest_ipaddr++;
> +		}
> +	} else {
> +		ipaddr = be32_to_cpu(filter->dst_ip[0]);
> +		memcpy(&cld->ipaddr.v4.data, &ipaddr, 4);
> +	}
> +
> +	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
> +	cld->tenant_id = cpu_to_le32(filter->tenant_id);
> +}
> +
> +/**
> + * i40e_add_del_cloud_filter - Add/del cloud filter
> + * @vsi: pointer to VSI
> + * @filter: cloud filter rule
> + * @add: if true, add, if false, delete
> + *
> + * Add or delete a cloud filter for a specific flow spec.
> + * Returns 0 if the filter were successfully added.
> + **/
> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
> +				     struct i40e_cloud_filter *filter, bool add)
> +{
> +	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
> +	struct i40e_pf *pf = vsi->back;
> +	int ret;
> +	static const u16 flag_table[128] = {
> +		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
> +		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
> +		[I40E_CLOUD_FILTER_FLAGS_IIP] =
> +			I40E_AQC_ADD_CLOUD_FILTER_IIP,
> +	};
> +
> +	if (filter->flags >= ARRAY_SIZE(flag_table))
> +		return I40E_ERR_CONFIG;
> +
> +	/* copy element needed to add cloud filter from filter */
> +	i40e_set_cld_element(filter, &cld_filter);
> +
> +	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
> +		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
> +					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
> +
> +	if (filter->is_ipv6)
> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
> +	else
> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
> +
> +	if (add)
> +		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
> +						&cld_filter, 1);
> +	else
> +		ret = i40e_aq_remove_cloud_filters(&pf->hw, filter->seid,
> +						   &cld_filter, 1);
> +	if (ret)
> +		dev_dbg(&pf->pdev->dev,
> +			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
> +			add ? "add" : "delete", filter->dst_port, ret,
> +			pf->hw.aq.asq_last_status);
> +
> +	dev_info(&pf->pdev->dev,
> +		 "%s cloud filter for VSI: %d\n", add ? "Added" : "Deleted",
> +		 filter->seid);

If there was an error, this dev_info() is lying to the user.

> +	return ret;
> +}
> +
> +/**
> + * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
> + * @vsi: pointer to VSI
> + * @filter: cloud filter rule
> + * @add: if true, add, if false, delete
> + *
> + * Add or delete a cloud filter for a specific flow spec using big buffer.
> + * Returns 0 if the filter were successfully added.
> + **/
> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
> +					     struct i40e_cloud_filter *filter,
> +					     bool add)
> +{
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data cld_filter;
> +	struct i40e_pf *pf = vsi->back;
> +	int ret;
> +
> +	/* Both (Outer/Inner) valid mac_addr are not supported */
> +	if (is_valid_ether_addr(filter->dst_mac) &&
> +	    is_valid_ether_addr(filter->src_mac))
> +		return -EINVAL;
> +
> +	/* Make sure port is specified, otherwise bail out, for channel
> +	 * specific cloud filter needs 'L4 port' to be non-zero
> +	 */
> +	if (!filter->dst_port)
> +		return -EINVAL;
> +
> +	/* adding filter using src_port/src_ip is not supported at this stage */
> +	if (filter->src_port || filter->src_ip[0])
> +		return -EINVAL;
> +
> +	/* copy element needed to add cloud filter from filter */
> +	i40e_set_cld_element(filter, &cld_filter.element);
> +
> +	if (is_valid_ether_addr(filter->dst_mac) ||
> +	    is_valid_ether_addr(filter->src_mac) ||
> +	    is_multicast_ether_addr(filter->dst_mac) ||
> +	    is_multicast_ether_addr(filter->src_mac)) {
> +		/* MAC + IP : unsupported mode */
> +		if (filter->dst_ip[0])
> +			return -EINVAL;
> +
> +		/* since we validated that L4 port must be valid before
> +		 * we get here, start with respective "flags" value
> +		 * and update if vlan is present or not
> +		 */
> +		cld_filter.element.flags =
> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
> +
> +		if (filter->vlan_id) {
> +			cld_filter.element.flags =
> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
> +		}

		cld_filter.element.flags = cpu_to_le16(filter->vlan_id
				? I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT;
				: I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);


> +
> +	} else if (filter->dst_ip[0] || filter->is_ipv6) {
> +		cld_filter.element.flags =
> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
> +		if (filter->is_ipv6)
> +			cld_filter.element.flags |=
> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
> +		else
> +			cld_filter.element.flags |=
> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);

		cld_filter.element.flags |= cpu_to_le16(filter->is_ipv6
					? I40E_AQC_ADD_CLOUD_FLAGS_IPV6
					: I40E_AQC_ADD_CLOUD_FLAGS_IPV4);


> +	} else {
> +		dev_err(&pf->pdev->dev,
> +			"either mac or ip has to be valid for cloud filter\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Now copy L4 port in Byte 6..7 in general fields */
> +	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
> +						be16_to_cpu(filter->dst_port);
> +
> +	if (add) {
> +		bool proto_type, port_type;
> +
> +		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
> +		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
> +			     true : false;
> +
> +		/* For now, src port based cloud filter for channel is not
> +		 * supported
> +		 */
> +		if (!port_type) {
> +			dev_err(&pf->pdev->dev,
> +				"unsupported port type (src port)\n");
> +			return -EOPNOTSUPP;
> +		}
> +
> +		/* Validate current device switch mode, change if necessary */
> +		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
> +							port_type);
> +		if (ret) {
> +			dev_err(&pf->pdev->dev,
> +				"fail to and set switch mode, ret %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		ret = i40e_aq_add_cloud_filters_big_buffer(&pf->hw,
> +							   filter->seid,
> +							   &cld_filter, 1);
> +	} else {
> +		ret = i40e_aq_remove_cloud_filters_big_buffer(&pf->hw,
> +							      filter->seid,
> +							      &cld_filter, 1);
> +	}
> +
> +	if (ret)
> +		dev_dbg(&pf->pdev->dev,
> +			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
> +			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
> +
> +	dev_info(&pf->pdev->dev,
> +		 "%s cloud filter for VSI: %d, L4 port: %d\n",
> +		 add ? "add" : "delete", filter->seid, ntohs(filter->dst_port));

If there was an error, this dev_info is possibly lying to the user.

> +
> +	return ret;
> +}
> +
> +/**
> + * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + * @filter: Pointer to cloud filter structure
> + *
> + **/
> +static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
> +				 struct tc_cls_flower_offload *f,
> +				 struct i40e_cloud_filter *filter)
> +{
> +	struct i40e_pf *pf = vsi->back;
> +	u16 addr_type = 0;
> +	u8 field_flags = 0;
> +
> +	if (f->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_PORTS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
> +		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
> +			f->dissector->used_keys);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
> +		struct flow_dissector_key_keyid *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
> +						  f->key);
> +
> +		struct flow_dissector_key_keyid *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
> +						  f->mask);
> +
> +		if (mask->keyid != 0)
> +			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
> +
> +		filter->tenant_id = be32_to_cpu(key->keyid);
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
> +		struct flow_dissector_key_basic *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_BASIC,
> +						  f->key);
> +
> +		filter->ip_proto = key->ip_proto;
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
> +		struct flow_dissector_key_eth_addrs *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
> +						  f->key);
> +
> +		struct flow_dissector_key_eth_addrs *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
> +						  f->mask);
> +
> +		/* use is_broadcast and is_zero to check for all 0xf or 0 */
> +		if (!is_zero_ether_addr(mask->dst)) {
> +			if (is_broadcast_ether_addr(mask->dst)) {
> +				field_flags |= I40E_CLOUD_FIELD_OMAC;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
> +					mask->dst);
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (!is_zero_ether_addr(mask->src)) {
> +			if (is_broadcast_ether_addr(mask->src)) {
> +				field_flags |= I40E_CLOUD_FIELD_IMAC;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
> +					mask->src);
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +		ether_addr_copy(filter->dst_mac, key->dst);
> +		ether_addr_copy(filter->src_mac, key->src);
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
> +		struct flow_dissector_key_vlan *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_VLAN,
> +						  f->key);
> +		struct flow_dissector_key_vlan *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_VLAN,
> +						  f->mask);
> +
> +		if (mask->vlan_id) {
> +			if (mask->vlan_id == VLAN_VID_MASK) {
> +				field_flags |= I40E_CLOUD_FIELD_IVLAN;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad vlan mask %u\n",

Should this be a %04x rather than %u?

> +					mask->vlan_id);
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		filter->vlan_id = cpu_to_be16(key->vlan_id);
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
> +		struct flow_dissector_key_control *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_CONTROL,
> +						  f->key);
> +
> +		addr_type = key->addr_type;
> +	}
> +
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
> +		struct flow_dissector_key_ipv4_addrs *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
> +						  f->key);
> +		struct flow_dissector_key_ipv4_addrs *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
> +						  f->mask);
> +
> +		if (mask->dst) {
> +			if (mask->dst == cpu_to_be32(0xffffffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
> +					be32_to_cpu(mask->dst));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (mask->src) {
> +			if (mask->src == cpu_to_be32(0xffffffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
> +					be32_to_cpu(mask->dst));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
> +			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
> +			return I40E_ERR_CONFIG;
> +		}
> +		filter->dst_ip[0] = key->dst;
> +		filter->src_ip[0] = key->src;
> +	}
> +
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
> +		struct flow_dissector_key_ipv6_addrs *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
> +						  f->key);
> +		struct flow_dissector_key_ipv6_addrs *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
> +						  f->mask);
> +
> +		/* validate mask, make sure it is not IPV6_ADDR_ANY */
> +		if (ipv6_addr_any(&mask->dst)) {
> +			dev_err(&pf->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
> +				IPV6_ADDR_ANY);
> +			return I40E_ERR_CONFIG;
> +		}
> +
> +		/* validate src and dest IPV6 address, make sure they are not
> +		 * ANY (0:0:0:0:0:0:0:0) or LOOPBACK (0:0:0:0:0:0:0:1), which
> +		 * can be represented as ::1
> +		 */
> +		if (ipv6_addr_any(&key->dst) || ipv6_addr_loopback(&key->dst)) {
> +			dev_err(&pf->pdev->dev,
> +				"Bad ipv6 dst addr is ANY or LOOPBACK\n");
> +			return I40E_ERR_CONFIG;
> +		}
> +		if (ipv6_addr_loopback(&key->src)) {
> +			dev_err(&pf->pdev->dev,
> +				"Bad ipv6 src addr is ANY or LOOPBACK\n");
> +			return I40E_ERR_CONFIG;
> +		}
> +		memcpy(&filter->src_ipv6, &key->src.s6_addr,
> +		       ARRAY_SIZE(filter->src_ipv6));
> +		memcpy(&filter->dst_ipv6, &key->dst.s6_addr,
> +		       ARRAY_SIZE(filter->dst_ipv6));
> +
> +		/* mark it as IPv6 filter, to be used later */
> +		filter->is_ipv6 = true;
> +
> +		/* and it is IP[4|6] filter type */
> +		field_flags |= I40E_CLOUD_FIELD_IIP;
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
> +		struct flow_dissector_key_ports *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_PORTS,
> +						  f->key);
> +		struct flow_dissector_key_ports *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_PORTS,
> +						  f->mask);
> +
> +		if (mask->src) {
> +			if (mask->src == cpu_to_be16(0xffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad src port mask %u\n",

Should this be a %04x rather than %u?

> +					be16_to_cpu(mask->src));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (mask->dst) {
> +			if (mask->dst == cpu_to_be16(0xffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad dst port mask %u\n",

Should this be a %04x rather than %u?

> +					be16_to_cpu(mask->dst));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		filter->dst_port = key->dst;
> +		filter->src_port = key->src;
> +
> +		/* For now, only supports destination port*/
> +		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
> +
> +		switch (filter->ip_proto) {
> +		case IPPROTO_TCP:
> +		case IPPROTO_UDP:
> +			break;
> +		default:
> +			dev_err(&pf->pdev->dev,
> +				"Only UDP and TCP transport are supported\n");
> +			return -EINVAL;
> +		}
> +	}
> +	filter->flags = field_flags;
> +	return 0;
> +}
> +
> +/**
> + * i40e_handle_redirect_action: Forward to a traffic class on the device
> + * @vsi: Pointer to VSI
> + * @ifindex: ifindex of the device to forwared to
> + * @tc: traffic class index on the device
> + * @filter: Pointer to cloud filter structure
> + *
> + **/
> +static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
> +				       struct i40e_cloud_filter *filter)
> +{
> +	struct i40e_channel *ch, *ch_tmp;
> +
> +	/* redirect to a traffic class on the same device */
> +	if (vsi->netdev->ifindex == ifindex) {
> +		if (tc == 0) {
> +			filter->seid = vsi->seid;
> +			return 0;
> +		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
> +			if (!filter->dst_port) {
> +				dev_err(&vsi->back->pdev->dev,
> +					"Specify destination port to redirect to traffic class that is not default\n");
> +				return -EINVAL;
> +			}
> +			if (list_empty(&vsi->ch_list))
> +				return -EINVAL;
> +			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
> +						 list) {
> +				if (ch->seid == vsi->tc_seid_map[tc])
> +					filter->seid = ch->seid;
> +			}
> +			return 0;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +/**
> + * i40e_parse_tc_actions - Parse tc actions
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + * @filter: Pointer to cloud filter structure
> + *
> + **/
> +static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
> +				 struct i40e_cloud_filter *filter)
> +{
> +	const struct tc_action *a;
> +	LIST_HEAD(actions);
> +	int err;
> +
> +	if (tc_no_actions(exts))
> +		return -EINVAL;
> +
> +	tcf_exts_to_list(exts, &actions);
> +	list_for_each_entry(a, &actions, list) {
> +		/* Drop action */
> +		if (is_tcf_gact_shot(a)) {
> +			dev_err(&vsi->back->pdev->dev,
> +				"Cloud filters do not support the drop action.\n");
> +			return -EOPNOTSUPP;
> +		}
> +
> +		/* Redirect to a traffic class on the same device */
> +		if (!is_tcf_mirred_egress_redirect(a)) {
> +			int ifindex = tcf_mirred_ifindex(a);
> +			int tc = tcf_mirred_tc(a);
> +
> +			err = i40e_handle_redirect_action(vsi, ifindex, tc,
> +							  filter);
> +			if (err == 0)
> +				return err;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +/**
> + * i40e_configure_clsflower - Configure tc flower filters
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + *
> + **/
> +static int i40e_configure_clsflower(struct i40e_vsi *vsi,
> +				    struct tc_cls_flower_offload *cls_flower)
> +{
> +	struct i40e_cloud_filter *filter = NULL;
> +	struct i40e_pf *pf = vsi->back;
> +	int err = 0;
> +
> +	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
> +	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
> +		return -EBUSY;
> +
> +	if (pf->fdir_pf_active_filters ||
> +	    (!hlist_empty(&pf->fdir_filter_list))) {
> +		dev_err(&vsi->back->pdev->dev,
> +			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
> +		return -EINVAL;
> +	}
> +
> +	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
> +		dev_err(&vsi->back->pdev->dev,
> +			"Disable Flow Director Sideband while configuring Cloud filters via tc-flower\n");
> +		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
> +	}
> +
> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> +	if (!filter)
> +		return -ENOMEM;
> +
> +	filter->cookie = cls_flower->cookie;
> +
> +	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
> +	if (err < 0)
> +		goto err;
> +
> +	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
> +	if (err < 0)
> +		goto err;
> +
> +	/* Add cloud filter */
> +	if (filter->dst_port)
> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
> +	else
> +		err = i40e_add_del_cloud_filter(vsi, filter, true);
> +
> +	if (err) {
> +		dev_err(&pf->pdev->dev,
> +			"Failed to add cloud filter, err %s\n",
> +			i40e_stat_str(&pf->hw, err));
> +		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
> +		goto err;
> +	}
> +
> +	/* add filter to the ordered list */
> +	INIT_HLIST_NODE(&filter->cloud_node);
> +
> +	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
> +
> +	pf->num_cloud_filters++;
> +
> +	return err;
> +err:
> +	kfree(filter);
> +	return err;
> +}
> +
> +/**
> + * i40e_find_cloud_filter - Find the could filter in the list
> + * @vsi: Pointer to VSI
> + * @cookie: filter specific cookie
> + *
> + **/
> +static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
> +							unsigned long *cookie)
> +{
> +	struct i40e_cloud_filter *filter = NULL;
> +	struct hlist_node *node2;
> +
> +	hlist_for_each_entry_safe(filter, node2,
> +				  &vsi->back->cloud_filter_list, cloud_node)
> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
> +			return filter;
> +	return NULL;
> +}
> +
> +/**
> + * i40e_delete_clsflower - Remove tc flower filters
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + *
> + **/
> +static int i40e_delete_clsflower(struct i40e_vsi *vsi,
> +				 struct tc_cls_flower_offload *cls_flower)
> +{
> +	struct i40e_cloud_filter *filter = NULL;
> +	struct i40e_pf *pf = vsi->back;
> +	int err = 0;
> +
> +	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
> +
> +	if (!filter)
> +		return -EINVAL;
> +
> +	hash_del(&filter->cloud_node);
> +
> +	if (filter->dst_port)
> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
> +	else
> +		err = i40e_add_del_cloud_filter(vsi, filter, false);
> +	if (err) {
> +		kfree(filter);
> +		dev_err(&pf->pdev->dev,
> +			"Failed to delete cloud filter, err %s\n",
> +			i40e_stat_str(&pf->hw, err));
> +		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);

Hmmm... okay, maybe those earlier functions are not really lying if they 
have this followup to check them.

> +	}
> +
> +	kfree(filter);
> +	pf->num_cloud_filters--;
> +
> +	if (!pf->num_cloud_filters) {
> +		/* Re-enable FD-SB that was disabled while configuring cloud
> +		 * filters
> +		 */
> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
> +		    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
> +			if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
> +			      pf->hw.num_partitions > 1))
> +				pf->flags |= I40E_FLAG_FD_SB_ENABLED;

What if FD_SB was disabled for other reasons, like not enough msix 
vectors or queues, or some other odd circumstance that turns them off?

> +		}
> +	}
> +
> +	return 0;
> +}
> +
>   static int __i40e_setup_tc(struct net_device *netdev, u32 handle,
>   			   u32 chain_index, __be16 proto,
>   			   struct tc_to_netdev *tc)
>   {
> +	struct i40e_netdev_priv *np = netdev_priv(netdev);
> +	struct i40e_vsi *vsi = np->vsi;
> +
> +	if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
> +	    tc->type == TC_SETUP_CLSFLOWER) {
> +		switch (tc->cls_flower->command) {
> +		case TC_CLSFLOWER_REPLACE:
> +			return i40e_configure_clsflower(vsi, tc->cls_flower);
> +		case TC_CLSFLOWER_DESTROY:
> +			return i40e_delete_clsflower(vsi, tc->cls_flower);
> +		case TC_CLSFLOWER_STATS:
> +			return -EOPNOTSUPP;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
>   	return i40e_setup_tc(netdev, tc);
>   }
>   
> @@ -6948,6 +7829,16 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>   		kfree(cfilter);
>   	}
>   	pf->num_cloud_filters = 0;
> +
> +	/* Re-enable FD-SB that was disabled while configuring cloud
> +	 * filters
> +	 */
> +	if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
> +	    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
> +		if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
> +		      pf->hw.num_partitions > 1))
> +			pf->flags |= I40E_FLAG_FD_SB_ENABLED;

What if FD_SB was disabled for other reasons, like not enough msix 
vectors or queues, or some other odd circumstance that turns them off?

This is getting complex and repetative.  Maybe there needs to be a 
little function that checks all the FD_SB conditions and can be used 
from anywhere needed.

> +	}
>   }
>   
>   /**
> @@ -8157,6 +9048,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
>   }
>   
>   /**
> + * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
> + * @vsi: PF main vsi
> + * @seid: seid of main or channel VSIs
> + *
> + * Rebuilds cloud filters associated with main VSI and channel VSIs if they
> + * existed before reset
> + **/
> +static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
> +{
> +	struct i40e_cloud_filter *cfilter;
> +	struct i40e_pf *pf = vsi->back;
> +	struct hlist_node *node;
> +	i40e_status ret;
> +
> +	/* Add cloud filters back if they exist */
> +	if (hlist_empty(&pf->cloud_filter_list))
> +		return 0;
> +
> +	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
> +				  cloud_node) {
> +		if (cfilter->seid != seid)
> +			continue;
> +
> +		if (cfilter->dst_port)
> +			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
> +								true);
> +		else
> +			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
> +
> +		if (ret) {
> +			dev_dbg(&pf->pdev->dev,
> +				"Failed to rebuild cloud filter, err %s aq_err %s\n",
> +				i40e_stat_str(&pf->hw, ret),
> +				i40e_aq_str(&pf->hw,
> +					    pf->hw.aq.asq_last_status));
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/**
>    * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
>    * @vsi: PF main vsi
>    *
> @@ -8193,6 +9126,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
>   						I40E_BW_CREDIT_DIVISOR,
>   				ch->seid);
>   		}
> +		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
> +		if (ret) {
> +			dev_dbg(&vsi->back->pdev->dev,
> +				"Failed to rebuild cloud filters for channel VSI %u\n",
> +				ch->seid);
> +			return ret;
> +		}
>   	}
>   	return 0;
>   }
> @@ -8476,6 +9416,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>   			goto end_unlock;
>   	}
>   
> +	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
> +	if (ret)
> +		goto end_unlock;
> +
>   	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
>   	 * for this main VSI if they exist
>   	 */
> @@ -10846,7 +11790,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
>   		netdev->hw_features |= NETIF_F_NTUPLE;
>   	hw_features = hw_enc_features		|
>   		      NETIF_F_HW_VLAN_CTAG_TX	|
> -		      NETIF_F_HW_VLAN_CTAG_RX;
> +		      NETIF_F_HW_VLAN_CTAG_RX	|
> +		      NETIF_F_HW_TC;
>   
>   	netdev->hw_features |= hw_features;
>   
> @@ -12123,8 +13068,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>   	*/
>   
>   	if ((pf->hw.pf_id == 0) &&
> -	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
> +	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
>   		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
> +		pf->last_sw_conf_flags = flags;
> +	}
>   
>   	if (pf->hw.pf_id == 0) {
>   		u16 valid_flags;
> @@ -12140,6 +13087,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>   					     pf->hw.aq.asq_last_status));
>   			/* not a fatal problem, just keep going */
>   		}
> +		pf->last_sw_conf_valid_flags = valid_flags;
>   	}
>   
>   	/* first time setup */
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> index 9142d0d..e24f1ce 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> @@ -283,6 +283,23 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
>   		struct i40e_asq_cmd_details *cmd_details);
>   i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
>   				   struct i40e_asq_cmd_details *cmd_details);
> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
> +	u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count);
> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
> +		u16 vsi,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count);
> +
> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
> +		u16 vsi,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count);
> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
> +	struct i40e_hw *hw, u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count);
>   i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
>   			       struct i40e_lldp_variables *lldp_cfg);
>   /* i40e_common */
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-01 19:16     ` Shannon Nelson
  0 siblings, 0 replies; 58+ messages in thread
From: Shannon Nelson @ 2017-08-01 19:16 UTC (permalink / raw)
  To: intel-wired-lan

On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
> This patch enables tc-flower based hardware offloads. tc/flower
> filter provided by the kernel is configured as driver specific
> cloud filter. The patch implements functions and admin queue
> commands needed to support cloud filters in the driver and
> adds cloud filters to configure these tc-flower filters.
> 
> The only action supported is to redirect packets to a traffic class
> on the same device.
> 
> # tc qdisc add dev eth0 ingress
> # ethtool -K eth0 hw-tc-offload on
> 
> # tc filter add dev eth0 protocol ip parent ffff:\
>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 0
> 
> # tc filter add dev eth0 protocol ip parent ffff:\
>    prio 2 flower dst_ip 192.168.3.5/32\
>    ip_proto udp dst_port 25 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 1
> 
> # tc filter add dev eth0 protocol ipv6 parent ffff:\
>    prio 3 flower dst_ip fe8::200:1\
>    ip_proto udp dst_port 66 skip_sw indev eth0\
>    action mirred ingress redirect dev eth0 tc 2
> 
> Delete tc flower filter:
> Example:
> 
> # tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
> # tc filter del dev eth0 parent ffff:
> 
> Flow Director Sideband is disabled while configuring cloud filters
> via tc-flower.

Only while configuring, or the whole time there is a cloud filter?  This 
is unclear here.

> 
> Unsupported matches when cloud filters are added using enhanced
> big buffer cloud filter mode of underlying switch include:
> 1. source port and source IP
> 2. Combined MAC address and IP fields.
> 3. Not specfying L4 port

s/specfying/specifying/

> 
> These filter matches can however be used to redirect traffic to
> the main VSI (tc 0) which does not require the enhanced big buffer
> cloud filter support.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e.h           |   46 +
>   drivers/net/ethernet/intel/i40e/i40e_common.c    |  180 ++++
>   drivers/net/ethernet/intel/i40e/i40e_main.c      |  952 ++++++++++++++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_prototype.h |   17
>   4 files changed, 1193 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
> index 5c0cad5..7288265 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -55,6 +55,8 @@
>   #include <linux/net_tstamp.h>
>   #include <linux/ptp_clock_kernel.h>
>   #include <net/pkt_cls.h>
> +#include <net/tc_act/tc_gact.h>
> +#include <net/tc_act/tc_mirred.h>
>   #include "i40e_type.h"
>   #include "i40e_prototype.h"
>   #include "i40e_client.h"
> @@ -252,10 +254,51 @@ struct i40e_fdir_filter {
>   	u32 fd_id;
>   };
>   
> +#define I40E_CLOUD_FIELD_OMAC	0x01
> +#define I40E_CLOUD_FIELD_IMAC	0x02
> +#define I40E_CLOUD_FIELD_IVLAN	0x04
> +#define I40E_CLOUD_FIELD_TEN_ID	0x08
> +#define I40E_CLOUD_FIELD_IIP	0x10
> +
> +#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
> +						 I40E_CLOUD_FIELD_IVLAN)
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
> +						 I40E_CLOUD_FIELD_TEN_ID)
> +#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
> +						  I40E_CLOUD_FIELD_IMAC | \
> +						  I40E_CLOUD_FIELD_TEN_ID)
> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
> +						   I40E_CLOUD_FIELD_IVLAN | \
> +						   I40E_CLOUD_FIELD_TEN_ID)
> +#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
> +
>   struct i40e_cloud_filter {
>   	struct hlist_node cloud_node;
>   	/* cloud filter input set follows */
>   	unsigned long cookie;
> +	u8 dst_mac[ETH_ALEN];
> +	u8 src_mac[ETH_ALEN];
> +	__be16 vlan_id;
> +	__be32 dst_ip[4];
> +	__be32 src_ip[4];
> +	u8 dst_ipv6[16];
> +	u8 src_ipv6[16];
> +	__be16 dst_port;
> +	__be16 src_port;
> +	/* matter only when IP based filtering is set */
> +	bool is_ipv6;
> +	/* IPPROTO value */
> +	u8 ip_proto;
> +	/* L4 port type: src or destination port */
> +#define I40E_CLOUD_FILTER_PORT_SRC	0x01
> +#define I40E_CLOUD_FILTER_PORT_DEST	0x02
> +	u8 port_type;
> +	u32 tenant_id;
> +	u8 flags;
> +#define I40E_CLOUD_TNL_TYPE_NONE	0xff
> +	u8 tunnel_type;
>   	/* filter control */
>   	u16 seid;
>   };
> @@ -574,6 +617,9 @@ struct i40e_pf {
>   	u16 phy_led_val;
>   
>   	u16 override_q_count;
> +	u16 last_sw_conf_flags;
> +	u16 last_sw_conf_valid_flags;
> +

Unnecessary blank line

>   };
>   
>   /**
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
> index d0e8138..bfbe304 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_common.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
> @@ -5269,5 +5269,185 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
>   
>   	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
>   				   track_id, &offset, &info, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_add_cloud_filters
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to add cloud filters from
> + * @filters: Buffer which contains the filters to be added
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Set the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
> +		u16 seid,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	enum i40e_status_code status;
> +	u16 buff_len;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_add_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_add_cloud_filters_big_buffer
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to add cloud filters from
> + * @filters: Buffer which contains the filters in big buffer to be added
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Set the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
> +	u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	i40e_status status;
> +	u16 buff_len;
> +	int i;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_add_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
> +
> +	for (i = 0; i < filter_count; i++) {
> +		u16 tnl_type;
> +		u32 ti;
> +
> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
> +			ti = le32_to_cpu(filters[i].element.tenant_id);
> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
> +		}

You might want to add a comment to explain this little hack.  I believe 
this is for a bit of firmware weirdness?

> +	}
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_remove_cloud_filters
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to remove cloud filters from
> + * @filters: Buffer which contains the filters to be removed
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Remove the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
> +		u16 seid,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	enum i40e_status_code status;
> +	u16 buff_len;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_remove_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
> +	return status;
> +}
> +
> +/**
> + * i40e_aq_remove_cloud_filters_big_buffer
> + * @hw: pointer to the hardware structure
> + * @seid: VSI seid to remove cloud filters from
> + * @filters: Buffer which contains the filters in big buffer to be removed
> + * @filter_count: number of filters contained in the buffer
> + *
> + * Remove the cloud filters for a given VSI.  The contents of the
> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
> + * in by the caller of the function.
> + *
> + **/
> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
> +	struct i40e_hw *hw,
> +	u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count)
> +{
> +	struct i40e_aq_desc desc;
> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
> +	i40e_status status;
> +	u16 buff_len;
> +	int i;
> +
> +	i40e_fill_default_direct_cmd_desc(&desc,
> +					  i40e_aqc_opc_remove_cloud_filters);
> +
> +	buff_len = filter_count * sizeof(*filters);
> +	desc.datalen = cpu_to_le16(buff_len);
> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
> +	cmd->num_filters = filter_count;
> +	cmd->seid = cpu_to_le16(seid);
> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
> +
> +	for (i = 0; i < filter_count; i++) {
> +		u16 tnl_type;
> +		u32 ti;
> +
> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
> +			ti = le32_to_cpu(filters[i].element.tenant_id);
> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
> +		}

Same comment

> +	}
> +
> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
> +
>   	return status;
>   }
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index 93f6fe2..65b284e 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -69,6 +69,12 @@ static int i40e_reset(struct i40e_pf *pf);
>   static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
>   static void i40e_fdir_sb_setup(struct i40e_pf *pf);
>   static int i40e_veb_get_bw_info(struct i40e_veb *veb);
> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
> +				     struct i40e_cloud_filter *filter,
> +				     bool add);
> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
> +					     struct i40e_cloud_filter *filter,
> +					     bool add);
>   
>   /* i40e_pci_tbl - PCI Device ID Table
>    *
> @@ -5482,7 +5488,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
>    **/
>   static void i40e_remove_queue_channels(struct i40e_vsi *vsi)

Hmmm... I'm looking at net-next code and I don't see this function, or 
anything else with "i40e_channel" or "queue_channel".  What are you 
applying this patch to?  Am I missing something?

>   {
> +	enum i40e_admin_queue_err last_aq_status;
> +	struct i40e_cloud_filter *cfilter;
>   	struct i40e_channel *ch, *ch_tmp;
> +	struct i40e_pf *pf = vsi->back;
> +	struct hlist_node *node;
>   	int ret, i;
>   
>   	/* Reset rss size that was stored when reconfiguring rss for
> @@ -5523,6 +5533,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
>   				 "Failed to reset tx rate for ch->seid %u\n",
>   				 ch->seid);
>   
> +		/* delete cloud filters associated with this channel */
> +		hlist_for_each_entry_safe(cfilter, node,
> +					  &pf->cloud_filter_list, cloud_node) {
> +			if (cfilter->seid != ch->seid)
> +				continue;
> +
> +			hash_del(&cfilter->cloud_node);
> +			if (cfilter->dst_port)
> +				ret = i40e_add_del_cloud_filter_big_buf(vsi,
> +									cfilter,
> +									false);
> +			else
> +				ret = i40e_add_del_cloud_filter(vsi, cfilter,
> +								false);
> +			last_aq_status = pf->hw.aq.asq_last_status;
> +			if (ret)
> +				dev_info(&pf->pdev->dev,
> +					 "fail to delete cloud filter, err %s aq_err %s\n",
> +					 i40e_stat_str(&pf->hw, ret),
> +					 i40e_aq_str(&pf->hw, last_aq_status));
> +			kfree(cfilter);
> +		}
> +
>   		/* delete VSI from FW */
>   		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
>   					     NULL);
> @@ -6004,6 +6037,131 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
>   }
>   
>   /**
> + * i40e_get_device_capabilities - get device level info about the HW
> + * @pf: the PF struct
> + **/
> +static int i40e_get_device_capabilities(struct i40e_pf *pf)

Since this is almost exactly the same as the original 
i40e_get_capabilities(), it would be better to simply add list_type_opc 
to i40e_get_capabilities() parameter list

> +{
> +	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
> +	u16 data_size;
> +	int buf_len;
> +	int err;
> +
> +	buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
> +
> +	/* proceed with query device level capabilities */
> +	do {
> +		cap_buf = kzalloc(buf_len, GFP_KERNEL);
> +		if (!cap_buf)
> +			return -ENOMEM;
> +
> +		/* this loads the data into the hw struct for us */
> +		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
> +					     &data_size,
> +					     i40e_aqc_opc_list_dev_capabilities,
> +					     NULL);
> +		/* data loaded, buffer no longer needed */
> +		kfree(cap_buf);
> +
> +		if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) {
> +			/* retry with a larger buffer */
> +			buf_len = data_size;
> +		} else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) {
> +			dev_dbg(&pf->pdev->dev,
> +				"device capability discovery failed, err %s aq_err %s\n",
> +				i40e_stat_str(&pf->hw, err),
> +				i40e_aq_str(&pf->hw,
> +					    pf->hw.aq.asq_last_status));
> +			return -ENODEV;
> +		}
> +	} while (err);
> +
> +	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
> +		dev_info(&pf->pdev->dev,
> +			 "switch_mode=0x%04x, function_valid=0x%08x\n",
> +			 pf->hw.dev_caps.switch_mode,
> +			 pf->hw.dev_caps.valid_functions);
> +		dev_info(&pf->pdev->dev,
> +			 "SR-IOV=%d, num_vfs for all function=%u\n",
> +			 pf->hw.dev_caps.sr_iov_1_1, pf->hw.dev_caps.num_vfs);
> +		dev_info(&pf->pdev->dev,
> +			 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
> +			 pf->hw.dev_caps.num_vsis, pf->hw.dev_caps.num_rx_qp,
> +			 pf->hw.dev_caps.num_tx_qp);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * i40e_validate_and_set_switch_mode - sets up switch mode correctly
> + * @vsi: ptr to VSI which has PF backing
> + * @l4type: true for TCP ond false for UDP
> + * @port_type: true if port is destination and false if port is source
> + *
> + * Sets up switch mode correctly if it needs to be changed and perform
> + * what are allowed modes.
> + **/
> +static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
> +					     bool port_type)
> +{
> +	u8 mode;
> +	struct i40e_pf *pf = vsi->back;
> +	struct i40e_hw *hw = &pf->hw;
> +	int ret;
> +
> +	ret = i40e_get_device_capabilities(pf);
> +	if (ret)
> +		return -EINVAL;
> +
> +	if (hw->dev_caps.switch_mode) {
> +		/* if switch mode is set, support mode2 (non-tunneled for
> +		 * cloud filter) for now
> +		 */
> +#define I40E_SWITCH_MODE_MASK	0xF /* largest cloud filter mode is 0x8 */

Why isn't this defined in the appropriate .h file?

> +		u32 switch_mode = hw->dev_caps.switch_mode &
> +							I40E_SWITCH_MODE_MASK;
> +		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
> +			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
> +				return 0;
> +			dev_err(&pf->pdev->dev,
> +				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
> +				hw->dev_caps.switch_mode);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* port_type: true for destination port and false for source port
> +	 * For now, supports only destination port type
> +	 */
> +	if (!port_type) {
> +		dev_err(&pf->pdev->dev, "src port type not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Set Bit 7 to be valid */
> +	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
> +
> +	/* Set L4type to both TCP and UDP support */
> +	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
> +
> +	/* Set cloud filter mode */
> +	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
> +
> +	/* Prep mode field for set_switch_config */
> +	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
> +					pf->last_sw_conf_valid_flags,
> +					mode, NULL);
> +	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
> +		dev_err(&pf->pdev->dev,
> +			"couldn't set switch config bits, err %s aq_err %s\n",
> +			i40e_stat_str(hw, ret),
> +			i40e_aq_str(hw,
> +				    hw->aq.asq_last_status));
> +
> +	return ret;
> +}
> +
> +/**
>    * i40e_create_queue_channel - function to create channel
>    * @vsi: VSI to be configured
>    * @ch: ptr to channel (it contains channel specific params)
> @@ -6632,6 +6790,10 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>   	}
>   	if (!hw) {
>   		pf->flags &= ~I40E_FLAG_TC_MQPRIO;
> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
> +		    (pf->hw.func_caps.fd_filters_best_effort > 0))
> +			pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
> +
>   		if (tc->type == TC_SETUP_MQPRIO_EXT)
>   			memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
>   			       sizeof(*tc->mqprio_qopt));
> @@ -6682,6 +6844,11 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>   		       sizeof(*tc->mqprio_qopt));
>   		pf->flags |= I40E_FLAG_TC_MQPRIO;
>   		pf->flags &= ~I40E_FLAG_DCB_ENABLED;
> +		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
> +			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
> +			dev_info(&pf->pdev->dev,
> +				 "Disabling ATR in MQPRIO mode\n");
> +		}
>   		break;
>   	default:
>   		return -EINVAL;
> @@ -6743,10 +6910,724 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>   	return ret;
>   }
>   
> +/**
> + * i40e_set_cld_element - sets cloud filter element data
> + * @filter: cloud filter rule
> + * @cld: ptr to cloud filter element data
> + *
> + * This is helper function to copy data into cloud filter element
> + **/
> +static inline void
> +i40e_set_cld_element(struct i40e_cloud_filter *filter,
> +		     struct i40e_aqc_add_remove_cloud_filters_element_data *cld)
> +{
> +	u8 *dest_ipaddr;
> +	u32 ipaddr;
> +	int i;
> +
> +	memset(cld, 0, sizeof(*cld));
> +
> +	ether_addr_copy(cld->outer_mac, filter->dst_mac);
> +	ether_addr_copy(cld->inner_mac, filter->src_mac);
> +
> +	if (filter->is_ipv6) {
> +		dest_ipaddr = (u8 *)&cld->ipaddr.v6.data;
> +		for (i = ARRAY_SIZE(filter->dst_ipv6) - 1; i >= 0; i--) {
> +			memcpy(dest_ipaddr, &filter->dst_ipv6[i], 1);
> +			dest_ipaddr++;
> +		}
> +	} else {
> +		ipaddr = be32_to_cpu(filter->dst_ip[0]);
> +		memcpy(&cld->ipaddr.v4.data, &ipaddr, 4);
> +	}
> +
> +	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
> +	cld->tenant_id = cpu_to_le32(filter->tenant_id);
> +}
> +
> +/**
> + * i40e_add_del_cloud_filter - Add/del cloud filter
> + * @vsi: pointer to VSI
> + * @filter: cloud filter rule
> + * @add: if true, add, if false, delete
> + *
> + * Add or delete a cloud filter for a specific flow spec.
> + * Returns 0 if the filter were successfully added.
> + **/
> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
> +				     struct i40e_cloud_filter *filter, bool add)
> +{
> +	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
> +	struct i40e_pf *pf = vsi->back;
> +	int ret;
> +	static const u16 flag_table[128] = {
> +		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
> +		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
> +		[I40E_CLOUD_FILTER_FLAGS_IIP] =
> +			I40E_AQC_ADD_CLOUD_FILTER_IIP,
> +	};
> +
> +	if (filter->flags >= ARRAY_SIZE(flag_table))
> +		return I40E_ERR_CONFIG;
> +
> +	/* copy element needed to add cloud filter from filter */
> +	i40e_set_cld_element(filter, &cld_filter);
> +
> +	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
> +		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
> +					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
> +
> +	if (filter->is_ipv6)
> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
> +	else
> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
> +
> +	if (add)
> +		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
> +						&cld_filter, 1);
> +	else
> +		ret = i40e_aq_remove_cloud_filters(&pf->hw, filter->seid,
> +						   &cld_filter, 1);
> +	if (ret)
> +		dev_dbg(&pf->pdev->dev,
> +			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
> +			add ? "add" : "delete", filter->dst_port, ret,
> +			pf->hw.aq.asq_last_status);
> +
> +	dev_info(&pf->pdev->dev,
> +		 "%s cloud filter for VSI: %d\n", add ? "Added" : "Deleted",
> +		 filter->seid);

If there was an error, this dev_info() is lying to the user.

> +	return ret;
> +}
> +
> +/**
> + * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
> + * @vsi: pointer to VSI
> + * @filter: cloud filter rule
> + * @add: if true, add, if false, delete
> + *
> + * Add or delete a cloud filter for a specific flow spec using big buffer.
> + * Returns 0 if the filter were successfully added.
> + **/
> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
> +					     struct i40e_cloud_filter *filter,
> +					     bool add)
> +{
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data cld_filter;
> +	struct i40e_pf *pf = vsi->back;
> +	int ret;
> +
> +	/* Both (Outer/Inner) valid mac_addr are not supported */
> +	if (is_valid_ether_addr(filter->dst_mac) &&
> +	    is_valid_ether_addr(filter->src_mac))
> +		return -EINVAL;
> +
> +	/* Make sure port is specified, otherwise bail out, for channel
> +	 * specific cloud filter needs 'L4 port' to be non-zero
> +	 */
> +	if (!filter->dst_port)
> +		return -EINVAL;
> +
> +	/* adding filter using src_port/src_ip is not supported at this stage */
> +	if (filter->src_port || filter->src_ip[0])
> +		return -EINVAL;
> +
> +	/* copy element needed to add cloud filter from filter */
> +	i40e_set_cld_element(filter, &cld_filter.element);
> +
> +	if (is_valid_ether_addr(filter->dst_mac) ||
> +	    is_valid_ether_addr(filter->src_mac) ||
> +	    is_multicast_ether_addr(filter->dst_mac) ||
> +	    is_multicast_ether_addr(filter->src_mac)) {
> +		/* MAC + IP : unsupported mode */
> +		if (filter->dst_ip[0])
> +			return -EINVAL;
> +
> +		/* since we validated that L4 port must be valid before
> +		 * we get here, start with respective "flags" value
> +		 * and update if vlan is present or not
> +		 */
> +		cld_filter.element.flags =
> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
> +
> +		if (filter->vlan_id) {
> +			cld_filter.element.flags =
> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
> +		}

		cld_filter.element.flags = cpu_to_le16(filter->vlan_id
				? I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT;
				: I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);


> +
> +	} else if (filter->dst_ip[0] || filter->is_ipv6) {
> +		cld_filter.element.flags =
> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
> +		if (filter->is_ipv6)
> +			cld_filter.element.flags |=
> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
> +		else
> +			cld_filter.element.flags |=
> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);

		cld_filter.element.flags |= cpu_to_le16(filter->is_ipv6
					? I40E_AQC_ADD_CLOUD_FLAGS_IPV6
					: I40E_AQC_ADD_CLOUD_FLAGS_IPV4);


> +	} else {
> +		dev_err(&pf->pdev->dev,
> +			"either mac or ip has to be valid for cloud filter\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Now copy L4 port in Byte 6..7 in general fields */
> +	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
> +						be16_to_cpu(filter->dst_port);
> +
> +	if (add) {
> +		bool proto_type, port_type;
> +
> +		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
> +		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
> +			     true : false;
> +
> +		/* For now, src port based cloud filter for channel is not
> +		 * supported
> +		 */
> +		if (!port_type) {
> +			dev_err(&pf->pdev->dev,
> +				"unsupported port type (src port)\n");
> +			return -EOPNOTSUPP;
> +		}
> +
> +		/* Validate current device switch mode, change if necessary */
> +		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
> +							port_type);
> +		if (ret) {
> +			dev_err(&pf->pdev->dev,
> +				"fail to and set switch mode, ret %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		ret = i40e_aq_add_cloud_filters_big_buffer(&pf->hw,
> +							   filter->seid,
> +							   &cld_filter, 1);
> +	} else {
> +		ret = i40e_aq_remove_cloud_filters_big_buffer(&pf->hw,
> +							      filter->seid,
> +							      &cld_filter, 1);
> +	}
> +
> +	if (ret)
> +		dev_dbg(&pf->pdev->dev,
> +			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
> +			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
> +
> +	dev_info(&pf->pdev->dev,
> +		 "%s cloud filter for VSI: %d, L4 port: %d\n",
> +		 add ? "add" : "delete", filter->seid, ntohs(filter->dst_port));

If there was an error, this dev_info is possibly lying to the user.

> +
> +	return ret;
> +}
> +
> +/**
> + * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + * @filter: Pointer to cloud filter structure
> + *
> + **/
> +static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
> +				 struct tc_cls_flower_offload *f,
> +				 struct i40e_cloud_filter *filter)
> +{
> +	struct i40e_pf *pf = vsi->back;
> +	u16 addr_type = 0;
> +	u8 field_flags = 0;
> +
> +	if (f->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_PORTS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
> +		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
> +			f->dissector->used_keys);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
> +		struct flow_dissector_key_keyid *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
> +						  f->key);
> +
> +		struct flow_dissector_key_keyid *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
> +						  f->mask);
> +
> +		if (mask->keyid != 0)
> +			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
> +
> +		filter->tenant_id = be32_to_cpu(key->keyid);
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
> +		struct flow_dissector_key_basic *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_BASIC,
> +						  f->key);
> +
> +		filter->ip_proto = key->ip_proto;
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
> +		struct flow_dissector_key_eth_addrs *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
> +						  f->key);
> +
> +		struct flow_dissector_key_eth_addrs *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
> +						  f->mask);
> +
> +		/* use is_broadcast and is_zero to check for all 0xf or 0 */
> +		if (!is_zero_ether_addr(mask->dst)) {
> +			if (is_broadcast_ether_addr(mask->dst)) {
> +				field_flags |= I40E_CLOUD_FIELD_OMAC;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
> +					mask->dst);
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (!is_zero_ether_addr(mask->src)) {
> +			if (is_broadcast_ether_addr(mask->src)) {
> +				field_flags |= I40E_CLOUD_FIELD_IMAC;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
> +					mask->src);
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +		ether_addr_copy(filter->dst_mac, key->dst);
> +		ether_addr_copy(filter->src_mac, key->src);
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
> +		struct flow_dissector_key_vlan *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_VLAN,
> +						  f->key);
> +		struct flow_dissector_key_vlan *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_VLAN,
> +						  f->mask);
> +
> +		if (mask->vlan_id) {
> +			if (mask->vlan_id == VLAN_VID_MASK) {
> +				field_flags |= I40E_CLOUD_FIELD_IVLAN;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad vlan mask %u\n",

Should this be a %04x rather than %u?

> +					mask->vlan_id);
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		filter->vlan_id = cpu_to_be16(key->vlan_id);
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
> +		struct flow_dissector_key_control *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_CONTROL,
> +						  f->key);
> +
> +		addr_type = key->addr_type;
> +	}
> +
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
> +		struct flow_dissector_key_ipv4_addrs *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
> +						  f->key);
> +		struct flow_dissector_key_ipv4_addrs *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
> +						  f->mask);
> +
> +		if (mask->dst) {
> +			if (mask->dst == cpu_to_be32(0xffffffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
> +					be32_to_cpu(mask->dst));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (mask->src) {
> +			if (mask->src == cpu_to_be32(0xffffffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
> +					be32_to_cpu(mask->dst));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
> +			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
> +			return I40E_ERR_CONFIG;
> +		}
> +		filter->dst_ip[0] = key->dst;
> +		filter->src_ip[0] = key->src;
> +	}
> +
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
> +		struct flow_dissector_key_ipv6_addrs *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
> +						  f->key);
> +		struct flow_dissector_key_ipv6_addrs *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
> +						  f->mask);
> +
> +		/* validate mask, make sure it is not IPV6_ADDR_ANY */
> +		if (ipv6_addr_any(&mask->dst)) {
> +			dev_err(&pf->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
> +				IPV6_ADDR_ANY);
> +			return I40E_ERR_CONFIG;
> +		}
> +
> +		/* validate src and dest IPV6 address, make sure they are not
> +		 * ANY (0:0:0:0:0:0:0:0) or LOOPBACK (0:0:0:0:0:0:0:1), which
> +		 * can be represented as ::1
> +		 */
> +		if (ipv6_addr_any(&key->dst) || ipv6_addr_loopback(&key->dst)) {
> +			dev_err(&pf->pdev->dev,
> +				"Bad ipv6 dst addr is ANY or LOOPBACK\n");
> +			return I40E_ERR_CONFIG;
> +		}
> +		if (ipv6_addr_loopback(&key->src)) {
> +			dev_err(&pf->pdev->dev,
> +				"Bad ipv6 src addr is ANY or LOOPBACK\n");
> +			return I40E_ERR_CONFIG;
> +		}
> +		memcpy(&filter->src_ipv6, &key->src.s6_addr,
> +		       ARRAY_SIZE(filter->src_ipv6));
> +		memcpy(&filter->dst_ipv6, &key->dst.s6_addr,
> +		       ARRAY_SIZE(filter->dst_ipv6));
> +
> +		/* mark it as IPv6 filter, to be used later */
> +		filter->is_ipv6 = true;
> +
> +		/* and it is IP[4|6] filter type */
> +		field_flags |= I40E_CLOUD_FIELD_IIP;
> +	}
> +
> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
> +		struct flow_dissector_key_ports *key =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_PORTS,
> +						  f->key);
> +		struct flow_dissector_key_ports *mask =
> +			skb_flow_dissector_target(f->dissector,
> +						  FLOW_DISSECTOR_KEY_PORTS,
> +						  f->mask);
> +
> +		if (mask->src) {
> +			if (mask->src == cpu_to_be16(0xffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad src port mask %u\n",

Should this be a %04x rather than %u?

> +					be16_to_cpu(mask->src));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		if (mask->dst) {
> +			if (mask->dst == cpu_to_be16(0xffff)) {
> +				field_flags |= I40E_CLOUD_FIELD_IIP;
> +			} else {
> +				dev_err(&pf->pdev->dev, "Bad dst port mask %u\n",

Should this be a %04x rather than %u?

> +					be16_to_cpu(mask->dst));
> +				return I40E_ERR_CONFIG;
> +			}
> +		}
> +
> +		filter->dst_port = key->dst;
> +		filter->src_port = key->src;
> +
> +		/* For now, only supports destination port*/
> +		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
> +
> +		switch (filter->ip_proto) {
> +		case IPPROTO_TCP:
> +		case IPPROTO_UDP:
> +			break;
> +		default:
> +			dev_err(&pf->pdev->dev,
> +				"Only UDP and TCP transport are supported\n");
> +			return -EINVAL;
> +		}
> +	}
> +	filter->flags = field_flags;
> +	return 0;
> +}
> +
> +/**
> + * i40e_handle_redirect_action: Forward to a traffic class on the device
> + * @vsi: Pointer to VSI
> + * @ifindex: ifindex of the device to forwared to
> + * @tc: traffic class index on the device
> + * @filter: Pointer to cloud filter structure
> + *
> + **/
> +static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
> +				       struct i40e_cloud_filter *filter)
> +{
> +	struct i40e_channel *ch, *ch_tmp;
> +
> +	/* redirect to a traffic class on the same device */
> +	if (vsi->netdev->ifindex == ifindex) {
> +		if (tc == 0) {
> +			filter->seid = vsi->seid;
> +			return 0;
> +		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
> +			if (!filter->dst_port) {
> +				dev_err(&vsi->back->pdev->dev,
> +					"Specify destination port to redirect to traffic class that is not default\n");
> +				return -EINVAL;
> +			}
> +			if (list_empty(&vsi->ch_list))
> +				return -EINVAL;
> +			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
> +						 list) {
> +				if (ch->seid == vsi->tc_seid_map[tc])
> +					filter->seid = ch->seid;
> +			}
> +			return 0;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +/**
> + * i40e_parse_tc_actions - Parse tc actions
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + * @filter: Pointer to cloud filter structure
> + *
> + **/
> +static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
> +				 struct i40e_cloud_filter *filter)
> +{
> +	const struct tc_action *a;
> +	LIST_HEAD(actions);
> +	int err;
> +
> +	if (tc_no_actions(exts))
> +		return -EINVAL;
> +
> +	tcf_exts_to_list(exts, &actions);
> +	list_for_each_entry(a, &actions, list) {
> +		/* Drop action */
> +		if (is_tcf_gact_shot(a)) {
> +			dev_err(&vsi->back->pdev->dev,
> +				"Cloud filters do not support the drop action.\n");
> +			return -EOPNOTSUPP;
> +		}
> +
> +		/* Redirect to a traffic class on the same device */
> +		if (!is_tcf_mirred_egress_redirect(a)) {
> +			int ifindex = tcf_mirred_ifindex(a);
> +			int tc = tcf_mirred_tc(a);
> +
> +			err = i40e_handle_redirect_action(vsi, ifindex, tc,
> +							  filter);
> +			if (err == 0)
> +				return err;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +/**
> + * i40e_configure_clsflower - Configure tc flower filters
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + *
> + **/
> +static int i40e_configure_clsflower(struct i40e_vsi *vsi,
> +				    struct tc_cls_flower_offload *cls_flower)
> +{
> +	struct i40e_cloud_filter *filter = NULL;
> +	struct i40e_pf *pf = vsi->back;
> +	int err = 0;
> +
> +	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
> +	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
> +		return -EBUSY;
> +
> +	if (pf->fdir_pf_active_filters ||
> +	    (!hlist_empty(&pf->fdir_filter_list))) {
> +		dev_err(&vsi->back->pdev->dev,
> +			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
> +		return -EINVAL;
> +	}
> +
> +	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
> +		dev_err(&vsi->back->pdev->dev,
> +			"Disable Flow Director Sideband while configuring Cloud filters via tc-flower\n");
> +		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
> +	}
> +
> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> +	if (!filter)
> +		return -ENOMEM;
> +
> +	filter->cookie = cls_flower->cookie;
> +
> +	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
> +	if (err < 0)
> +		goto err;
> +
> +	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
> +	if (err < 0)
> +		goto err;
> +
> +	/* Add cloud filter */
> +	if (filter->dst_port)
> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
> +	else
> +		err = i40e_add_del_cloud_filter(vsi, filter, true);
> +
> +	if (err) {
> +		dev_err(&pf->pdev->dev,
> +			"Failed to add cloud filter, err %s\n",
> +			i40e_stat_str(&pf->hw, err));
> +		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
> +		goto err;
> +	}
> +
> +	/* add filter to the ordered list */
> +	INIT_HLIST_NODE(&filter->cloud_node);
> +
> +	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
> +
> +	pf->num_cloud_filters++;
> +
> +	return err;
> +err:
> +	kfree(filter);
> +	return err;
> +}
> +
> +/**
> + * i40e_find_cloud_filter - Find the could filter in the list
> + * @vsi: Pointer to VSI
> + * @cookie: filter specific cookie
> + *
> + **/
> +static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
> +							unsigned long *cookie)
> +{
> +	struct i40e_cloud_filter *filter = NULL;
> +	struct hlist_node *node2;
> +
> +	hlist_for_each_entry_safe(filter, node2,
> +				  &vsi->back->cloud_filter_list, cloud_node)
> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
> +			return filter;
> +	return NULL;
> +}
> +
> +/**
> + * i40e_delete_clsflower - Remove tc flower filters
> + * @vsi: Pointer to VSI
> + * @cls_flower: Pointer to struct tc_cls_flower_offload
> + *
> + **/
> +static int i40e_delete_clsflower(struct i40e_vsi *vsi,
> +				 struct tc_cls_flower_offload *cls_flower)
> +{
> +	struct i40e_cloud_filter *filter = NULL;
> +	struct i40e_pf *pf = vsi->back;
> +	int err = 0;
> +
> +	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
> +
> +	if (!filter)
> +		return -EINVAL;
> +
> +	hash_del(&filter->cloud_node);
> +
> +	if (filter->dst_port)
> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
> +	else
> +		err = i40e_add_del_cloud_filter(vsi, filter, false);
> +	if (err) {
> +		kfree(filter);
> +		dev_err(&pf->pdev->dev,
> +			"Failed to delete cloud filter, err %s\n",
> +			i40e_stat_str(&pf->hw, err));
> +		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);

Hmmm... okay, maybe those earlier functions are not really lying if they 
have this followup to check them.

> +	}
> +
> +	kfree(filter);
> +	pf->num_cloud_filters--;
> +
> +	if (!pf->num_cloud_filters) {
> +		/* Re-enable FD-SB that was disabled while configuring cloud
> +		 * filters
> +		 */
> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
> +		    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
> +			if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
> +			      pf->hw.num_partitions > 1))
> +				pf->flags |= I40E_FLAG_FD_SB_ENABLED;

What if FD_SB was disabled for other reasons, like not enough msix 
vectors or queues, or some other odd circumstance that turns them off?

> +		}
> +	}
> +
> +	return 0;
> +}
> +
>   static int __i40e_setup_tc(struct net_device *netdev, u32 handle,
>   			   u32 chain_index, __be16 proto,
>   			   struct tc_to_netdev *tc)
>   {
> +	struct i40e_netdev_priv *np = netdev_priv(netdev);
> +	struct i40e_vsi *vsi = np->vsi;
> +
> +	if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
> +	    tc->type == TC_SETUP_CLSFLOWER) {
> +		switch (tc->cls_flower->command) {
> +		case TC_CLSFLOWER_REPLACE:
> +			return i40e_configure_clsflower(vsi, tc->cls_flower);
> +		case TC_CLSFLOWER_DESTROY:
> +			return i40e_delete_clsflower(vsi, tc->cls_flower);
> +		case TC_CLSFLOWER_STATS:
> +			return -EOPNOTSUPP;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
>   	return i40e_setup_tc(netdev, tc);
>   }
>   
> @@ -6948,6 +7829,16 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>   		kfree(cfilter);
>   	}
>   	pf->num_cloud_filters = 0;
> +
> +	/* Re-enable FD-SB that was disabled while configuring cloud
> +	 * filters
> +	 */
> +	if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
> +	    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
> +		if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
> +		      pf->hw.num_partitions > 1))
> +			pf->flags |= I40E_FLAG_FD_SB_ENABLED;

What if FD_SB was disabled for other reasons, like not enough msix 
vectors or queues, or some other odd circumstance that turns them off?

This is getting complex and repetative.  Maybe there needs to be a 
little function that checks all the FD_SB conditions and can be used 
from anywhere needed.

> +	}
>   }
>   
>   /**
> @@ -8157,6 +9048,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
>   }
>   
>   /**
> + * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
> + * @vsi: PF main vsi
> + * @seid: seid of main or channel VSIs
> + *
> + * Rebuilds cloud filters associated with main VSI and channel VSIs if they
> + * existed before reset
> + **/
> +static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
> +{
> +	struct i40e_cloud_filter *cfilter;
> +	struct i40e_pf *pf = vsi->back;
> +	struct hlist_node *node;
> +	i40e_status ret;
> +
> +	/* Add cloud filters back if they exist */
> +	if (hlist_empty(&pf->cloud_filter_list))
> +		return 0;
> +
> +	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
> +				  cloud_node) {
> +		if (cfilter->seid != seid)
> +			continue;
> +
> +		if (cfilter->dst_port)
> +			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
> +								true);
> +		else
> +			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
> +
> +		if (ret) {
> +			dev_dbg(&pf->pdev->dev,
> +				"Failed to rebuild cloud filter, err %s aq_err %s\n",
> +				i40e_stat_str(&pf->hw, ret),
> +				i40e_aq_str(&pf->hw,
> +					    pf->hw.aq.asq_last_status));
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/**
>    * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
>    * @vsi: PF main vsi
>    *
> @@ -8193,6 +9126,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
>   						I40E_BW_CREDIT_DIVISOR,
>   				ch->seid);
>   		}
> +		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
> +		if (ret) {
> +			dev_dbg(&vsi->back->pdev->dev,
> +				"Failed to rebuild cloud filters for channel VSI %u\n",
> +				ch->seid);
> +			return ret;
> +		}
>   	}
>   	return 0;
>   }
> @@ -8476,6 +9416,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>   			goto end_unlock;
>   	}
>   
> +	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
> +	if (ret)
> +		goto end_unlock;
> +
>   	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
>   	 * for this main VSI if they exist
>   	 */
> @@ -10846,7 +11790,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
>   		netdev->hw_features |= NETIF_F_NTUPLE;
>   	hw_features = hw_enc_features		|
>   		      NETIF_F_HW_VLAN_CTAG_TX	|
> -		      NETIF_F_HW_VLAN_CTAG_RX;
> +		      NETIF_F_HW_VLAN_CTAG_RX	|
> +		      NETIF_F_HW_TC;
>   
>   	netdev->hw_features |= hw_features;
>   
> @@ -12123,8 +13068,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>   	*/
>   
>   	if ((pf->hw.pf_id == 0) &&
> -	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
> +	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
>   		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
> +		pf->last_sw_conf_flags = flags;
> +	}
>   
>   	if (pf->hw.pf_id == 0) {
>   		u16 valid_flags;
> @@ -12140,6 +13087,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>   					     pf->hw.aq.asq_last_status));
>   			/* not a fatal problem, just keep going */
>   		}
> +		pf->last_sw_conf_valid_flags = valid_flags;
>   	}
>   
>   	/* first time setup */
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> index 9142d0d..e24f1ce 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> @@ -283,6 +283,23 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
>   		struct i40e_asq_cmd_details *cmd_details);
>   i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
>   				   struct i40e_asq_cmd_details *cmd_details);
> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
> +	u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count);
> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
> +		u16 vsi,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count);
> +
> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
> +		u16 vsi,
> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
> +		u8 filter_count);
> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
> +	struct i40e_hw *hw, u16 seid,
> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
> +	u8 filter_count);
>   i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
>   			       struct i40e_lldp_variables *lldp_cfg);
>   /* i40e_common */
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
  2017-08-01 10:15   ` [Intel-wired-lan] " Jamal Hadi Salim
@ 2017-08-02  0:57     ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  0:57 UTC (permalink / raw)
  To: Jamal Hadi Salim, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz


On 8/1/2017 3:15 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:36 PM, Amritha Nambiar wrote:
>> This patch series enables configuring cloud filters in i40e
>> using the tc/flower classifier. The only tc-filter action
>> supported is to redirect packets to a traffic class on the
>> same device. The tc/mirred:redirect action is extended to
>> accept a traffic class to achieve this.
>>
>> The cloud filters are added for a VSI and are cleaned up when
>> the VSI is deleted. The filters that match on L4 ports needs
>> enhanced admin queue functions with big buffer support for
>> extended general fields in Add/Remove Cloud filters command.
>>
>> Example:
>> # tc qdisc add dev eth0 ingress
>>
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>
> 
> I think "queue 1" sounds better than "tc 1".
> "tc" is  already a keyword in a few places (even within that declaration
> above).

The idea is to redirect to a traffic class that has queues assigned to
it and not a single queue i.e. these are actually queue groups and not a
single queue. So may be "qgroup 1" or "tcqgroup 1" fits better.

> 
> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-02  0:57     ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  0:57 UTC (permalink / raw)
  To: intel-wired-lan


On 8/1/2017 3:15 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:36 PM, Amritha Nambiar wrote:
>> This patch series enables configuring cloud filters in i40e
>> using the tc/flower classifier. The only tc-filter action
>> supported is to redirect packets to a traffic class on the
>> same device. The tc/mirred:redirect action is extended to
>> accept a traffic class to achieve this.
>>
>> The cloud filters are added for a VSI and are cleaned up when
>> the VSI is deleted. The filters that match on L4 ports needs
>> enhanced admin queue functions with big buffer support for
>> extended general fields in Add/Remove Cloud filters command.
>>
>> Example:
>> # tc qdisc add dev eth0 ingress
>>
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>
> 
> I think "queue 1" sounds better than "tc 1".
> "tc" is  already a keyword in a few places (even within that declaration
> above).

The idea is to redirect to a traffic class that has queues assigned to
it and not a single queue i.e. these are actually queue groups and not a
single queue. So may be "qgroup 1" or "tcqgroup 1" fits better.

> 
> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01 10:22     ` [Intel-wired-lan] " Jamal Hadi Salim
@ 2017-08-02  1:12       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  1:12 UTC (permalink / raw)
  To: Jamal Hadi Salim, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny


On 8/1/2017 3:22 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:37 PM, Amritha Nambiar wrote:
>> The Mirred/redirect action is extended to forward to a traffic
>> class on the device. The traffic class index needs to be
>> provided in addition to the device's ifindex.
>>
>> Example:
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>>   3 files changed, 29 insertions(+)
>>
>> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>> index 604bc31..60058c4 100644
>> --- a/include/net/tc_act/tc_mirred.h
>> +++ b/include/net/tc_act/tc_mirred.h
>> @@ -9,6 +9,8 @@ struct tcf_mirred {
>>   	int			tcfm_eaction;
>>   	int			tcfm_ifindex;
>>   	bool			tcfm_mac_header_xmit;
>> +	u8			tcfm_tc;
>> +	u32			flags;
>>   	struct net_device __rcu	*tcfm_dev;
>>   	struct list_head	tcfm_list;
>>   };
>> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>>   	return to_mirred(a)->tcfm_ifindex;
>>   }
>>   
>> +static inline int tcf_mirred_tc(const struct tc_action *a)
>> +{
>> +	return to_mirred(a)->tcfm_tc;
>> +}
>> +
>>   #endif /* __NET_TC_MIR_H */
>> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>> index 3d7a2b3..8ff4d76 100644
>> --- a/include/uapi/linux/tc_act/tc_mirred.h
>> +++ b/include/uapi/linux/tc_act/tc_mirred.h
>> @@ -9,6 +9,10 @@
>>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>> +
>> +#define MIRRED_F_TC_MAP		0x1
>> +#define MIRRED_TC_MAP_MAX	0x10
> 
> Assuming this is the max number of queues?
> Where does this upper bound come from? Is it a spec
> or an intel thing? If spec - mentioning which
> spec and section would be useful.

This is the max number of TCs. The Linux upper bound for this is defined
in linux/netdevice.h. I will fix this part to remove the definition here
and reuse the existing one.

> 
> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-02  1:12       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  1:12 UTC (permalink / raw)
  To: intel-wired-lan


On 8/1/2017 3:22 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:37 PM, Amritha Nambiar wrote:
>> The Mirred/redirect action is extended to forward to a traffic
>> class on the device. The traffic class index needs to be
>> provided in addition to the device's ifindex.
>>
>> Example:
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>>   3 files changed, 29 insertions(+)
>>
>> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>> index 604bc31..60058c4 100644
>> --- a/include/net/tc_act/tc_mirred.h
>> +++ b/include/net/tc_act/tc_mirred.h
>> @@ -9,6 +9,8 @@ struct tcf_mirred {
>>   	int			tcfm_eaction;
>>   	int			tcfm_ifindex;
>>   	bool			tcfm_mac_header_xmit;
>> +	u8			tcfm_tc;
>> +	u32			flags;
>>   	struct net_device __rcu	*tcfm_dev;
>>   	struct list_head	tcfm_list;
>>   };
>> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>>   	return to_mirred(a)->tcfm_ifindex;
>>   }
>>   
>> +static inline int tcf_mirred_tc(const struct tc_action *a)
>> +{
>> +	return to_mirred(a)->tcfm_tc;
>> +}
>> +
>>   #endif /* __NET_TC_MIR_H */
>> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>> index 3d7a2b3..8ff4d76 100644
>> --- a/include/uapi/linux/tc_act/tc_mirred.h
>> +++ b/include/uapi/linux/tc_act/tc_mirred.h
>> @@ -9,6 +9,10 @@
>>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>> +
>> +#define MIRRED_F_TC_MAP		0x1
>> +#define MIRRED_TC_MAP_MAX	0x10
> 
> Assuming this is the max number of queues?
> Where does this upper bound come from? Is it a spec
> or an intel thing? If spec - mentioning which
> spec and section would be useful.

This is the max number of TCs. The Linux upper bound for this is defined
in linux/netdevice.h. I will fix this part to remove the definition here
and reuse the existing one.

> 
> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01 10:44     ` [Intel-wired-lan] " Jamal Hadi Salim
@ 2017-08-02  1:42       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  1:42 UTC (permalink / raw)
  To: Jamal Hadi Salim, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny


On 8/1/2017 3:44 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:37 PM, Amritha Nambiar wrote:
>> The Mirred/redirect action is extended to forward to a traffic
>> class on the device. The traffic class index needs to be
>> provided in addition to the device's ifindex.
>>
>> Example:
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>>   3 files changed, 29 insertions(+)
>>
>> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>> index 604bc31..60058c4 100644
>> --- a/include/net/tc_act/tc_mirred.h
>> +++ b/include/net/tc_act/tc_mirred.h
>> @@ -9,6 +9,8 @@ struct tcf_mirred {
>>   	int			tcfm_eaction;
>>   	int			tcfm_ifindex;
>>   	bool			tcfm_mac_header_xmit;
>> +	u8			tcfm_tc;
>> +	u32			flags;
>>   	struct net_device __rcu	*tcfm_dev;
>>   	struct list_head	tcfm_list;
>>   };
>> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>>   	return to_mirred(a)->tcfm_ifindex;
>>   }
>>   
>> +static inline int tcf_mirred_tc(const struct tc_action *a)
>> +{
>> +	return to_mirred(a)->tcfm_tc;
>> +}
>> +
>>   #endif /* __NET_TC_MIR_H */
>> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>> index 3d7a2b3..8ff4d76 100644
>> --- a/include/uapi/linux/tc_act/tc_mirred.h
>> +++ b/include/uapi/linux/tc_act/tc_mirred.h
>> @@ -9,6 +9,10 @@
>>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>> +
>> +#define MIRRED_F_TC_MAP		0x1
>> +#define MIRRED_TC_MAP_MAX	0x10
>> +#define MIRRED_TC_MAP_MASK	0xF
>>                                                                                   
>>   struct tc_mirred {
>>   	tc_gen;
>> @@ -21,6 +25,7 @@ enum {
>>   	TCA_MIRRED_TM,
>>   	TCA_MIRRED_PARMS,
>>   	TCA_MIRRED_PAD,
>> +	TCA_MIRRED_TC_MAP,
>>   	__TCA_MIRRED_MAX
>>   };
>>   #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
>> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
>> index 1b5549a..f9801de 100644
>> --- a/net/sched/act_mirred.c
>> +++ b/net/sched/act_mirred.c
>> @@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
>>   
>>   static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
>>   	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
>> +	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
>>   };
>>   
>>   static unsigned int mirred_net_id;
>> @@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   	struct tcf_mirred *m;
>>   	struct net_device *dev;
>>   	bool exists = false;
>> +	u8 *tc_map = NULL;
>> +	u32 flags = 0;
>>   	int ret;
>>   
>>   	if (nla == NULL)
>> @@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   		return ret;
>>   	if (tb[TCA_MIRRED_PARMS] == NULL)
>>   		return -EINVAL;
>> +
>> +	if (tb[TCA_MIRRED_TC_MAP]) {
>> +		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
>> +		if (*tc_map >= MIRRED_TC_MAP_MAX)
>> +			return -EINVAL;
>> +		flags |= MIRRED_F_TC_MAP;
> 
> 
>> +	}
>> +
>>   	parm = nla_data(tb[TCA_MIRRED_PARMS]);
>>   
>>   	exists = tcf_hash_check(tn, parm->index, a, bind);
>> @@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   	ASSERT_RTNL();
>>   	m->tcf_action = parm->action;
>>   	m->tcfm_eaction = parm->eaction;
>> +	m->flags = flags;
>>   	if (dev != NULL) {
>>   		m->tcfm_ifindex = parm->ifindex;
>>   		if (ret != ACT_P_CREATED)
>> @@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   		dev_hold(dev);
>>   		rcu_assign_pointer(m->tcfm_dev, dev);
>>   		m->tcfm_mac_header_xmit = mac_header_xmit;
>> +		if (flags & MIRRED_F_TC_MAP)
>> +			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
>>   	}
>>   
> Is the mask a hardware limit. I dont know how these queues are
> allocated - I am assuming each of these "tc queues" maps to a rx
> DMA ring?

This is the bitmask for TC max range again defined in linux/netdevice.h.
I'll fix this to remove the new definition I have (MIRRED_TC_MAP_MASK)
here and replace with the existing TC_BITMASK. These are just the
traffic class index that are offloaded to the device. I had submitted
another series that handles configuring queues for the traffic classes
and queue layout offloaded via the mqprio framework.

> 
>>   	if (ret == ACT_P_CREATED) {
>> @@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
>>   
>>   	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
>>   		goto nla_put_failure;
>> +	if ((m->flags & MIRRED_F_TC_MAP) &&
>> +	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
>> +		goto nla_put_failure;
>>
> 
> If you have m->tcfm_tc then I dont think you need the flags; so i would
> remove it from the struct altogether.

I think I would still need the flags as "tc" index is an optional
parameter and the value zero is also a valid TC index. I was also
planning to use this flag to address Jiri's comment to fail this rule in
devices not supporting this offload.

> 
> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-02  1:42       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  1:42 UTC (permalink / raw)
  To: intel-wired-lan


On 8/1/2017 3:44 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:37 PM, Amritha Nambiar wrote:
>> The Mirred/redirect action is extended to forward to a traffic
>> class on the device. The traffic class index needs to be
>> provided in addition to the device's ifindex.
>>
>> Example:
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>    dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>    skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   include/net/tc_act/tc_mirred.h        |    7 +++++++
>>   include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>>   net/sched/act_mirred.c                |   17 +++++++++++++++++
>>   3 files changed, 29 insertions(+)
>>
>> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>> index 604bc31..60058c4 100644
>> --- a/include/net/tc_act/tc_mirred.h
>> +++ b/include/net/tc_act/tc_mirred.h
>> @@ -9,6 +9,8 @@ struct tcf_mirred {
>>   	int			tcfm_eaction;
>>   	int			tcfm_ifindex;
>>   	bool			tcfm_mac_header_xmit;
>> +	u8			tcfm_tc;
>> +	u32			flags;
>>   	struct net_device __rcu	*tcfm_dev;
>>   	struct list_head	tcfm_list;
>>   };
>> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>>   	return to_mirred(a)->tcfm_ifindex;
>>   }
>>   
>> +static inline int tcf_mirred_tc(const struct tc_action *a)
>> +{
>> +	return to_mirred(a)->tcfm_tc;
>> +}
>> +
>>   #endif /* __NET_TC_MIR_H */
>> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>> index 3d7a2b3..8ff4d76 100644
>> --- a/include/uapi/linux/tc_act/tc_mirred.h
>> +++ b/include/uapi/linux/tc_act/tc_mirred.h
>> @@ -9,6 +9,10 @@
>>   #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>>   #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>>   #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>> +
>> +#define MIRRED_F_TC_MAP		0x1
>> +#define MIRRED_TC_MAP_MAX	0x10
>> +#define MIRRED_TC_MAP_MASK	0xF
>>                                                                                   
>>   struct tc_mirred {
>>   	tc_gen;
>> @@ -21,6 +25,7 @@ enum {
>>   	TCA_MIRRED_TM,
>>   	TCA_MIRRED_PARMS,
>>   	TCA_MIRRED_PAD,
>> +	TCA_MIRRED_TC_MAP,
>>   	__TCA_MIRRED_MAX
>>   };
>>   #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
>> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
>> index 1b5549a..f9801de 100644
>> --- a/net/sched/act_mirred.c
>> +++ b/net/sched/act_mirred.c
>> @@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
>>   
>>   static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
>>   	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
>> +	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
>>   };
>>   
>>   static unsigned int mirred_net_id;
>> @@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   	struct tcf_mirred *m;
>>   	struct net_device *dev;
>>   	bool exists = false;
>> +	u8 *tc_map = NULL;
>> +	u32 flags = 0;
>>   	int ret;
>>   
>>   	if (nla == NULL)
>> @@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   		return ret;
>>   	if (tb[TCA_MIRRED_PARMS] == NULL)
>>   		return -EINVAL;
>> +
>> +	if (tb[TCA_MIRRED_TC_MAP]) {
>> +		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
>> +		if (*tc_map >= MIRRED_TC_MAP_MAX)
>> +			return -EINVAL;
>> +		flags |= MIRRED_F_TC_MAP;
> 
> 
>> +	}
>> +
>>   	parm = nla_data(tb[TCA_MIRRED_PARMS]);
>>   
>>   	exists = tcf_hash_check(tn, parm->index, a, bind);
>> @@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   	ASSERT_RTNL();
>>   	m->tcf_action = parm->action;
>>   	m->tcfm_eaction = parm->eaction;
>> +	m->flags = flags;
>>   	if (dev != NULL) {
>>   		m->tcfm_ifindex = parm->ifindex;
>>   		if (ret != ACT_P_CREATED)
>> @@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>>   		dev_hold(dev);
>>   		rcu_assign_pointer(m->tcfm_dev, dev);
>>   		m->tcfm_mac_header_xmit = mac_header_xmit;
>> +		if (flags & MIRRED_F_TC_MAP)
>> +			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
>>   	}
>>   
> Is the mask a hardware limit. I dont know how these queues are
> allocated - I am assuming each of these "tc queues" maps to a rx
> DMA ring?

This is the bitmask for TC max range again defined in linux/netdevice.h.
I'll fix this to remove the new definition I have (MIRRED_TC_MAP_MASK)
here and replace with the existing TC_BITMASK. These are just the
traffic class index that are offloaded to the device. I had submitted
another series that handles configuring queues for the traffic classes
and queue layout offloaded via the mqprio framework.

> 
>>   	if (ret == ACT_P_CREATED) {
>> @@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
>>   
>>   	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
>>   		goto nla_put_failure;
>> +	if ((m->flags & MIRRED_F_TC_MAP) &&
>> +	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
>> +		goto nla_put_failure;
>>
> 
> If you have m->tcfm_tc then I dont think you need the flags; so i would
> remove it from the struct altogether.

I think I would still need the flags as "tc" index is an optional
parameter and the value zero is also a valid TC index. I was also
planning to use this flag to address Jiri's comment to fail this rule in
devices not supporting this offload.

> 
> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-01 10:56     ` [Intel-wired-lan] " Jamal Hadi Salim
@ 2017-08-02  2:13       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  2:13 UTC (permalink / raw)
  To: Jamal Hadi Salim, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz


On 8/1/2017 3:56 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:38 PM, Amritha Nambiar wrote:
>> This patch enables tc-flower based hardware offloads. tc/flower
>> filter provided by the kernel is configured as driver specific
>> cloud filter. The patch implements functions and admin queue
>> commands needed to support cloud filters in the driver and
>> adds cloud filters to configure these tc-flower filters.
>>
>> The only action supported is to redirect packets to a traffic class
>> on the same device.
>>
>> # tc qdisc add dev eth0 ingress
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 0
>>
> 
> Out of curiosity - did you need to say "indev eth0" there?

It looks like I don't need to specify "indev eth0". I will need to look
up how this part is offloaded and probably validate in the driver when
this is specified.

> Also: Is it possible to add an skbmark? Example something like
> these that directs two flows to the same queue but different
> skb marks:
> 
> # tc filter add dev eth0 protocol ip parent ffff: \
>    prio 2 flower dst_ip 192.168.3.5/32 \
>    ip_proto udp dst_port 2a skip_sw \
>    action skbedit mark 11 \
>    action mirred ingress redirect dev eth0 tcqueue 1
> 
> # tc filter add dev eth0 protocol ip parent ffff: \
>      prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>      action skbedit mark 12 \
>      action mirred ingress redirect dev eth0 tcqueue 1
> 

It is possible to support the skbedit mark action for the first rule
here (L3 and L4) which I can take up in a subsequent patch, but this
cannot be supported on our device for L2 based match in the second rule.

> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-02  2:13       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  2:13 UTC (permalink / raw)
  To: intel-wired-lan


On 8/1/2017 3:56 AM, Jamal Hadi Salim wrote:
> On 17-07-31 08:38 PM, Amritha Nambiar wrote:
>> This patch enables tc-flower based hardware offloads. tc/flower
>> filter provided by the kernel is configured as driver specific
>> cloud filter. The patch implements functions and admin queue
>> commands needed to support cloud filters in the driver and
>> adds cloud filters to configure these tc-flower filters.
>>
>> The only action supported is to redirect packets to a traffic class
>> on the same device.
>>
>> # tc qdisc add dev eth0 ingress
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 0
>>
> 
> Out of curiosity - did you need to say "indev eth0" there?

It looks like I don't need to specify "indev eth0". I will need to look
up how this part is offloaded and probably validate in the driver when
this is specified.

> Also: Is it possible to add an skbmark? Example something like
> these that directs two flows to the same queue but different
> skb marks:
> 
> # tc filter add dev eth0 protocol ip parent ffff: \
>    prio 2 flower dst_ip 192.168.3.5/32 \
>    ip_proto udp dst_port 2a skip_sw \
>    action skbedit mark 11 \
>    action mirred ingress redirect dev eth0 tcqueue 1
> 
> # tc filter add dev eth0 protocol ip parent ffff: \
>      prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>      action skbedit mark 12 \
>      action mirred ingress redirect dev eth0 tcqueue 1
> 

It is possible to support the skbedit mark action for the first rule
here (L3 and L4) which I can take up in a subsequent patch, but this
cannot be supported on our device for L2 based match in the second rule.

> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
  2017-08-01 11:12     ` [Intel-wired-lan] " Jiri Pirko
@ 2017-08-02  2:20       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  2:20 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: intel-wired-lan, jeffrey.t.kirsher, alexander.h.duyck,
	kiran.patil, netdev, mitch.a.williams, alexander.duyck,
	neerav.parikh, sridhar.samudrala, carolyn.wyborny


On 8/1/2017 4:12 AM, Jiri Pirko wrote:
> Tue, Aug 01, 2017 at 02:37:37AM CEST, amritha.nambiar@intel.com wrote:
>> The Mirred/redirect action is extended to forward to a traffic
>> class on the device. The traffic class index needs to be
>> provided in addition to the device's ifindex.
>>
>> Example:
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> You need to make sure that the current offloaders fill forbid to add
> this rule, not just silently ignore the tc value.

I will fix this in the next version, probably using the 'flags' field
I've defined.

> 
> 
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>> include/net/tc_act/tc_mirred.h        |    7 +++++++
>> include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>> net/sched/act_mirred.c                |   17 +++++++++++++++++
>> 3 files changed, 29 insertions(+)
>>
>> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>> index 604bc31..60058c4 100644
>> --- a/include/net/tc_act/tc_mirred.h
>> +++ b/include/net/tc_act/tc_mirred.h
>> @@ -9,6 +9,8 @@ struct tcf_mirred {
>> 	int			tcfm_eaction;
>> 	int			tcfm_ifindex;
>> 	bool			tcfm_mac_header_xmit;
>> +	u8			tcfm_tc;
>> +	u32			flags;
>> 	struct net_device __rcu	*tcfm_dev;
>> 	struct list_head	tcfm_list;
>> };
>> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>> 	return to_mirred(a)->tcfm_ifindex;
>> }
>>
>> +static inline int tcf_mirred_tc(const struct tc_action *a)
>> +{
>> +	return to_mirred(a)->tcfm_tc;
>> +}
>> +
>> #endif /* __NET_TC_MIR_H */
>> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>> index 3d7a2b3..8ff4d76 100644
>> --- a/include/uapi/linux/tc_act/tc_mirred.h
>> +++ b/include/uapi/linux/tc_act/tc_mirred.h
>> @@ -9,6 +9,10 @@
>> #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>> #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>> #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>> +
>> +#define MIRRED_F_TC_MAP		0x1
>> +#define MIRRED_TC_MAP_MAX	0x10
>> +#define MIRRED_TC_MAP_MASK	0xF
> 
> I'm completely lost. Why do you have these values here? and in fact one
> twice?

I'll fix this to remove the defines for the TC max range and its bitmap
here and reuse the existing ones defined in linux/netdevice.h

> 
> 
>>                                                                                 
>> struct tc_mirred {
>> 	tc_gen;
>> @@ -21,6 +25,7 @@ enum {
>> 	TCA_MIRRED_TM,
>> 	TCA_MIRRED_PARMS,
>> 	TCA_MIRRED_PAD,
>> +	TCA_MIRRED_TC_MAP,
>> 	__TCA_MIRRED_MAX
>> };
>> #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
>> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
>> index 1b5549a..f9801de 100644
>> --- a/net/sched/act_mirred.c
>> +++ b/net/sched/act_mirred.c
>> @@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
>>
>> static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
>> 	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
>> +	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
>> };
>>
>> static unsigned int mirred_net_id;
>> @@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 	struct tcf_mirred *m;
>> 	struct net_device *dev;
>> 	bool exists = false;
>> +	u8 *tc_map = NULL;
>> +	u32 flags = 0;
>> 	int ret;
>>
>> 	if (nla == NULL)
>> @@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 		return ret;
>> 	if (tb[TCA_MIRRED_PARMS] == NULL)
>> 		return -EINVAL;
>> +
>> +	if (tb[TCA_MIRRED_TC_MAP]) {
>> +		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
>> +		if (*tc_map >= MIRRED_TC_MAP_MAX)
>> +			return -EINVAL;
>> +		flags |= MIRRED_F_TC_MAP;
>> +	}
>> +
>> 	parm = nla_data(tb[TCA_MIRRED_PARMS]);
>>
>> 	exists = tcf_hash_check(tn, parm->index, a, bind);
>> @@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 	ASSERT_RTNL();
>> 	m->tcf_action = parm->action;
>> 	m->tcfm_eaction = parm->eaction;
>> +	m->flags = flags;
>> 	if (dev != NULL) {
>> 		m->tcfm_ifindex = parm->ifindex;
>> 		if (ret != ACT_P_CREATED)
>> @@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 		dev_hold(dev);
>> 		rcu_assign_pointer(m->tcfm_dev, dev);
>> 		m->tcfm_mac_header_xmit = mac_header_xmit;
>> +		if (flags & MIRRED_F_TC_MAP)
>> +			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
>> 	}
>>
>> 	if (ret == ACT_P_CREATED) {
>> @@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
>>
>> 	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
>> 		goto nla_put_failure;
>> +	if ((m->flags & MIRRED_F_TC_MAP) &&
>> +	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
>> +		goto nla_put_failure;
>>
>> 	tcf_tm_dump(&t, &m->tcf_tm);
>> 	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class
@ 2017-08-02  2:20       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02  2:20 UTC (permalink / raw)
  To: intel-wired-lan


On 8/1/2017 4:12 AM, Jiri Pirko wrote:
> Tue, Aug 01, 2017 at 02:37:37AM CEST, amritha.nambiar at intel.com wrote:
>> The Mirred/redirect action is extended to forward to a traffic
>> class on the device. The traffic class index needs to be
>> provided in addition to the device's ifindex.
>>
>> Example:
>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>  dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>  skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
> 
> You need to make sure that the current offloaders fill forbid to add
> this rule, not just silently ignore the tc value.

I will fix this in the next version, probably using the 'flags' field
I've defined.

> 
> 
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>> include/net/tc_act/tc_mirred.h        |    7 +++++++
>> include/uapi/linux/tc_act/tc_mirred.h |    5 +++++
>> net/sched/act_mirred.c                |   17 +++++++++++++++++
>> 3 files changed, 29 insertions(+)
>>
>> diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
>> index 604bc31..60058c4 100644
>> --- a/include/net/tc_act/tc_mirred.h
>> +++ b/include/net/tc_act/tc_mirred.h
>> @@ -9,6 +9,8 @@ struct tcf_mirred {
>> 	int			tcfm_eaction;
>> 	int			tcfm_ifindex;
>> 	bool			tcfm_mac_header_xmit;
>> +	u8			tcfm_tc;
>> +	u32			flags;
>> 	struct net_device __rcu	*tcfm_dev;
>> 	struct list_head	tcfm_list;
>> };
>> @@ -37,4 +39,9 @@ static inline int tcf_mirred_ifindex(const struct tc_action *a)
>> 	return to_mirred(a)->tcfm_ifindex;
>> }
>>
>> +static inline int tcf_mirred_tc(const struct tc_action *a)
>> +{
>> +	return to_mirred(a)->tcfm_tc;
>> +}
>> +
>> #endif /* __NET_TC_MIR_H */
>> diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h
>> index 3d7a2b3..8ff4d76 100644
>> --- a/include/uapi/linux/tc_act/tc_mirred.h
>> +++ b/include/uapi/linux/tc_act/tc_mirred.h
>> @@ -9,6 +9,10 @@
>> #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
>> #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
>> #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
>> +
>> +#define MIRRED_F_TC_MAP		0x1
>> +#define MIRRED_TC_MAP_MAX	0x10
>> +#define MIRRED_TC_MAP_MASK	0xF
> 
> I'm completely lost. Why do you have these values here? and in fact one
> twice?

I'll fix this to remove the defines for the TC max range and its bitmap
here and reuse the existing ones defined in linux/netdevice.h

> 
> 
>>                                                                                 
>> struct tc_mirred {
>> 	tc_gen;
>> @@ -21,6 +25,7 @@ enum {
>> 	TCA_MIRRED_TM,
>> 	TCA_MIRRED_PARMS,
>> 	TCA_MIRRED_PAD,
>> +	TCA_MIRRED_TC_MAP,
>> 	__TCA_MIRRED_MAX
>> };
>> #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
>> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
>> index 1b5549a..f9801de 100644
>> --- a/net/sched/act_mirred.c
>> +++ b/net/sched/act_mirred.c
>> @@ -67,6 +67,7 @@ static void tcf_mirred_release(struct tc_action *a, int bind)
>>
>> static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
>> 	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
>> +	[TCA_MIRRED_TC_MAP]	= { .type = NLA_U8 },
>> };
>>
>> static unsigned int mirred_net_id;
>> @@ -83,6 +84,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 	struct tcf_mirred *m;
>> 	struct net_device *dev;
>> 	bool exists = false;
>> +	u8 *tc_map = NULL;
>> +	u32 flags = 0;
>> 	int ret;
>>
>> 	if (nla == NULL)
>> @@ -92,6 +95,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 		return ret;
>> 	if (tb[TCA_MIRRED_PARMS] == NULL)
>> 		return -EINVAL;
>> +
>> +	if (tb[TCA_MIRRED_TC_MAP]) {
>> +		tc_map = nla_data(tb[TCA_MIRRED_TC_MAP]);
>> +		if (*tc_map >= MIRRED_TC_MAP_MAX)
>> +			return -EINVAL;
>> +		flags |= MIRRED_F_TC_MAP;
>> +	}
>> +
>> 	parm = nla_data(tb[TCA_MIRRED_PARMS]);
>>
>> 	exists = tcf_hash_check(tn, parm->index, a, bind);
>> @@ -139,6 +150,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 	ASSERT_RTNL();
>> 	m->tcf_action = parm->action;
>> 	m->tcfm_eaction = parm->eaction;
>> +	m->flags = flags;
>> 	if (dev != NULL) {
>> 		m->tcfm_ifindex = parm->ifindex;
>> 		if (ret != ACT_P_CREATED)
>> @@ -146,6 +158,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>> 		dev_hold(dev);
>> 		rcu_assign_pointer(m->tcfm_dev, dev);
>> 		m->tcfm_mac_header_xmit = mac_header_xmit;
>> +		if (flags & MIRRED_F_TC_MAP)
>> +			m->tcfm_tc = *tc_map & MIRRED_TC_MAP_MASK;
>> 	}
>>
>> 	if (ret == ACT_P_CREATED) {
>> @@ -259,6 +273,9 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
>>
>> 	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
>> 		goto nla_put_failure;
>> +	if ((m->flags & MIRRED_F_TC_MAP) &&
>> +	    nla_put_u8(skb, TCA_MIRRED_TC_MAP, m->tcfm_tc))
>> +		goto nla_put_failure;
>>
>> 	tcf_tm_dump(&t, &m->tcf_tm);
>> 	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
  2017-08-02  0:57     ` [Intel-wired-lan] " Nambiar, Amritha
@ 2017-08-02 12:01       ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-02 12:01 UTC (permalink / raw)
  To: Nambiar, Amritha, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz

On 17-08-01 08:57 PM, Nambiar, Amritha wrote:
> 
> On 8/1/2017 3:15 AM, Jamal Hadi Salim wrote:
>> On 17-07-31 08:36 PM, Amritha Nambiar wrote:

>>>
>>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>>     dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>>     skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>>
>>
>> I think "queue 1" sounds better than "tc 1".
>> "tc" is  already a keyword in a few places (even within that declaration
>> above).
> 
> The idea is to redirect to a traffic class that has queues assigned to
> it and not a single queue i.e. these are actually queue groups and not a
> single queue. So may be "qgroup 1" or "tcqgroup 1" fits better.
> 

Can you describe how this works? So the specific memeber of a
a tcgroups show up on a specific rx DMA ring? If you only have
16 and 512 RX DMA rings - how does that work?

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-02 12:01       ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-02 12:01 UTC (permalink / raw)
  To: intel-wired-lan

On 17-08-01 08:57 PM, Nambiar, Amritha wrote:
> 
> On 8/1/2017 3:15 AM, Jamal Hadi Salim wrote:
>> On 17-07-31 08:36 PM, Amritha Nambiar wrote:

>>>
>>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>>     dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>>     skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>>
>>
>> I think "queue 1" sounds better than "tc 1".
>> "tc" is  already a keyword in a few places (even within that declaration
>> above).
> 
> The idea is to redirect to a traffic class that has queues assigned to
> it and not a single queue i.e. these are actually queue groups and not a
> single queue. So may be "qgroup 1" or "tcqgroup 1" fits better.
> 

Can you describe how this works? So the specific memeber of a
a tcgroups show up on a specific rx DMA ring? If you only have
16 and 512 RX DMA rings - how does that work?

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-02  2:13       ` [Intel-wired-lan] " Nambiar, Amritha
@ 2017-08-02 12:02         ` Jamal Hadi Salim
  -1 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-02 12:02 UTC (permalink / raw)
  To: Nambiar, Amritha, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz

On 17-08-01 10:13 PM, Nambiar, Amritha wrote:
> 
> On 8/1/2017 3:56 AM, Jamal Hadi Salim wrote:
>> On 17-07-31 08:38 PM, Amritha Nambiar wrote:
>>> This patch enables tc-flower based hardware offloads. tc/flower
>>> filter provided by the kernel is configured as driver specific
>>> cloud filter. The patch implements functions and admin queue
>>> commands needed to support cloud filters in the driver and
>>> adds cloud filters to configure these tc-flower filters.
>>>
>>> The only action supported is to redirect packets to a traffic class
>>> on the same device.
>>>
>>> # tc qdisc add dev eth0 ingress
>>> # ethtool -K eth0 hw-tc-offload on
>>>
>>> # tc filter add dev eth0 protocol ip parent ffff:\
>>>     prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>>     action mirred ingress redirect dev eth0 tc 0
>>>
>>
>> Out of curiosity - did you need to say "indev eth0" there?
> 
> It looks like I don't need to specify "indev eth0". I will need to look
> up how this part is offloaded and probably validate in the driver when
> this is specified.
> 
>> Also: Is it possible to add an skbmark? Example something like
>> these that directs two flows to the same queue but different
>> skb marks:
>>
>> # tc filter add dev eth0 protocol ip parent ffff: \
>>     prio 2 flower dst_ip 192.168.3.5/32 \
>>     ip_proto udp dst_port 2a skip_sw \
>>     action skbedit mark 11 \
>>     action mirred ingress redirect dev eth0 tcqueue 1
>>
>> # tc filter add dev eth0 protocol ip parent ffff: \
>>       prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>>       action skbedit mark 12 \
>>       action mirred ingress redirect dev eth0 tcqueue 1
>>
> 
> It is possible to support the skbedit mark action for the first rule
> here (L3 and L4) which I can take up in a subsequent patch, but this
> cannot be supported on our device for L2 based match in the second rule.
> 

Ok, thanks. So the issue is one of hardware limitation?

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-02 12:02         ` Jamal Hadi Salim
  0 siblings, 0 replies; 58+ messages in thread
From: Jamal Hadi Salim @ 2017-08-02 12:02 UTC (permalink / raw)
  To: intel-wired-lan

On 17-08-01 10:13 PM, Nambiar, Amritha wrote:
> 
> On 8/1/2017 3:56 AM, Jamal Hadi Salim wrote:
>> On 17-07-31 08:38 PM, Amritha Nambiar wrote:
>>> This patch enables tc-flower based hardware offloads. tc/flower
>>> filter provided by the kernel is configured as driver specific
>>> cloud filter. The patch implements functions and admin queue
>>> commands needed to support cloud filters in the driver and
>>> adds cloud filters to configure these tc-flower filters.
>>>
>>> The only action supported is to redirect packets to a traffic class
>>> on the same device.
>>>
>>> # tc qdisc add dev eth0 ingress
>>> # ethtool -K eth0 hw-tc-offload on
>>>
>>> # tc filter add dev eth0 protocol ip parent ffff:\
>>>     prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>>     action mirred ingress redirect dev eth0 tc 0
>>>
>>
>> Out of curiosity - did you need to say "indev eth0" there?
> 
> It looks like I don't need to specify "indev eth0". I will need to look
> up how this part is offloaded and probably validate in the driver when
> this is specified.
> 
>> Also: Is it possible to add an skbmark? Example something like
>> these that directs two flows to the same queue but different
>> skb marks:
>>
>> # tc filter add dev eth0 protocol ip parent ffff: \
>>     prio 2 flower dst_ip 192.168.3.5/32 \
>>     ip_proto udp dst_port 2a skip_sw \
>>     action skbedit mark 11 \
>>     action mirred ingress redirect dev eth0 tcqueue 1
>>
>> # tc filter add dev eth0 protocol ip parent ffff: \
>>       prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>>       action skbedit mark 12 \
>>       action mirred ingress redirect dev eth0 tcqueue 1
>>
> 
> It is possible to support the skbedit mark action for the first rule
> here (L3 and L4) which I can take up in a subsequent patch, but this
> cannot be supported on our device for L2 based match in the second rule.
> 

Ok, thanks. So the issue is one of hardware limitation?

cheers,
jamal

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
  2017-08-02 12:01       ` [Intel-wired-lan] " Jamal Hadi Salim
@ 2017-08-02 18:17         ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02 18:17 UTC (permalink / raw)
  To: Jamal Hadi Salim, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz


On 8/2/2017 5:01 AM, Jamal Hadi Salim wrote:
> On 17-08-01 08:57 PM, Nambiar, Amritha wrote:
>>
>> On 8/1/2017 3:15 AM, Jamal Hadi Salim wrote:
>>> On 17-07-31 08:36 PM, Amritha Nambiar wrote:
> 
>>>>
>>>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>>>     dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>>>     skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>>>
>>>
>>> I think "queue 1" sounds better than "tc 1".
>>> "tc" is  already a keyword in a few places (even within that declaration
>>> above).
>>
>> The idea is to redirect to a traffic class that has queues assigned to
>> it and not a single queue i.e. these are actually queue groups and not a
>> single queue. So may be "qgroup 1" or "tcqgroup 1" fits better.
>>
> 
> Can you describe how this works? So the specific memeber of a
> a tcgroups show up on a specific rx DMA ring? If you only have
> 16 and 512 RX DMA rings - how does that work?
>

The Rx rule here is to redirect packets a specific traffic class. It is
the traffic class index (queue group index) that is offloaded to the
device. Queues were already configured for the traffic class by mapping
the queue counts and offsets and offloading this layout using the mqprio
framework. I had submitted a patch series for this which uses a new
hardware offload mode in mqprio to offload the TCs, the queue
configurations and the bandwidth rates for the TCs. So the 512 rings can
be mapped into 16 TCs using the mqprio offload mechanism, something like
this:
TC0 : 0 – 15
TC1: 16 – 31
TC2: 32 – 33
TC3: 34 – 49
.
.
.
TC15: 500 - 511

Now, once the TC configuration is prepared, it is just a matter of
hooking up the Rx rules to route traffic to the traffic class/queue
group. Rx queue selection within the queue group happens based on RSS.

> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier
@ 2017-08-02 18:17         ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02 18:17 UTC (permalink / raw)
  To: intel-wired-lan


On 8/2/2017 5:01 AM, Jamal Hadi Salim wrote:
> On 17-08-01 08:57 PM, Nambiar, Amritha wrote:
>>
>> On 8/1/2017 3:15 AM, Jamal Hadi Salim wrote:
>>> On 17-07-31 08:36 PM, Amritha Nambiar wrote:
> 
>>>>
>>>> # tc filter add dev eth0 protocol ip parent ffff: prio 1 flower\
>>>>     dst_ip 192.168.1.1/32 ip_proto udp dst_port 22\
>>>>     skip_sw indev eth0 action mirred ingress redirect dev eth0 tc 1
>>>>
>>>
>>> I think "queue 1" sounds better than "tc 1".
>>> "tc" is  already a keyword in a few places (even within that declaration
>>> above).
>>
>> The idea is to redirect to a traffic class that has queues assigned to
>> it and not a single queue i.e. these are actually queue groups and not a
>> single queue. So may be "qgroup 1" or "tcqgroup 1" fits better.
>>
> 
> Can you describe how this works? So the specific memeber of a
> a tcgroups show up on a specific rx DMA ring? If you only have
> 16 and 512 RX DMA rings - how does that work?
>

The Rx rule here is to redirect packets a specific traffic class. It is
the traffic class index (queue group index) that is offloaded to the
device. Queues were already configured for the traffic class by mapping
the queue counts and offsets and offloading this layout using the mqprio
framework. I had submitted a patch series for this which uses a new
hardware offload mode in mqprio to offload the TCs, the queue
configurations and the bandwidth rates for the TCs. So the 512 rings can
be mapped into 16 TCs using the mqprio offload mechanism, something like
this:
TC0 : 0 ? 15
TC1: 16 ? 31
TC2: 32 ? 33
TC3: 34 ? 49
.
.
.
TC15: 500 - 511

Now, once the TC configuration is prepared, it is just a matter of
hooking up the Rx rules to route traffic to the traffic class/queue
group. Rx queue selection within the queue group happens based on RSS.

> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-02 12:02         ` [Intel-wired-lan] " Jamal Hadi Salim
@ 2017-08-02 18:20           ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02 18:20 UTC (permalink / raw)
  To: Jamal Hadi Salim, intel-wired-lan, jeffrey.t.kirsher
  Cc: alexander.h.duyck, kiran.patil, netdev, mitch.a.williams,
	alexander.duyck, neerav.parikh, sridhar.samudrala,
	carolyn.wyborny, Or Gerlitz

On 8/2/2017 5:02 AM, Jamal Hadi Salim wrote:
> On 17-08-01 10:13 PM, Nambiar, Amritha wrote:
>>
>> On 8/1/2017 3:56 AM, Jamal Hadi Salim wrote:
>>> On 17-07-31 08:38 PM, Amritha Nambiar wrote:
>>>> This patch enables tc-flower based hardware offloads. tc/flower
>>>> filter provided by the kernel is configured as driver specific
>>>> cloud filter. The patch implements functions and admin queue
>>>> commands needed to support cloud filters in the driver and
>>>> adds cloud filters to configure these tc-flower filters.
>>>>
>>>> The only action supported is to redirect packets to a traffic class
>>>> on the same device.
>>>>
>>>> # tc qdisc add dev eth0 ingress
>>>> # ethtool -K eth0 hw-tc-offload on
>>>>
>>>> # tc filter add dev eth0 protocol ip parent ffff:\
>>>>     prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>>>     action mirred ingress redirect dev eth0 tc 0
>>>>
>>>
>>> Out of curiosity - did you need to say "indev eth0" there?
>>
>> It looks like I don't need to specify "indev eth0". I will need to look
>> up how this part is offloaded and probably validate in the driver when
>> this is specified.
>>
>>> Also: Is it possible to add an skbmark? Example something like
>>> these that directs two flows to the same queue but different
>>> skb marks:
>>>
>>> # tc filter add dev eth0 protocol ip parent ffff: \
>>>     prio 2 flower dst_ip 192.168.3.5/32 \
>>>     ip_proto udp dst_port 2a skip_sw \
>>>     action skbedit mark 11 \
>>>     action mirred ingress redirect dev eth0 tcqueue 1
>>>
>>> # tc filter add dev eth0 protocol ip parent ffff: \
>>>       prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>>>       action skbedit mark 12 \
>>>       action mirred ingress redirect dev eth0 tcqueue 1
>>>
>>
>> It is possible to support the skbedit mark action for the first rule
>> here (L3 and L4) which I can take up in a subsequent patch, but this
>> cannot be supported on our device for L2 based match in the second rule.
>>
> 
> Ok, thanks. So the issue is one of hardware limitation?
> 

Right. Our hardware does not have this support now.

> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-02 18:20           ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-02 18:20 UTC (permalink / raw)
  To: intel-wired-lan

On 8/2/2017 5:02 AM, Jamal Hadi Salim wrote:
> On 17-08-01 10:13 PM, Nambiar, Amritha wrote:
>>
>> On 8/1/2017 3:56 AM, Jamal Hadi Salim wrote:
>>> On 17-07-31 08:38 PM, Amritha Nambiar wrote:
>>>> This patch enables tc-flower based hardware offloads. tc/flower
>>>> filter provided by the kernel is configured as driver specific
>>>> cloud filter. The patch implements functions and admin queue
>>>> commands needed to support cloud filters in the driver and
>>>> adds cloud filters to configure these tc-flower filters.
>>>>
>>>> The only action supported is to redirect packets to a traffic class
>>>> on the same device.
>>>>
>>>> # tc qdisc add dev eth0 ingress
>>>> # ethtool -K eth0 hw-tc-offload on
>>>>
>>>> # tc filter add dev eth0 protocol ip parent ffff:\
>>>>     prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>>>     action mirred ingress redirect dev eth0 tc 0
>>>>
>>>
>>> Out of curiosity - did you need to say "indev eth0" there?
>>
>> It looks like I don't need to specify "indev eth0". I will need to look
>> up how this part is offloaded and probably validate in the driver when
>> this is specified.
>>
>>> Also: Is it possible to add an skbmark? Example something like
>>> these that directs two flows to the same queue but different
>>> skb marks:
>>>
>>> # tc filter add dev eth0 protocol ip parent ffff: \
>>>     prio 2 flower dst_ip 192.168.3.5/32 \
>>>     ip_proto udp dst_port 2a skip_sw \
>>>     action skbedit mark 11 \
>>>     action mirred ingress redirect dev eth0 tcqueue 1
>>>
>>> # tc filter add dev eth0 protocol ip parent ffff: \
>>>       prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>>>       action skbedit mark 12 \
>>>       action mirred ingress redirect dev eth0 tcqueue 1
>>>
>>
>> It is possible to support the skbedit mark action for the first rule
>> here (L3 and L4) which I can take up in a subsequent patch, but this
>> cannot be supported on our device for L2 based match in the second rule.
>>
> 
> Ok, thanks. So the issue is one of hardware limitation?
> 

Right. Our hardware does not have this support now.

> cheers,
> jamal
> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters
  2017-08-01 19:16     ` Shannon Nelson
@ 2017-08-14 18:59       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-14 18:59 UTC (permalink / raw)
  To: Shannon Nelson, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

On 8/1/2017 12:16 PM, Shannon Nelson wrote:
> On 7/31/2017 5:37 PM, Amritha Nambiar wrote:
>> Add new admin queue definitions and extended fields for cloud
>> filter support. Define big buffer for extended general fields
>> in Add/Remove Cloud filters command.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
>> Signed-off-by: Store Laura <laura.stroe@intel.com>
>> Signed-off-by: Iremonger Bernard <bernard.iremonger@intel.com>
>> Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
>> ---
>>   drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   98 +++++++++++++++++++++
>>   1 file changed, 97 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> index 8bba04c..9f14305 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> 
> I see that you're changing the i40e version of this, but not the i40evf 
> version.  I understand that these changes are not useful for the VF, but 
> are you no longer trying to keep the AdminQ definitions consistent 
> between the two?

I will add these definitions to the VF as well for consistency in the
next version.

> 
>> @@ -1358,7 +1358,9 @@ struct i40e_aqc_add_remove_cloud_filters {
>>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT	0
>>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK	(0x3FF << \
>>   					I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT)
>> -	u8	reserved2[4];
>> +	u8	big_buffer_flag;
>> +#define	I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER	1
>> +	u8	reserved2[3];
>>   	__le32	addr_high;
>>   	__le32	addr_low;
>>   };
>> @@ -1395,6 +1397,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>>   #define I40E_AQC_ADD_CLOUD_FILTER_IMAC			0x000A
>>   #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC	0x000B
>>   #define I40E_AQC_ADD_CLOUD_FILTER_IIP			0x000C
>> +/* 0x0010 to 0x0017 is for custom filters */
>> +/* flag to be used when adding cloud filter: IP + L4 Port */
>> +#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT		0x0010
>> +/* flag to be used when adding cloud filter: Dest MAC + L4 Port */
>> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT		0x0011
>> +/* flag to be used when adding cloud filter: Dest MAC + VLAN + L4 Port */
>> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT		0x0012
>>   
>>   #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE		0x0080
>>   #define I40E_AQC_ADD_CLOUD_VNK_SHIFT			6
>> @@ -1429,6 +1438,45 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>>   	u8	response_reserved[7];
>>   };
> 
> I know you didn't add this struct, but where's the I40E_CHECK_STRUCT_LEN 
> check?

Will add all the needed I40E_CHECK_STRUCT_LEN check in the next version
of the series.

> 
>>   
>> +/* i40e_aqc_add_remove_cloud_filters_element_big_data is used when
>> + * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set.
>> + */
>> +struct i40e_aqc_add_remove_cloud_filters_element_big_data {
>> +	struct i40e_aqc_add_remove_cloud_filters_element_data element;
>> +	u16     general_fields[32];
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0	0
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1	1
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2	2
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0	3
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1	4
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2	5
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0	6
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1	7
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2	8
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0	9
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1	10
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2	11
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0	12
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1	13
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2	14
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0	15
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1	16
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2	17
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3	18
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4	19
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5	20
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6	21
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7	22
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0	23
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1	24
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2	25
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3	26
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4	27
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5	28
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6	29
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7	30
>> +};
> 
> Needs the I40E_CHECK_STRUCT_LEN to be consistent with the data checking 
> in the rest of this file.
> 
>> +
>>   struct i40e_aqc_remove_cloud_filters_completion {
>>   	__le16 perfect_ovlan_used;
>>   	__le16 perfect_ovlan_free;
>> @@ -1440,6 +1488,54 @@ struct i40e_aqc_remove_cloud_filters_completion {
>>   
>>   I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion);
>>   
>> +/* Replace filter Command 0x025F
>> + * uses the i40e_aqc_replace_cloud_filters,
>> + * and the generic indirect completion structure
>> + */
>> +struct i40e_filter_data {
>> +	u8 filter_type;
>> +	u8 input[3];
>> +};
> 
> Should have I40E_CHECK_STRUCT_LEN check
> 
>> +
>> +struct i40e_aqc_replace_cloud_filters_cmd {
>> +	u8      valid_flags;
>> +#define I40E_AQC_REPLACE_L1_FILTER		0x0
>> +#define I40E_AQC_REPLACE_CLOUD_FILTER		0x1
>> +#define I40E_AQC_GET_CLOUD_FILTERS		0x2
>> +#define I40E_AQC_MIRROR_CLOUD_FILTER		0x4
>> +#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER	0x8
>> +	u8      old_filter_type;
>> +	u8      new_filter_type;
>> +	u8      tr_bit;
>> +	u8      reserved[4];
>> +	__le32 addr_high;
>> +	__le32 addr_low;
>> +};
> 
> Should have I40E_CHECK_CMD_LENGTH check
> 
>> +
>> +struct i40e_aqc_replace_cloud_filters_cmd_buf {
>> +	u8      data[32];
>> +/* Filter type INPUT codes*/
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX	3
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED	BIT(7)
>> +
>> +/* Field Vector offsets */
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA	0
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH	6
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG	7
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN	8
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN	9
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN	10
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY	11
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC	12
>> +/* big FLU */
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA	14
>> +/* big FLU */
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA	15
>> +
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN	37
>> +	struct i40e_filter_data filters[8];
>> +};
> 
> Should have I40E_CHECK_STRUCT_LEN check
> 
>> +
>>   /* Add Mirror Rule (indirect or direct 0x0260)
>>    * Delete Mirror Rule (indirect or direct 0x0261)
>>    * note: some rule types (4,5) do not use an external buffer.
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan@osuosl.org
>> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters
@ 2017-08-14 18:59       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-14 18:59 UTC (permalink / raw)
  To: intel-wired-lan

On 8/1/2017 12:16 PM, Shannon Nelson wrote:
> On 7/31/2017 5:37 PM, Amritha Nambiar wrote:
>> Add new admin queue definitions and extended fields for cloud
>> filter support. Define big buffer for extended general fields
>> in Add/Remove Cloud filters command.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
>> Signed-off-by: Store Laura <laura.stroe@intel.com>
>> Signed-off-by: Iremonger Bernard <bernard.iremonger@intel.com>
>> Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
>> ---
>>   drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h |   98 +++++++++++++++++++++
>>   1 file changed, 97 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> index 8bba04c..9f14305 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> 
> I see that you're changing the i40e version of this, but not the i40evf 
> version.  I understand that these changes are not useful for the VF, but 
> are you no longer trying to keep the AdminQ definitions consistent 
> between the two?

I will add these definitions to the VF as well for consistency in the
next version.

> 
>> @@ -1358,7 +1358,9 @@ struct i40e_aqc_add_remove_cloud_filters {
>>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT	0
>>   #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK	(0x3FF << \
>>   					I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT)
>> -	u8	reserved2[4];
>> +	u8	big_buffer_flag;
>> +#define	I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER	1
>> +	u8	reserved2[3];
>>   	__le32	addr_high;
>>   	__le32	addr_low;
>>   };
>> @@ -1395,6 +1397,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>>   #define I40E_AQC_ADD_CLOUD_FILTER_IMAC			0x000A
>>   #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC	0x000B
>>   #define I40E_AQC_ADD_CLOUD_FILTER_IIP			0x000C
>> +/* 0x0010 to 0x0017 is for custom filters */
>> +/* flag to be used when adding cloud filter: IP + L4 Port */
>> +#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT		0x0010
>> +/* flag to be used when adding cloud filter: Dest MAC + L4 Port */
>> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT		0x0011
>> +/* flag to be used when adding cloud filter: Dest MAC + VLAN + L4 Port */
>> +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT		0x0012
>>   
>>   #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE		0x0080
>>   #define I40E_AQC_ADD_CLOUD_VNK_SHIFT			6
>> @@ -1429,6 +1438,45 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
>>   	u8	response_reserved[7];
>>   };
> 
> I know you didn't add this struct, but where's the I40E_CHECK_STRUCT_LEN 
> check?

Will add all the needed I40E_CHECK_STRUCT_LEN check in the next version
of the series.

> 
>>   
>> +/* i40e_aqc_add_remove_cloud_filters_element_big_data is used when
>> + * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set.
>> + */
>> +struct i40e_aqc_add_remove_cloud_filters_element_big_data {
>> +	struct i40e_aqc_add_remove_cloud_filters_element_data element;
>> +	u16     general_fields[32];
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0	0
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1	1
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2	2
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0	3
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1	4
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2	5
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0	6
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1	7
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2	8
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0	9
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1	10
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2	11
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0	12
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1	13
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2	14
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0	15
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1	16
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2	17
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3	18
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4	19
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5	20
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6	21
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7	22
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0	23
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1	24
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2	25
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3	26
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4	27
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5	28
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6	29
>> +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7	30
>> +};
> 
> Needs the I40E_CHECK_STRUCT_LEN to be consistent with the data checking 
> in the rest of this file.
> 
>> +
>>   struct i40e_aqc_remove_cloud_filters_completion {
>>   	__le16 perfect_ovlan_used;
>>   	__le16 perfect_ovlan_free;
>> @@ -1440,6 +1488,54 @@ struct i40e_aqc_remove_cloud_filters_completion {
>>   
>>   I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion);
>>   
>> +/* Replace filter Command 0x025F
>> + * uses the i40e_aqc_replace_cloud_filters,
>> + * and the generic indirect completion structure
>> + */
>> +struct i40e_filter_data {
>> +	u8 filter_type;
>> +	u8 input[3];
>> +};
> 
> Should have I40E_CHECK_STRUCT_LEN check
> 
>> +
>> +struct i40e_aqc_replace_cloud_filters_cmd {
>> +	u8      valid_flags;
>> +#define I40E_AQC_REPLACE_L1_FILTER		0x0
>> +#define I40E_AQC_REPLACE_CLOUD_FILTER		0x1
>> +#define I40E_AQC_GET_CLOUD_FILTERS		0x2
>> +#define I40E_AQC_MIRROR_CLOUD_FILTER		0x4
>> +#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER	0x8
>> +	u8      old_filter_type;
>> +	u8      new_filter_type;
>> +	u8      tr_bit;
>> +	u8      reserved[4];
>> +	__le32 addr_high;
>> +	__le32 addr_low;
>> +};
> 
> Should have I40E_CHECK_CMD_LENGTH check
> 
>> +
>> +struct i40e_aqc_replace_cloud_filters_cmd_buf {
>> +	u8      data[32];
>> +/* Filter type INPUT codes*/
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX	3
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED	BIT(7)
>> +
>> +/* Field Vector offsets */
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA	0
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH	6
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG	7
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN	8
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN	9
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN	10
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY	11
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC	12
>> +/* big FLU */
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA	14
>> +/* big FLU */
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA	15
>> +
>> +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN	37
>> +	struct i40e_filter_data filters[8];
>> +};
> 
> Should have I40E_CHECK_STRUCT_LEN check
> 
>> +
>>   /* Add Mirror Rule (indirect or direct 0x0260)
>>    * Delete Mirror Rule (indirect or direct 0x0261)
>>    * note: some rule types (4,5) do not use an external buffer.
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan at osuosl.org
>> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH 5/6] [net-next]net: i40e: Clean up of cloud filters
  2017-08-01 19:16     ` Shannon Nelson
@ 2017-08-14 19:06       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-14 19:06 UTC (permalink / raw)
  To: Shannon Nelson, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

On 8/1/2017 12:16 PM, Shannon Nelson wrote:
> On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
>> Introduce the cloud filter datastructure and cleanup of cloud
>> filters associated with the device.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   drivers/net/ethernet/intel/i40e/i40e.h      |   11 +++++++++++
>>   drivers/net/ethernet/intel/i40e/i40e_main.c |   27 +++++++++++++++++++++++++++
>>   2 files changed, 38 insertions(+)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
>> index 1391e5d..5c0cad5 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
>> @@ -252,6 +252,14 @@ struct i40e_fdir_filter {
>>   	u32 fd_id;
>>   };
>>   
>> +struct i40e_cloud_filter {
>> +	struct hlist_node cloud_node;
>> +	/* cloud filter input set follows */
>> +	unsigned long cookie;
>> +	/* filter control */
>> +	u16 seid;
>> +};
> 
> This would be cleaner and more readable with the field comments off to 
> the side rather than in line with the fields.

Will fix in the next version of the series.

> 
>> +
>>   #define I40E_ETH_P_LLDP			0x88cc
>>   
>>   #define I40E_DCB_PRIO_TYPE_STRICT	0
>> @@ -419,6 +427,9 @@ struct i40e_pf {
>>   	struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
>>   	u16 pending_udp_bitmap;
>>   
>> +	struct hlist_head cloud_filter_list;
>> +	u16 num_cloud_filters;
>> +
>>   	enum i40e_interrupt_policy int_policy;
>>   	u16 rx_itr_default;
>>   	u16 tx_itr_default;
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> index fdddd74..93f6fe2 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> @@ -6928,6 +6928,29 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
>>   }
>>   
>>   /**
>> + * i40e_cloud_filter_exit - Cleans up the Cloud Filters
>> + * @pf: Pointer to PF
>> + *
>> + * This function destroys the hlist where all the Cloud Filters
>> + * filters were saved.
>> + **/
>> +static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>> +{
>> +	struct i40e_cloud_filter *cfilter;
>> +	struct hlist_node *node;
>> +
>> +	if (hlist_empty(&pf->cloud_filter_list))
>> +		return;
> 
> Is this check really necessary?  Doesn't hlist_for_each_entry_safe() 
> check for this?

That's right. Will fix in the next version of the series.

> 
>> +
>> +	hlist_for_each_entry_safe(cfilter, node,
>> +				  &pf->cloud_filter_list, cloud_node) {
>> +		hlist_del(&cfilter->cloud_node);
>> +		kfree(cfilter);
>> +	}
>> +	pf->num_cloud_filters = 0;
>> +}
>> +
>> +/**
>>    * i40e_close - Disables a network interface
>>    * @netdev: network interface device structure
>>    *
>> @@ -12137,6 +12160,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>>   			vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]);
>>   		if (!vsi) {
>>   			dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n");
>> +			i40e_cloud_filter_exit(pf);
>>   			i40e_fdir_teardown(pf);
>>   			return -EAGAIN;
>>   		}
>> @@ -12961,6 +12985,8 @@ static void i40e_remove(struct pci_dev *pdev)
>>   	if (pf->vsi[pf->lan_vsi])
>>   		i40e_vsi_release(pf->vsi[pf->lan_vsi]);
>>   
>> +	i40e_cloud_filter_exit(pf);
>> +
>>   	/* remove attached clients */
>>   	if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
>>   		ret_code = i40e_lan_del_device(pf);
>> @@ -13170,6 +13196,7 @@ static void i40e_shutdown(struct pci_dev *pdev)
>>   
>>   	del_timer_sync(&pf->service_timer);
>>   	cancel_work_sync(&pf->service_task);
>> +	i40e_cloud_filter_exit(pf);
>>   	i40e_fdir_teardown(pf);
>>   
>>   	/* Client close must be called explicitly here because the timer
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan@osuosl.org
>> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 5/6] [net-next]net: i40e: Clean up of cloud filters
@ 2017-08-14 19:06       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-14 19:06 UTC (permalink / raw)
  To: intel-wired-lan

On 8/1/2017 12:16 PM, Shannon Nelson wrote:
> On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
>> Introduce the cloud filter datastructure and cleanup of cloud
>> filters associated with the device.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   drivers/net/ethernet/intel/i40e/i40e.h      |   11 +++++++++++
>>   drivers/net/ethernet/intel/i40e/i40e_main.c |   27 +++++++++++++++++++++++++++
>>   2 files changed, 38 insertions(+)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
>> index 1391e5d..5c0cad5 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
>> @@ -252,6 +252,14 @@ struct i40e_fdir_filter {
>>   	u32 fd_id;
>>   };
>>   
>> +struct i40e_cloud_filter {
>> +	struct hlist_node cloud_node;
>> +	/* cloud filter input set follows */
>> +	unsigned long cookie;
>> +	/* filter control */
>> +	u16 seid;
>> +};
> 
> This would be cleaner and more readable with the field comments off to 
> the side rather than in line with the fields.

Will fix in the next version of the series.

> 
>> +
>>   #define I40E_ETH_P_LLDP			0x88cc
>>   
>>   #define I40E_DCB_PRIO_TYPE_STRICT	0
>> @@ -419,6 +427,9 @@ struct i40e_pf {
>>   	struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
>>   	u16 pending_udp_bitmap;
>>   
>> +	struct hlist_head cloud_filter_list;
>> +	u16 num_cloud_filters;
>> +
>>   	enum i40e_interrupt_policy int_policy;
>>   	u16 rx_itr_default;
>>   	u16 tx_itr_default;
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> index fdddd74..93f6fe2 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> @@ -6928,6 +6928,29 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf)
>>   }
>>   
>>   /**
>> + * i40e_cloud_filter_exit - Cleans up the Cloud Filters
>> + * @pf: Pointer to PF
>> + *
>> + * This function destroys the hlist where all the Cloud Filters
>> + * filters were saved.
>> + **/
>> +static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>> +{
>> +	struct i40e_cloud_filter *cfilter;
>> +	struct hlist_node *node;
>> +
>> +	if (hlist_empty(&pf->cloud_filter_list))
>> +		return;
> 
> Is this check really necessary?  Doesn't hlist_for_each_entry_safe() 
> check for this?

That's right. Will fix in the next version of the series.

> 
>> +
>> +	hlist_for_each_entry_safe(cfilter, node,
>> +				  &pf->cloud_filter_list, cloud_node) {
>> +		hlist_del(&cfilter->cloud_node);
>> +		kfree(cfilter);
>> +	}
>> +	pf->num_cloud_filters = 0;
>> +}
>> +
>> +/**
>>    * i40e_close - Disables a network interface
>>    * @netdev: network interface device structure
>>    *
>> @@ -12137,6 +12160,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>>   			vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]);
>>   		if (!vsi) {
>>   			dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n");
>> +			i40e_cloud_filter_exit(pf);
>>   			i40e_fdir_teardown(pf);
>>   			return -EAGAIN;
>>   		}
>> @@ -12961,6 +12985,8 @@ static void i40e_remove(struct pci_dev *pdev)
>>   	if (pf->vsi[pf->lan_vsi])
>>   		i40e_vsi_release(pf->vsi[pf->lan_vsi]);
>>   
>> +	i40e_cloud_filter_exit(pf);
>> +
>>   	/* remove attached clients */
>>   	if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
>>   		ret_code = i40e_lan_del_device(pf);
>> @@ -13170,6 +13196,7 @@ static void i40e_shutdown(struct pci_dev *pdev)
>>   
>>   	del_timer_sync(&pf->service_timer);
>>   	cancel_work_sync(&pf->service_task);
>> +	i40e_cloud_filter_exit(pf);
>>   	i40e_fdir_teardown(pf);
>>   
>>   	/* Client close must be called explicitly here because the timer
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan at osuosl.org
>> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
  2017-08-01 19:16     ` Shannon Nelson
@ 2017-08-14 19:21       ` Nambiar, Amritha
  -1 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-14 19:21 UTC (permalink / raw)
  To: Shannon Nelson, intel-wired-lan, jeffrey.t.kirsher
  Cc: netdev, mitch.a.williams

On 8/1/2017 12:16 PM, Shannon Nelson wrote:
> On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
>> This patch enables tc-flower based hardware offloads. tc/flower
>> filter provided by the kernel is configured as driver specific
>> cloud filter. The patch implements functions and admin queue
>> commands needed to support cloud filters in the driver and
>> adds cloud filters to configure these tc-flower filters.
>>
>> The only action supported is to redirect packets to a traffic class
>> on the same device.
>>
>> # tc qdisc add dev eth0 ingress
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 0
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>    prio 2 flower dst_ip 192.168.3.5/32\
>>    ip_proto udp dst_port 25 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 1
>>
>> # tc filter add dev eth0 protocol ipv6 parent ffff:\
>>    prio 3 flower dst_ip fe8::200:1\
>>    ip_proto udp dst_port 66 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 2
>>
>> Delete tc flower filter:
>> Example:
>>
>> # tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
>> # tc filter del dev eth0 parent ffff:
>>
>> Flow Director Sideband is disabled while configuring cloud filters
>> via tc-flower.
> 
> Only while configuring, or the whole time there is a cloud filter?  This 
> is unclear here.

The entire time cloud filters exists. Will make the comment clearer in v2.

> 
>>
>> Unsupported matches when cloud filters are added using enhanced
>> big buffer cloud filter mode of underlying switch include:
>> 1. source port and source IP
>> 2. Combined MAC address and IP fields.
>> 3. Not specfying L4 port
> 
> s/specfying/specifying/

Will fix in v2.

> 
>>
>> These filter matches can however be used to redirect traffic to
>> the main VSI (tc 0) which does not require the enhanced big buffer
>> cloud filter support.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
>> ---
>>   drivers/net/ethernet/intel/i40e/i40e.h           |   46 +
>>   drivers/net/ethernet/intel/i40e/i40e_common.c    |  180 ++++
>>   drivers/net/ethernet/intel/i40e/i40e_main.c      |  952 ++++++++++++++++++++++
>>   drivers/net/ethernet/intel/i40e/i40e_prototype.h |   17
>>   4 files changed, 1193 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
>> index 5c0cad5..7288265 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
>> @@ -55,6 +55,8 @@
>>   #include <linux/net_tstamp.h>
>>   #include <linux/ptp_clock_kernel.h>
>>   #include <net/pkt_cls.h>
>> +#include <net/tc_act/tc_gact.h>
>> +#include <net/tc_act/tc_mirred.h>
>>   #include "i40e_type.h"
>>   #include "i40e_prototype.h"
>>   #include "i40e_client.h"
>> @@ -252,10 +254,51 @@ struct i40e_fdir_filter {
>>   	u32 fd_id;
>>   };
>>   
>> +#define I40E_CLOUD_FIELD_OMAC	0x01
>> +#define I40E_CLOUD_FIELD_IMAC	0x02
>> +#define I40E_CLOUD_FIELD_IVLAN	0x04
>> +#define I40E_CLOUD_FIELD_TEN_ID	0x08
>> +#define I40E_CLOUD_FIELD_IIP	0x10
>> +
>> +#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
>> +						 I40E_CLOUD_FIELD_IVLAN)
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
>> +						 I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
>> +						  I40E_CLOUD_FIELD_IMAC | \
>> +						  I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
>> +						   I40E_CLOUD_FIELD_IVLAN | \
>> +						   I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
>> +
>>   struct i40e_cloud_filter {
>>   	struct hlist_node cloud_node;
>>   	/* cloud filter input set follows */
>>   	unsigned long cookie;
>> +	u8 dst_mac[ETH_ALEN];
>> +	u8 src_mac[ETH_ALEN];
>> +	__be16 vlan_id;
>> +	__be32 dst_ip[4];
>> +	__be32 src_ip[4];
>> +	u8 dst_ipv6[16];
>> +	u8 src_ipv6[16];
>> +	__be16 dst_port;
>> +	__be16 src_port;
>> +	/* matter only when IP based filtering is set */
>> +	bool is_ipv6;
>> +	/* IPPROTO value */
>> +	u8 ip_proto;
>> +	/* L4 port type: src or destination port */
>> +#define I40E_CLOUD_FILTER_PORT_SRC	0x01
>> +#define I40E_CLOUD_FILTER_PORT_DEST	0x02
>> +	u8 port_type;
>> +	u32 tenant_id;
>> +	u8 flags;
>> +#define I40E_CLOUD_TNL_TYPE_NONE	0xff
>> +	u8 tunnel_type;
>>   	/* filter control */
>>   	u16 seid;
>>   };
>> @@ -574,6 +617,9 @@ struct i40e_pf {
>>   	u16 phy_led_val;
>>   
>>   	u16 override_q_count;
>> +	u16 last_sw_conf_flags;
>> +	u16 last_sw_conf_valid_flags;
>> +
> 
> Unnecessary blank line
> 
>>   };
>>   
>>   /**
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
>> index d0e8138..bfbe304 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_common.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
>> @@ -5269,5 +5269,185 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
>>   
>>   	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
>>   				   track_id, &offset, &info, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_add_cloud_filters
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to add cloud filters from
>> + * @filters: Buffer which contains the filters to be added
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Set the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
>> +		u16 seid,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	enum i40e_status_code status;
>> +	u16 buff_len;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_add_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_add_cloud_filters_big_buffer
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to add cloud filters from
>> + * @filters: Buffer which contains the filters in big buffer to be added
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Set the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
>> +	u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	i40e_status status;
>> +	u16 buff_len;
>> +	int i;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_add_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
>> +
>> +	for (i = 0; i < filter_count; i++) {
>> +		u16 tnl_type;
>> +		u32 ti;
>> +
>> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
>> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
>> +			ti = le32_to_cpu(filters[i].element.tenant_id);
>> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
>> +		}
> 
> You might want to add a comment to explain this little hack.  I believe 
> this is for a bit of firmware weirdness?

Yes, this is to handle a firmware behavior specific to Geneve filters.
Will add the comment in v2.

> 
>> +	}
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_remove_cloud_filters
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to remove cloud filters from
>> + * @filters: Buffer which contains the filters to be removed
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Remove the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
>> +		u16 seid,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	enum i40e_status_code status;
>> +	u16 buff_len;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_remove_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_remove_cloud_filters_big_buffer
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to remove cloud filters from
>> + * @filters: Buffer which contains the filters in big buffer to be removed
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Remove the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
>> +	struct i40e_hw *hw,
>> +	u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	i40e_status status;
>> +	u16 buff_len;
>> +	int i;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_remove_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
>> +
>> +	for (i = 0; i < filter_count; i++) {
>> +		u16 tnl_type;
>> +		u32 ti;
>> +
>> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
>> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
>> +			ti = le32_to_cpu(filters[i].element.tenant_id);
>> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
>> +		}
> 
> Same comment
> 
>> +	}
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>>   	return status;
>>   }
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> index 93f6fe2..65b284e 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> @@ -69,6 +69,12 @@ static int i40e_reset(struct i40e_pf *pf);
>>   static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
>>   static void i40e_fdir_sb_setup(struct i40e_pf *pf);
>>   static int i40e_veb_get_bw_info(struct i40e_veb *veb);
>> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
>> +				     struct i40e_cloud_filter *filter,
>> +				     bool add);
>> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
>> +					     struct i40e_cloud_filter *filter,
>> +					     bool add);
>>   
>>   /* i40e_pci_tbl - PCI Device ID Table
>>    *
>> @@ -5482,7 +5488,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
>>    **/
>>   static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
> 
> Hmmm... I'm looking at net-next code and I don't see this function, or 
> anything else with "i40e_channel" or "queue_channel".  What are you 
> applying this patch to?  Am I missing something?
> 
>>   {
>> +	enum i40e_admin_queue_err last_aq_status;
>> +	struct i40e_cloud_filter *cfilter;
>>   	struct i40e_channel *ch, *ch_tmp;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct hlist_node *node;
>>   	int ret, i;
>>   
>>   	/* Reset rss size that was stored when reconfiguring rss for
>> @@ -5523,6 +5533,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
>>   				 "Failed to reset tx rate for ch->seid %u\n",
>>   				 ch->seid);
>>   
>> +		/* delete cloud filters associated with this channel */
>> +		hlist_for_each_entry_safe(cfilter, node,
>> +					  &pf->cloud_filter_list, cloud_node) {
>> +			if (cfilter->seid != ch->seid)
>> +				continue;
>> +
>> +			hash_del(&cfilter->cloud_node);
>> +			if (cfilter->dst_port)
>> +				ret = i40e_add_del_cloud_filter_big_buf(vsi,
>> +									cfilter,
>> +									false);
>> +			else
>> +				ret = i40e_add_del_cloud_filter(vsi, cfilter,
>> +								false);
>> +			last_aq_status = pf->hw.aq.asq_last_status;
>> +			if (ret)
>> +				dev_info(&pf->pdev->dev,
>> +					 "fail to delete cloud filter, err %s aq_err %s\n",
>> +					 i40e_stat_str(&pf->hw, ret),
>> +					 i40e_aq_str(&pf->hw, last_aq_status));
>> +			kfree(cfilter);
>> +		}
>> +
>>   		/* delete VSI from FW */
>>   		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
>>   					     NULL);
>> @@ -6004,6 +6037,131 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
>>   }
>>   
>>   /**
>> + * i40e_get_device_capabilities - get device level info about the HW
>> + * @pf: the PF struct
>> + **/
>> +static int i40e_get_device_capabilities(struct i40e_pf *pf)
> 
> Since this is almost exactly the same as the original 
> i40e_get_capabilities(), it would be better to simply add list_type_opc 
> to i40e_get_capabilities() parameter list

Will fix this in v2.

> 
>> +{
>> +	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
>> +	u16 data_size;
>> +	int buf_len;
>> +	int err;
>> +
>> +	buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
>> +
>> +	/* proceed with query device level capabilities */
>> +	do {
>> +		cap_buf = kzalloc(buf_len, GFP_KERNEL);
>> +		if (!cap_buf)
>> +			return -ENOMEM;
>> +
>> +		/* this loads the data into the hw struct for us */
>> +		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
>> +					     &data_size,
>> +					     i40e_aqc_opc_list_dev_capabilities,
>> +					     NULL);
>> +		/* data loaded, buffer no longer needed */
>> +		kfree(cap_buf);
>> +
>> +		if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) {
>> +			/* retry with a larger buffer */
>> +			buf_len = data_size;
>> +		} else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) {
>> +			dev_dbg(&pf->pdev->dev,
>> +				"device capability discovery failed, err %s aq_err %s\n",
>> +				i40e_stat_str(&pf->hw, err),
>> +				i40e_aq_str(&pf->hw,
>> +					    pf->hw.aq.asq_last_status));
>> +			return -ENODEV;
>> +		}
>> +	} while (err);
>> +
>> +	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
>> +		dev_info(&pf->pdev->dev,
>> +			 "switch_mode=0x%04x, function_valid=0x%08x\n",
>> +			 pf->hw.dev_caps.switch_mode,
>> +			 pf->hw.dev_caps.valid_functions);
>> +		dev_info(&pf->pdev->dev,
>> +			 "SR-IOV=%d, num_vfs for all function=%u\n",
>> +			 pf->hw.dev_caps.sr_iov_1_1, pf->hw.dev_caps.num_vfs);
>> +		dev_info(&pf->pdev->dev,
>> +			 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
>> +			 pf->hw.dev_caps.num_vsis, pf->hw.dev_caps.num_rx_qp,
>> +			 pf->hw.dev_caps.num_tx_qp);
>> +	}
>> +	return 0;
>> +}
>> +
>> +/**
>> + * i40e_validate_and_set_switch_mode - sets up switch mode correctly
>> + * @vsi: ptr to VSI which has PF backing
>> + * @l4type: true for TCP ond false for UDP
>> + * @port_type: true if port is destination and false if port is source
>> + *
>> + * Sets up switch mode correctly if it needs to be changed and perform
>> + * what are allowed modes.
>> + **/
>> +static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
>> +					     bool port_type)
>> +{
>> +	u8 mode;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct i40e_hw *hw = &pf->hw;
>> +	int ret;
>> +
>> +	ret = i40e_get_device_capabilities(pf);
>> +	if (ret)
>> +		return -EINVAL;
>> +
>> +	if (hw->dev_caps.switch_mode) {
>> +		/* if switch mode is set, support mode2 (non-tunneled for
>> +		 * cloud filter) for now
>> +		 */
>> +#define I40E_SWITCH_MODE_MASK	0xF /* largest cloud filter mode is 0x8 */
> 
> Why isn't this defined in the appropriate .h file?

Will move this in v2.

> 
>> +		u32 switch_mode = hw->dev_caps.switch_mode &
>> +							I40E_SWITCH_MODE_MASK;
>> +		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
>> +			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
>> +				return 0;
>> +			dev_err(&pf->pdev->dev,
>> +				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
>> +				hw->dev_caps.switch_mode);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	/* port_type: true for destination port and false for source port
>> +	 * For now, supports only destination port type
>> +	 */
>> +	if (!port_type) {
>> +		dev_err(&pf->pdev->dev, "src port type not supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Set Bit 7 to be valid */
>> +	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
>> +
>> +	/* Set L4type to both TCP and UDP support */
>> +	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
>> +
>> +	/* Set cloud filter mode */
>> +	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
>> +
>> +	/* Prep mode field for set_switch_config */
>> +	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
>> +					pf->last_sw_conf_valid_flags,
>> +					mode, NULL);
>> +	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
>> +		dev_err(&pf->pdev->dev,
>> +			"couldn't set switch config bits, err %s aq_err %s\n",
>> +			i40e_stat_str(hw, ret),
>> +			i40e_aq_str(hw,
>> +				    hw->aq.asq_last_status));
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>>    * i40e_create_queue_channel - function to create channel
>>    * @vsi: VSI to be configured
>>    * @ch: ptr to channel (it contains channel specific params)
>> @@ -6632,6 +6790,10 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>>   	}
>>   	if (!hw) {
>>   		pf->flags &= ~I40E_FLAG_TC_MQPRIO;
>> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
>> +		    (pf->hw.func_caps.fd_filters_best_effort > 0))
>> +			pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
>> +
>>   		if (tc->type == TC_SETUP_MQPRIO_EXT)
>>   			memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
>>   			       sizeof(*tc->mqprio_qopt));
>> @@ -6682,6 +6844,11 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>>   		       sizeof(*tc->mqprio_qopt));
>>   		pf->flags |= I40E_FLAG_TC_MQPRIO;
>>   		pf->flags &= ~I40E_FLAG_DCB_ENABLED;
>> +		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
>> +			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
>> +			dev_info(&pf->pdev->dev,
>> +				 "Disabling ATR in MQPRIO mode\n");
>> +		}
>>   		break;
>>   	default:
>>   		return -EINVAL;
>> @@ -6743,10 +6910,724 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>>   	return ret;
>>   }
>>   
>> +/**
>> + * i40e_set_cld_element - sets cloud filter element data
>> + * @filter: cloud filter rule
>> + * @cld: ptr to cloud filter element data
>> + *
>> + * This is helper function to copy data into cloud filter element
>> + **/
>> +static inline void
>> +i40e_set_cld_element(struct i40e_cloud_filter *filter,
>> +		     struct i40e_aqc_add_remove_cloud_filters_element_data *cld)
>> +{
>> +	u8 *dest_ipaddr;
>> +	u32 ipaddr;
>> +	int i;
>> +
>> +	memset(cld, 0, sizeof(*cld));
>> +
>> +	ether_addr_copy(cld->outer_mac, filter->dst_mac);
>> +	ether_addr_copy(cld->inner_mac, filter->src_mac);
>> +
>> +	if (filter->is_ipv6) {
>> +		dest_ipaddr = (u8 *)&cld->ipaddr.v6.data;
>> +		for (i = ARRAY_SIZE(filter->dst_ipv6) - 1; i >= 0; i--) {
>> +			memcpy(dest_ipaddr, &filter->dst_ipv6[i], 1);
>> +			dest_ipaddr++;
>> +		}
>> +	} else {
>> +		ipaddr = be32_to_cpu(filter->dst_ip[0]);
>> +		memcpy(&cld->ipaddr.v4.data, &ipaddr, 4);
>> +	}
>> +
>> +	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
>> +	cld->tenant_id = cpu_to_le32(filter->tenant_id);
>> +}
>> +
>> +/**
>> + * i40e_add_del_cloud_filter - Add/del cloud filter
>> + * @vsi: pointer to VSI
>> + * @filter: cloud filter rule
>> + * @add: if true, add, if false, delete
>> + *
>> + * Add or delete a cloud filter for a specific flow spec.
>> + * Returns 0 if the filter were successfully added.
>> + **/
>> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
>> +				     struct i40e_cloud_filter *filter, bool add)
>> +{
>> +	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int ret;
>> +	static const u16 flag_table[128] = {
>> +		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
>> +		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
>> +		[I40E_CLOUD_FILTER_FLAGS_IIP] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IIP,
>> +	};
>> +
>> +	if (filter->flags >= ARRAY_SIZE(flag_table))
>> +		return I40E_ERR_CONFIG;
>> +
>> +	/* copy element needed to add cloud filter from filter */
>> +	i40e_set_cld_element(filter, &cld_filter);
>> +
>> +	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
>> +		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
>> +					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
>> +
>> +	if (filter->is_ipv6)
>> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
>> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
>> +	else
>> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
>> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
>> +
>> +	if (add)
>> +		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
>> +						&cld_filter, 1);
>> +	else
>> +		ret = i40e_aq_remove_cloud_filters(&pf->hw, filter->seid,
>> +						   &cld_filter, 1);
>> +	if (ret)
>> +		dev_dbg(&pf->pdev->dev,
>> +			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
>> +			add ? "add" : "delete", filter->dst_port, ret,
>> +			pf->hw.aq.asq_last_status);
>> +
>> +	dev_info(&pf->pdev->dev,
>> +		 "%s cloud filter for VSI: %d\n", add ? "Added" : "Deleted",
>> +		 filter->seid);
> 
> If there was an error, this dev_info() is lying to the user.

Will fix this in v2.

> 
>> +	return ret;
>> +}
>> +
>> +/**
>> + * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
>> + * @vsi: pointer to VSI
>> + * @filter: cloud filter rule
>> + * @add: if true, add, if false, delete
>> + *
>> + * Add or delete a cloud filter for a specific flow spec using big buffer.
>> + * Returns 0 if the filter were successfully added.
>> + **/
>> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
>> +					     struct i40e_cloud_filter *filter,
>> +					     bool add)
>> +{
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data cld_filter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int ret;
>> +
>> +	/* Both (Outer/Inner) valid mac_addr are not supported */
>> +	if (is_valid_ether_addr(filter->dst_mac) &&
>> +	    is_valid_ether_addr(filter->src_mac))
>> +		return -EINVAL;
>> +
>> +	/* Make sure port is specified, otherwise bail out, for channel
>> +	 * specific cloud filter needs 'L4 port' to be non-zero
>> +	 */
>> +	if (!filter->dst_port)
>> +		return -EINVAL;
>> +
>> +	/* adding filter using src_port/src_ip is not supported at this stage */
>> +	if (filter->src_port || filter->src_ip[0])
>> +		return -EINVAL;
>> +
>> +	/* copy element needed to add cloud filter from filter */
>> +	i40e_set_cld_element(filter, &cld_filter.element);
>> +
>> +	if (is_valid_ether_addr(filter->dst_mac) ||
>> +	    is_valid_ether_addr(filter->src_mac) ||
>> +	    is_multicast_ether_addr(filter->dst_mac) ||
>> +	    is_multicast_ether_addr(filter->src_mac)) {
>> +		/* MAC + IP : unsupported mode */
>> +		if (filter->dst_ip[0])
>> +			return -EINVAL;
>> +
>> +		/* since we validated that L4 port must be valid before
>> +		 * we get here, start with respective "flags" value
>> +		 * and update if vlan is present or not
>> +		 */
>> +		cld_filter.element.flags =
>> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
>> +
>> +		if (filter->vlan_id) {
>> +			cld_filter.element.flags =
>> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
>> +		}
> 
> 		cld_filter.element.flags = cpu_to_le16(filter->vlan_id
> 				? I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT;
> 				: I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
> 
> 
>> +
>> +	} else if (filter->dst_ip[0] || filter->is_ipv6) {
>> +		cld_filter.element.flags =
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
>> +		if (filter->is_ipv6)
>> +			cld_filter.element.flags |=
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
>> +		else
>> +			cld_filter.element.flags |=
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
> 
> 		cld_filter.element.flags |= cpu_to_le16(filter->is_ipv6
> 					? I40E_AQC_ADD_CLOUD_FLAGS_IPV6
> 					: I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
> 
> 
>> +	} else {
>> +		dev_err(&pf->pdev->dev,
>> +			"either mac or ip has to be valid for cloud filter\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Now copy L4 port in Byte 6..7 in general fields */
>> +	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
>> +						be16_to_cpu(filter->dst_port);
>> +
>> +	if (add) {
>> +		bool proto_type, port_type;
>> +
>> +		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
>> +		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
>> +			     true : false;
>> +
>> +		/* For now, src port based cloud filter for channel is not
>> +		 * supported
>> +		 */
>> +		if (!port_type) {
>> +			dev_err(&pf->pdev->dev,
>> +				"unsupported port type (src port)\n");
>> +			return -EOPNOTSUPP;
>> +		}
>> +
>> +		/* Validate current device switch mode, change if necessary */
>> +		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
>> +							port_type);
>> +		if (ret) {
>> +			dev_err(&pf->pdev->dev,
>> +				"fail to and set switch mode, ret %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		ret = i40e_aq_add_cloud_filters_big_buffer(&pf->hw,
>> +							   filter->seid,
>> +							   &cld_filter, 1);
>> +	} else {
>> +		ret = i40e_aq_remove_cloud_filters_big_buffer(&pf->hw,
>> +							      filter->seid,
>> +							      &cld_filter, 1);
>> +	}
>> +
>> +	if (ret)
>> +		dev_dbg(&pf->pdev->dev,
>> +			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
>> +			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
>> +
>> +	dev_info(&pf->pdev->dev,
>> +		 "%s cloud filter for VSI: %d, L4 port: %d\n",
>> +		 add ? "add" : "delete", filter->seid, ntohs(filter->dst_port));
> 
> If there was an error, this dev_info is possibly lying to the user.

Will fix this in v2.

> 
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
>> +				 struct tc_cls_flower_offload *f,
>> +				 struct i40e_cloud_filter *filter)
>> +{
>> +	struct i40e_pf *pf = vsi->back;
>> +	u16 addr_type = 0;
>> +	u8 field_flags = 0;
>> +
>> +	if (f->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_PORTS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
>> +		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
>> +			f->dissector->used_keys);
>> +		return -EOPNOTSUPP;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
>> +		struct flow_dissector_key_keyid *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
>> +						  f->key);
>> +
>> +		struct flow_dissector_key_keyid *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
>> +						  f->mask);
>> +
>> +		if (mask->keyid != 0)
>> +			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
>> +
>> +		filter->tenant_id = be32_to_cpu(key->keyid);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
>> +		struct flow_dissector_key_basic *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_BASIC,
>> +						  f->key);
>> +
>> +		filter->ip_proto = key->ip_proto;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
>> +		struct flow_dissector_key_eth_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
>> +						  f->key);
>> +
>> +		struct flow_dissector_key_eth_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
>> +						  f->mask);
>> +
>> +		/* use is_broadcast and is_zero to check for all 0xf or 0 */
>> +		if (!is_zero_ether_addr(mask->dst)) {
>> +			if (is_broadcast_ether_addr(mask->dst)) {
>> +				field_flags |= I40E_CLOUD_FIELD_OMAC;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
>> +					mask->dst);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (!is_zero_ether_addr(mask->src)) {
>> +			if (is_broadcast_ether_addr(mask->src)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IMAC;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
>> +					mask->src);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +		ether_addr_copy(filter->dst_mac, key->dst);
>> +		ether_addr_copy(filter->src_mac, key->src);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
>> +		struct flow_dissector_key_vlan *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_VLAN,
>> +						  f->key);
>> +		struct flow_dissector_key_vlan *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_VLAN,
>> +						  f->mask);
>> +
>> +		if (mask->vlan_id) {
>> +			if (mask->vlan_id == VLAN_VID_MASK) {
>> +				field_flags |= I40E_CLOUD_FIELD_IVLAN;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad vlan mask %u\n",
> 
> Should this be a %04x rather than %u?

That's right. Will fix this in v2.

> 
>> +					mask->vlan_id);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		filter->vlan_id = cpu_to_be16(key->vlan_id);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
>> +		struct flow_dissector_key_control *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_CONTROL,
>> +						  f->key);
>> +
>> +		addr_type = key->addr_type;
>> +	}
>> +
>> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
>> +		struct flow_dissector_key_ipv4_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
>> +						  f->key);
>> +		struct flow_dissector_key_ipv4_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
>> +						  f->mask);
>> +
>> +		if (mask->dst) {
>> +			if (mask->dst == cpu_to_be32(0xffffffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
>> +					be32_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (mask->src) {
>> +			if (mask->src == cpu_to_be32(0xffffffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
>> +					be32_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
>> +			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		filter->dst_ip[0] = key->dst;
>> +		filter->src_ip[0] = key->src;
>> +	}
>> +
>> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
>> +		struct flow_dissector_key_ipv6_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
>> +						  f->key);
>> +		struct flow_dissector_key_ipv6_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
>> +						  f->mask);
>> +
>> +		/* validate mask, make sure it is not IPV6_ADDR_ANY */
>> +		if (ipv6_addr_any(&mask->dst)) {
>> +			dev_err(&pf->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
>> +				IPV6_ADDR_ANY);
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +
>> +		/* validate src and dest IPV6 address, make sure they are not
>> +		 * ANY (0:0:0:0:0:0:0:0) or LOOPBACK (0:0:0:0:0:0:0:1), which
>> +		 * can be represented as ::1
>> +		 */
>> +		if (ipv6_addr_any(&key->dst) || ipv6_addr_loopback(&key->dst)) {
>> +			dev_err(&pf->pdev->dev,
>> +				"Bad ipv6 dst addr is ANY or LOOPBACK\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		if (ipv6_addr_loopback(&key->src)) {
>> +			dev_err(&pf->pdev->dev,
>> +				"Bad ipv6 src addr is ANY or LOOPBACK\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		memcpy(&filter->src_ipv6, &key->src.s6_addr,
>> +		       ARRAY_SIZE(filter->src_ipv6));
>> +		memcpy(&filter->dst_ipv6, &key->dst.s6_addr,
>> +		       ARRAY_SIZE(filter->dst_ipv6));
>> +
>> +		/* mark it as IPv6 filter, to be used later */
>> +		filter->is_ipv6 = true;
>> +
>> +		/* and it is IP[4|6] filter type */
>> +		field_flags |= I40E_CLOUD_FIELD_IIP;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
>> +		struct flow_dissector_key_ports *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_PORTS,
>> +						  f->key);
>> +		struct flow_dissector_key_ports *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_PORTS,
>> +						  f->mask);
>> +
>> +		if (mask->src) {
>> +			if (mask->src == cpu_to_be16(0xffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad src port mask %u\n",
> 
> Should this be a %04x rather than %u?

Will fix this in v2.

> 
>> +					be16_to_cpu(mask->src));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (mask->dst) {
>> +			if (mask->dst == cpu_to_be16(0xffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad dst port mask %u\n",
> 
> Should this be a %04x rather than %u?
> 
>> +					be16_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		filter->dst_port = key->dst;
>> +		filter->src_port = key->src;
>> +
>> +		/* For now, only supports destination port*/
>> +		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
>> +
>> +		switch (filter->ip_proto) {
>> +		case IPPROTO_TCP:
>> +		case IPPROTO_UDP:
>> +			break;
>> +		default:
>> +			dev_err(&pf->pdev->dev,
>> +				"Only UDP and TCP transport are supported\n");
>> +			return -EINVAL;
>> +		}
>> +	}
>> +	filter->flags = field_flags;
>> +	return 0;
>> +}
>> +
>> +/**
>> + * i40e_handle_redirect_action: Forward to a traffic class on the device
>> + * @vsi: Pointer to VSI
>> + * @ifindex: ifindex of the device to forwared to
>> + * @tc: traffic class index on the device
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
>> +				       struct i40e_cloud_filter *filter)
>> +{
>> +	struct i40e_channel *ch, *ch_tmp;
>> +
>> +	/* redirect to a traffic class on the same device */
>> +	if (vsi->netdev->ifindex == ifindex) {
>> +		if (tc == 0) {
>> +			filter->seid = vsi->seid;
>> +			return 0;
>> +		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
>> +			if (!filter->dst_port) {
>> +				dev_err(&vsi->back->pdev->dev,
>> +					"Specify destination port to redirect to traffic class that is not default\n");
>> +				return -EINVAL;
>> +			}
>> +			if (list_empty(&vsi->ch_list))
>> +				return -EINVAL;
>> +			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
>> +						 list) {
>> +				if (ch->seid == vsi->tc_seid_map[tc])
>> +					filter->seid = ch->seid;
>> +			}
>> +			return 0;
>> +		}
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * i40e_parse_tc_actions - Parse tc actions
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
>> +				 struct i40e_cloud_filter *filter)
>> +{
>> +	const struct tc_action *a;
>> +	LIST_HEAD(actions);
>> +	int err;
>> +
>> +	if (tc_no_actions(exts))
>> +		return -EINVAL;
>> +
>> +	tcf_exts_to_list(exts, &actions);
>> +	list_for_each_entry(a, &actions, list) {
>> +		/* Drop action */
>> +		if (is_tcf_gact_shot(a)) {
>> +			dev_err(&vsi->back->pdev->dev,
>> +				"Cloud filters do not support the drop action.\n");
>> +			return -EOPNOTSUPP;
>> +		}
>> +
>> +		/* Redirect to a traffic class on the same device */
>> +		if (!is_tcf_mirred_egress_redirect(a)) {
>> +			int ifindex = tcf_mirred_ifindex(a);
>> +			int tc = tcf_mirred_tc(a);
>> +
>> +			err = i40e_handle_redirect_action(vsi, ifindex, tc,
>> +							  filter);
>> +			if (err == 0)
>> +				return err;
>> +		}
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * i40e_configure_clsflower - Configure tc flower filters
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + *
>> + **/
>> +static int i40e_configure_clsflower(struct i40e_vsi *vsi,
>> +				    struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int err = 0;
>> +
>> +	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
>> +	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
>> +		return -EBUSY;
>> +
>> +	if (pf->fdir_pf_active_filters ||
>> +	    (!hlist_empty(&pf->fdir_filter_list))) {
>> +		dev_err(&vsi->back->pdev->dev,
>> +			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
>> +		dev_err(&vsi->back->pdev->dev,
>> +			"Disable Flow Director Sideband while configuring Cloud filters via tc-flower\n");
>> +		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
>> +	}
>> +
>> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
>> +	if (!filter)
>> +		return -ENOMEM;
>> +
>> +	filter->cookie = cls_flower->cookie;
>> +
>> +	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
>> +	if (err < 0)
>> +		goto err;
>> +
>> +	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
>> +	if (err < 0)
>> +		goto err;
>> +
>> +	/* Add cloud filter */
>> +	if (filter->dst_port)
>> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
>> +	else
>> +		err = i40e_add_del_cloud_filter(vsi, filter, true);
>> +
>> +	if (err) {
>> +		dev_err(&pf->pdev->dev,
>> +			"Failed to add cloud filter, err %s\n",
>> +			i40e_stat_str(&pf->hw, err));
>> +		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
>> +		goto err;
>> +	}
>> +
>> +	/* add filter to the ordered list */
>> +	INIT_HLIST_NODE(&filter->cloud_node);
>> +
>> +	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
>> +
>> +	pf->num_cloud_filters++;
>> +
>> +	return err;
>> +err:
>> +	kfree(filter);
>> +	return err;
>> +}
>> +
>> +/**
>> + * i40e_find_cloud_filter - Find the could filter in the list
>> + * @vsi: Pointer to VSI
>> + * @cookie: filter specific cookie
>> + *
>> + **/
>> +static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
>> +							unsigned long *cookie)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct hlist_node *node2;
>> +
>> +	hlist_for_each_entry_safe(filter, node2,
>> +				  &vsi->back->cloud_filter_list, cloud_node)
>> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
>> +			return filter;
>> +	return NULL;
>> +}
>> +
>> +/**
>> + * i40e_delete_clsflower - Remove tc flower filters
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + *
>> + **/
>> +static int i40e_delete_clsflower(struct i40e_vsi *vsi,
>> +				 struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int err = 0;
>> +
>> +	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
>> +
>> +	if (!filter)
>> +		return -EINVAL;
>> +
>> +	hash_del(&filter->cloud_node);
>> +
>> +	if (filter->dst_port)
>> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
>> +	else
>> +		err = i40e_add_del_cloud_filter(vsi, filter, false);
>> +	if (err) {
>> +		kfree(filter);
>> +		dev_err(&pf->pdev->dev,
>> +			"Failed to delete cloud filter, err %s\n",
>> +			i40e_stat_str(&pf->hw, err));
>> +		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
> 
> Hmmm... okay, maybe those earlier functions are not really lying if they 
> have this followup to check them.
> 
>> +	}
>> +
>> +	kfree(filter);
>> +	pf->num_cloud_filters--;
>> +
>> +	if (!pf->num_cloud_filters) {
>> +		/* Re-enable FD-SB that was disabled while configuring cloud
>> +		 * filters
>> +		 */
>> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
>> +		    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
>> +			if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
>> +			      pf->hw.num_partitions > 1))
>> +				pf->flags |= I40E_FLAG_FD_SB_ENABLED;
> 
> What if FD_SB was disabled for other reasons, like not enough msix 
> vectors or queues, or some other odd circumstance that turns them off?

I agree, this doesn't totally work to keep FD SB and Cloud filters
exclusive. I'll fix this in v2, perhaps with new PF flags to keep track
of the status of FD SB and Cloud filters.

> 
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int __i40e_setup_tc(struct net_device *netdev, u32 handle,
>>   			   u32 chain_index, __be16 proto,
>>   			   struct tc_to_netdev *tc)
>>   {
>> +	struct i40e_netdev_priv *np = netdev_priv(netdev);
>> +	struct i40e_vsi *vsi = np->vsi;
>> +
>> +	if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
>> +	    tc->type == TC_SETUP_CLSFLOWER) {
>> +		switch (tc->cls_flower->command) {
>> +		case TC_CLSFLOWER_REPLACE:
>> +			return i40e_configure_clsflower(vsi, tc->cls_flower);
>> +		case TC_CLSFLOWER_DESTROY:
>> +			return i40e_delete_clsflower(vsi, tc->cls_flower);
>> +		case TC_CLSFLOWER_STATS:
>> +			return -EOPNOTSUPP;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>>   	return i40e_setup_tc(netdev, tc);
>>   }
>>   
>> @@ -6948,6 +7829,16 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>>   		kfree(cfilter);
>>   	}
>>   	pf->num_cloud_filters = 0;
>> +
>> +	/* Re-enable FD-SB that was disabled while configuring cloud
>> +	 * filters
>> +	 */
>> +	if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
>> +	    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
>> +		if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
>> +		      pf->hw.num_partitions > 1))
>> +			pf->flags |= I40E_FLAG_FD_SB_ENABLED;
> 
> What if FD_SB was disabled for other reasons, like not enough msix 
> vectors or queues, or some other odd circumstance that turns them off?
> 
> This is getting complex and repetative.  Maybe there needs to be a 
> little function that checks all the FD_SB conditions and can be used 
> from anywhere needed.
> 
>> +	}
>>   }
>>   
>>   /**
>> @@ -8157,6 +9048,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
>>   }
>>   
>>   /**
>> + * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
>> + * @vsi: PF main vsi
>> + * @seid: seid of main or channel VSIs
>> + *
>> + * Rebuilds cloud filters associated with main VSI and channel VSIs if they
>> + * existed before reset
>> + **/
>> +static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
>> +{
>> +	struct i40e_cloud_filter *cfilter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct hlist_node *node;
>> +	i40e_status ret;
>> +
>> +	/* Add cloud filters back if they exist */
>> +	if (hlist_empty(&pf->cloud_filter_list))
>> +		return 0;
>> +
>> +	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
>> +				  cloud_node) {
>> +		if (cfilter->seid != seid)
>> +			continue;
>> +
>> +		if (cfilter->dst_port)
>> +			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
>> +								true);
>> +		else
>> +			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
>> +
>> +		if (ret) {
>> +			dev_dbg(&pf->pdev->dev,
>> +				"Failed to rebuild cloud filter, err %s aq_err %s\n",
>> +				i40e_stat_str(&pf->hw, ret),
>> +				i40e_aq_str(&pf->hw,
>> +					    pf->hw.aq.asq_last_status));
>> +			return ret;
>> +		}
>> +	}
>> +	return 0;
>> +}
>> +
>> +/**
>>    * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
>>    * @vsi: PF main vsi
>>    *
>> @@ -8193,6 +9126,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
>>   						I40E_BW_CREDIT_DIVISOR,
>>   				ch->seid);
>>   		}
>> +		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
>> +		if (ret) {
>> +			dev_dbg(&vsi->back->pdev->dev,
>> +				"Failed to rebuild cloud filters for channel VSI %u\n",
>> +				ch->seid);
>> +			return ret;
>> +		}
>>   	}
>>   	return 0;
>>   }
>> @@ -8476,6 +9416,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>>   			goto end_unlock;
>>   	}
>>   
>> +	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
>> +	if (ret)
>> +		goto end_unlock;
>> +
>>   	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
>>   	 * for this main VSI if they exist
>>   	 */
>> @@ -10846,7 +11790,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
>>   		netdev->hw_features |= NETIF_F_NTUPLE;
>>   	hw_features = hw_enc_features		|
>>   		      NETIF_F_HW_VLAN_CTAG_TX	|
>> -		      NETIF_F_HW_VLAN_CTAG_RX;
>> +		      NETIF_F_HW_VLAN_CTAG_RX	|
>> +		      NETIF_F_HW_TC;
>>   
>>   	netdev->hw_features |= hw_features;
>>   
>> @@ -12123,8 +13068,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>>   	*/
>>   
>>   	if ((pf->hw.pf_id == 0) &&
>> -	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
>> +	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
>>   		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
>> +		pf->last_sw_conf_flags = flags;
>> +	}
>>   
>>   	if (pf->hw.pf_id == 0) {
>>   		u16 valid_flags;
>> @@ -12140,6 +13087,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>>   					     pf->hw.aq.asq_last_status));
>>   			/* not a fatal problem, just keep going */
>>   		}
>> +		pf->last_sw_conf_valid_flags = valid_flags;
>>   	}
>>   
>>   	/* first time setup */
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> index 9142d0d..e24f1ce 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> @@ -283,6 +283,23 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
>>   		struct i40e_asq_cmd_details *cmd_details);
>>   i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
>>   				   struct i40e_asq_cmd_details *cmd_details);
>> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
>> +	u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count);
>> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
>> +		u16 vsi,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count);
>> +
>> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
>> +		u16 vsi,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count);
>> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
>> +	struct i40e_hw *hw, u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count);
>>   i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
>>   			       struct i40e_lldp_variables *lldp_cfg);
>>   /* i40e_common */
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan@osuosl.org
>> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Intel-wired-lan] [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier
@ 2017-08-14 19:21       ` Nambiar, Amritha
  0 siblings, 0 replies; 58+ messages in thread
From: Nambiar, Amritha @ 2017-08-14 19:21 UTC (permalink / raw)
  To: intel-wired-lan

On 8/1/2017 12:16 PM, Shannon Nelson wrote:
> On 7/31/2017 5:38 PM, Amritha Nambiar wrote:
>> This patch enables tc-flower based hardware offloads. tc/flower
>> filter provided by the kernel is configured as driver specific
>> cloud filter. The patch implements functions and admin queue
>> commands needed to support cloud filters in the driver and
>> adds cloud filters to configure these tc-flower filters.
>>
>> The only action supported is to redirect packets to a traffic class
>> on the same device.
>>
>> # tc qdisc add dev eth0 ingress
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>    prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 0
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>    prio 2 flower dst_ip 192.168.3.5/32\
>>    ip_proto udp dst_port 25 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 1
>>
>> # tc filter add dev eth0 protocol ipv6 parent ffff:\
>>    prio 3 flower dst_ip fe8::200:1\
>>    ip_proto udp dst_port 66 skip_sw indev eth0\
>>    action mirred ingress redirect dev eth0 tc 2
>>
>> Delete tc flower filter:
>> Example:
>>
>> # tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
>> # tc filter del dev eth0 parent ffff:
>>
>> Flow Director Sideband is disabled while configuring cloud filters
>> via tc-flower.
> 
> Only while configuring, or the whole time there is a cloud filter?  This 
> is unclear here.

The entire time cloud filters exists. Will make the comment clearer in v2.

> 
>>
>> Unsupported matches when cloud filters are added using enhanced
>> big buffer cloud filter mode of underlying switch include:
>> 1. source port and source IP
>> 2. Combined MAC address and IP fields.
>> 3. Not specfying L4 port
> 
> s/specfying/specifying/

Will fix in v2.

> 
>>
>> These filter matches can however be used to redirect traffic to
>> the main VSI (tc 0) which does not require the enhanced big buffer
>> cloud filter support.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
>> ---
>>   drivers/net/ethernet/intel/i40e/i40e.h           |   46 +
>>   drivers/net/ethernet/intel/i40e/i40e_common.c    |  180 ++++
>>   drivers/net/ethernet/intel/i40e/i40e_main.c      |  952 ++++++++++++++++++++++
>>   drivers/net/ethernet/intel/i40e/i40e_prototype.h |   17
>>   4 files changed, 1193 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
>> index 5c0cad5..7288265 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
>> @@ -55,6 +55,8 @@
>>   #include <linux/net_tstamp.h>
>>   #include <linux/ptp_clock_kernel.h>
>>   #include <net/pkt_cls.h>
>> +#include <net/tc_act/tc_gact.h>
>> +#include <net/tc_act/tc_mirred.h>
>>   #include "i40e_type.h"
>>   #include "i40e_prototype.h"
>>   #include "i40e_client.h"
>> @@ -252,10 +254,51 @@ struct i40e_fdir_filter {
>>   	u32 fd_id;
>>   };
>>   
>> +#define I40E_CLOUD_FIELD_OMAC	0x01
>> +#define I40E_CLOUD_FIELD_IMAC	0x02
>> +#define I40E_CLOUD_FIELD_IVLAN	0x04
>> +#define I40E_CLOUD_FIELD_TEN_ID	0x08
>> +#define I40E_CLOUD_FIELD_IIP	0x10
>> +
>> +#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
>> +						 I40E_CLOUD_FIELD_IVLAN)
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
>> +						 I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
>> +						  I40E_CLOUD_FIELD_IMAC | \
>> +						  I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
>> +						   I40E_CLOUD_FIELD_IVLAN | \
>> +						   I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
>> +
>>   struct i40e_cloud_filter {
>>   	struct hlist_node cloud_node;
>>   	/* cloud filter input set follows */
>>   	unsigned long cookie;
>> +	u8 dst_mac[ETH_ALEN];
>> +	u8 src_mac[ETH_ALEN];
>> +	__be16 vlan_id;
>> +	__be32 dst_ip[4];
>> +	__be32 src_ip[4];
>> +	u8 dst_ipv6[16];
>> +	u8 src_ipv6[16];
>> +	__be16 dst_port;
>> +	__be16 src_port;
>> +	/* matter only when IP based filtering is set */
>> +	bool is_ipv6;
>> +	/* IPPROTO value */
>> +	u8 ip_proto;
>> +	/* L4 port type: src or destination port */
>> +#define I40E_CLOUD_FILTER_PORT_SRC	0x01
>> +#define I40E_CLOUD_FILTER_PORT_DEST	0x02
>> +	u8 port_type;
>> +	u32 tenant_id;
>> +	u8 flags;
>> +#define I40E_CLOUD_TNL_TYPE_NONE	0xff
>> +	u8 tunnel_type;
>>   	/* filter control */
>>   	u16 seid;
>>   };
>> @@ -574,6 +617,9 @@ struct i40e_pf {
>>   	u16 phy_led_val;
>>   
>>   	u16 override_q_count;
>> +	u16 last_sw_conf_flags;
>> +	u16 last_sw_conf_valid_flags;
>> +
> 
> Unnecessary blank line
> 
>>   };
>>   
>>   /**
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
>> index d0e8138..bfbe304 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_common.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
>> @@ -5269,5 +5269,185 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
>>   
>>   	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
>>   				   track_id, &offset, &info, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_add_cloud_filters
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to add cloud filters from
>> + * @filters: Buffer which contains the filters to be added
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Set the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
>> +		u16 seid,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	enum i40e_status_code status;
>> +	u16 buff_len;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_add_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_add_cloud_filters_big_buffer
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to add cloud filters from
>> + * @filters: Buffer which contains the filters in big buffer to be added
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Set the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
>> +	u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	i40e_status status;
>> +	u16 buff_len;
>> +	int i;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_add_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
>> +
>> +	for (i = 0; i < filter_count; i++) {
>> +		u16 tnl_type;
>> +		u32 ti;
>> +
>> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
>> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
>> +			ti = le32_to_cpu(filters[i].element.tenant_id);
>> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
>> +		}
> 
> You might want to add a comment to explain this little hack.  I believe 
> this is for a bit of firmware weirdness?

Yes, this is to handle a firmware behavior specific to Geneve filters.
Will add the comment in v2.

> 
>> +	}
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_remove_cloud_filters
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to remove cloud filters from
>> + * @filters: Buffer which contains the filters to be removed
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Remove the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
>> +		u16 seid,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	enum i40e_status_code status;
>> +	u16 buff_len;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_remove_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_remove_cloud_filters_big_buffer
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to remove cloud filters from
>> + * @filters: Buffer which contains the filters in big buffer to be removed
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Remove the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_add_remove_cloud_filters_element_big_data are filled
>> + * in by the caller of the function.
>> + *
>> + **/
>> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
>> +	struct i40e_hw *hw,
>> +	u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	i40e_status status;
>> +	u16 buff_len;
>> +	int i;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_remove_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +	cmd->big_buffer_flag = I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER;
>> +
>> +	for (i = 0; i < filter_count; i++) {
>> +		u16 tnl_type;
>> +		u32 ti;
>> +
>> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
>> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
>> +			ti = le32_to_cpu(filters[i].element.tenant_id);
>> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
>> +		}
> 
> Same comment
> 
>> +	}
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>>   	return status;
>>   }
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> index 93f6fe2..65b284e 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> @@ -69,6 +69,12 @@ static int i40e_reset(struct i40e_pf *pf);
>>   static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
>>   static void i40e_fdir_sb_setup(struct i40e_pf *pf);
>>   static int i40e_veb_get_bw_info(struct i40e_veb *veb);
>> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
>> +				     struct i40e_cloud_filter *filter,
>> +				     bool add);
>> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
>> +					     struct i40e_cloud_filter *filter,
>> +					     bool add);
>>   
>>   /* i40e_pci_tbl - PCI Device ID Table
>>    *
>> @@ -5482,7 +5488,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
>>    **/
>>   static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
> 
> Hmmm... I'm looking at net-next code and I don't see this function, or 
> anything else with "i40e_channel" or "queue_channel".  What are you 
> applying this patch to?  Am I missing something?
> 
>>   {
>> +	enum i40e_admin_queue_err last_aq_status;
>> +	struct i40e_cloud_filter *cfilter;
>>   	struct i40e_channel *ch, *ch_tmp;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct hlist_node *node;
>>   	int ret, i;
>>   
>>   	/* Reset rss size that was stored when reconfiguring rss for
>> @@ -5523,6 +5533,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
>>   				 "Failed to reset tx rate for ch->seid %u\n",
>>   				 ch->seid);
>>   
>> +		/* delete cloud filters associated with this channel */
>> +		hlist_for_each_entry_safe(cfilter, node,
>> +					  &pf->cloud_filter_list, cloud_node) {
>> +			if (cfilter->seid != ch->seid)
>> +				continue;
>> +
>> +			hash_del(&cfilter->cloud_node);
>> +			if (cfilter->dst_port)
>> +				ret = i40e_add_del_cloud_filter_big_buf(vsi,
>> +									cfilter,
>> +									false);
>> +			else
>> +				ret = i40e_add_del_cloud_filter(vsi, cfilter,
>> +								false);
>> +			last_aq_status = pf->hw.aq.asq_last_status;
>> +			if (ret)
>> +				dev_info(&pf->pdev->dev,
>> +					 "fail to delete cloud filter, err %s aq_err %s\n",
>> +					 i40e_stat_str(&pf->hw, ret),
>> +					 i40e_aq_str(&pf->hw, last_aq_status));
>> +			kfree(cfilter);
>> +		}
>> +
>>   		/* delete VSI from FW */
>>   		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
>>   					     NULL);
>> @@ -6004,6 +6037,131 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
>>   }
>>   
>>   /**
>> + * i40e_get_device_capabilities - get device level info about the HW
>> + * @pf: the PF struct
>> + **/
>> +static int i40e_get_device_capabilities(struct i40e_pf *pf)
> 
> Since this is almost exactly the same as the original 
> i40e_get_capabilities(), it would be better to simply add list_type_opc 
> to i40e_get_capabilities() parameter list

Will fix this in v2.

> 
>> +{
>> +	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
>> +	u16 data_size;
>> +	int buf_len;
>> +	int err;
>> +
>> +	buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
>> +
>> +	/* proceed with query device level capabilities */
>> +	do {
>> +		cap_buf = kzalloc(buf_len, GFP_KERNEL);
>> +		if (!cap_buf)
>> +			return -ENOMEM;
>> +
>> +		/* this loads the data into the hw struct for us */
>> +		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
>> +					     &data_size,
>> +					     i40e_aqc_opc_list_dev_capabilities,
>> +					     NULL);
>> +		/* data loaded, buffer no longer needed */
>> +		kfree(cap_buf);
>> +
>> +		if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) {
>> +			/* retry with a larger buffer */
>> +			buf_len = data_size;
>> +		} else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) {
>> +			dev_dbg(&pf->pdev->dev,
>> +				"device capability discovery failed, err %s aq_err %s\n",
>> +				i40e_stat_str(&pf->hw, err),
>> +				i40e_aq_str(&pf->hw,
>> +					    pf->hw.aq.asq_last_status));
>> +			return -ENODEV;
>> +		}
>> +	} while (err);
>> +
>> +	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
>> +		dev_info(&pf->pdev->dev,
>> +			 "switch_mode=0x%04x, function_valid=0x%08x\n",
>> +			 pf->hw.dev_caps.switch_mode,
>> +			 pf->hw.dev_caps.valid_functions);
>> +		dev_info(&pf->pdev->dev,
>> +			 "SR-IOV=%d, num_vfs for all function=%u\n",
>> +			 pf->hw.dev_caps.sr_iov_1_1, pf->hw.dev_caps.num_vfs);
>> +		dev_info(&pf->pdev->dev,
>> +			 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
>> +			 pf->hw.dev_caps.num_vsis, pf->hw.dev_caps.num_rx_qp,
>> +			 pf->hw.dev_caps.num_tx_qp);
>> +	}
>> +	return 0;
>> +}
>> +
>> +/**
>> + * i40e_validate_and_set_switch_mode - sets up switch mode correctly
>> + * @vsi: ptr to VSI which has PF backing
>> + * @l4type: true for TCP ond false for UDP
>> + * @port_type: true if port is destination and false if port is source
>> + *
>> + * Sets up switch mode correctly if it needs to be changed and perform
>> + * what are allowed modes.
>> + **/
>> +static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
>> +					     bool port_type)
>> +{
>> +	u8 mode;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct i40e_hw *hw = &pf->hw;
>> +	int ret;
>> +
>> +	ret = i40e_get_device_capabilities(pf);
>> +	if (ret)
>> +		return -EINVAL;
>> +
>> +	if (hw->dev_caps.switch_mode) {
>> +		/* if switch mode is set, support mode2 (non-tunneled for
>> +		 * cloud filter) for now
>> +		 */
>> +#define I40E_SWITCH_MODE_MASK	0xF /* largest cloud filter mode is 0x8 */
> 
> Why isn't this defined in the appropriate .h file?

Will move this in v2.

> 
>> +		u32 switch_mode = hw->dev_caps.switch_mode &
>> +							I40E_SWITCH_MODE_MASK;
>> +		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
>> +			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
>> +				return 0;
>> +			dev_err(&pf->pdev->dev,
>> +				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
>> +				hw->dev_caps.switch_mode);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	/* port_type: true for destination port and false for source port
>> +	 * For now, supports only destination port type
>> +	 */
>> +	if (!port_type) {
>> +		dev_err(&pf->pdev->dev, "src port type not supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Set Bit 7 to be valid */
>> +	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
>> +
>> +	/* Set L4type to both TCP and UDP support */
>> +	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
>> +
>> +	/* Set cloud filter mode */
>> +	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
>> +
>> +	/* Prep mode field for set_switch_config */
>> +	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
>> +					pf->last_sw_conf_valid_flags,
>> +					mode, NULL);
>> +	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
>> +		dev_err(&pf->pdev->dev,
>> +			"couldn't set switch config bits, err %s aq_err %s\n",
>> +			i40e_stat_str(hw, ret),
>> +			i40e_aq_str(hw,
>> +				    hw->aq.asq_last_status));
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>>    * i40e_create_queue_channel - function to create channel
>>    * @vsi: VSI to be configured
>>    * @ch: ptr to channel (it contains channel specific params)
>> @@ -6632,6 +6790,10 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>>   	}
>>   	if (!hw) {
>>   		pf->flags &= ~I40E_FLAG_TC_MQPRIO;
>> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
>> +		    (pf->hw.func_caps.fd_filters_best_effort > 0))
>> +			pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
>> +
>>   		if (tc->type == TC_SETUP_MQPRIO_EXT)
>>   			memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
>>   			       sizeof(*tc->mqprio_qopt));
>> @@ -6682,6 +6844,11 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>>   		       sizeof(*tc->mqprio_qopt));
>>   		pf->flags |= I40E_FLAG_TC_MQPRIO;
>>   		pf->flags &= ~I40E_FLAG_DCB_ENABLED;
>> +		if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
>> +			pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
>> +			dev_info(&pf->pdev->dev,
>> +				 "Disabling ATR in MQPRIO mode\n");
>> +		}
>>   		break;
>>   	default:
>>   		return -EINVAL;
>> @@ -6743,10 +6910,724 @@ static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
>>   	return ret;
>>   }
>>   
>> +/**
>> + * i40e_set_cld_element - sets cloud filter element data
>> + * @filter: cloud filter rule
>> + * @cld: ptr to cloud filter element data
>> + *
>> + * This is helper function to copy data into cloud filter element
>> + **/
>> +static inline void
>> +i40e_set_cld_element(struct i40e_cloud_filter *filter,
>> +		     struct i40e_aqc_add_remove_cloud_filters_element_data *cld)
>> +{
>> +	u8 *dest_ipaddr;
>> +	u32 ipaddr;
>> +	int i;
>> +
>> +	memset(cld, 0, sizeof(*cld));
>> +
>> +	ether_addr_copy(cld->outer_mac, filter->dst_mac);
>> +	ether_addr_copy(cld->inner_mac, filter->src_mac);
>> +
>> +	if (filter->is_ipv6) {
>> +		dest_ipaddr = (u8 *)&cld->ipaddr.v6.data;
>> +		for (i = ARRAY_SIZE(filter->dst_ipv6) - 1; i >= 0; i--) {
>> +			memcpy(dest_ipaddr, &filter->dst_ipv6[i], 1);
>> +			dest_ipaddr++;
>> +		}
>> +	} else {
>> +		ipaddr = be32_to_cpu(filter->dst_ip[0]);
>> +		memcpy(&cld->ipaddr.v4.data, &ipaddr, 4);
>> +	}
>> +
>> +	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
>> +	cld->tenant_id = cpu_to_le32(filter->tenant_id);
>> +}
>> +
>> +/**
>> + * i40e_add_del_cloud_filter - Add/del cloud filter
>> + * @vsi: pointer to VSI
>> + * @filter: cloud filter rule
>> + * @add: if true, add, if false, delete
>> + *
>> + * Add or delete a cloud filter for a specific flow spec.
>> + * Returns 0 if the filter were successfully added.
>> + **/
>> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
>> +				     struct i40e_cloud_filter *filter, bool add)
>> +{
>> +	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int ret;
>> +	static const u16 flag_table[128] = {
>> +		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
>> +		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
>> +		[I40E_CLOUD_FILTER_FLAGS_IIP] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IIP,
>> +	};
>> +
>> +	if (filter->flags >= ARRAY_SIZE(flag_table))
>> +		return I40E_ERR_CONFIG;
>> +
>> +	/* copy element needed to add cloud filter from filter */
>> +	i40e_set_cld_element(filter, &cld_filter);
>> +
>> +	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
>> +		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
>> +					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
>> +
>> +	if (filter->is_ipv6)
>> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
>> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
>> +	else
>> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
>> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
>> +
>> +	if (add)
>> +		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
>> +						&cld_filter, 1);
>> +	else
>> +		ret = i40e_aq_remove_cloud_filters(&pf->hw, filter->seid,
>> +						   &cld_filter, 1);
>> +	if (ret)
>> +		dev_dbg(&pf->pdev->dev,
>> +			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
>> +			add ? "add" : "delete", filter->dst_port, ret,
>> +			pf->hw.aq.asq_last_status);
>> +
>> +	dev_info(&pf->pdev->dev,
>> +		 "%s cloud filter for VSI: %d\n", add ? "Added" : "Deleted",
>> +		 filter->seid);
> 
> If there was an error, this dev_info() is lying to the user.

Will fix this in v2.

> 
>> +	return ret;
>> +}
>> +
>> +/**
>> + * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
>> + * @vsi: pointer to VSI
>> + * @filter: cloud filter rule
>> + * @add: if true, add, if false, delete
>> + *
>> + * Add or delete a cloud filter for a specific flow spec using big buffer.
>> + * Returns 0 if the filter were successfully added.
>> + **/
>> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
>> +					     struct i40e_cloud_filter *filter,
>> +					     bool add)
>> +{
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data cld_filter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int ret;
>> +
>> +	/* Both (Outer/Inner) valid mac_addr are not supported */
>> +	if (is_valid_ether_addr(filter->dst_mac) &&
>> +	    is_valid_ether_addr(filter->src_mac))
>> +		return -EINVAL;
>> +
>> +	/* Make sure port is specified, otherwise bail out, for channel
>> +	 * specific cloud filter needs 'L4 port' to be non-zero
>> +	 */
>> +	if (!filter->dst_port)
>> +		return -EINVAL;
>> +
>> +	/* adding filter using src_port/src_ip is not supported at this stage */
>> +	if (filter->src_port || filter->src_ip[0])
>> +		return -EINVAL;
>> +
>> +	/* copy element needed to add cloud filter from filter */
>> +	i40e_set_cld_element(filter, &cld_filter.element);
>> +
>> +	if (is_valid_ether_addr(filter->dst_mac) ||
>> +	    is_valid_ether_addr(filter->src_mac) ||
>> +	    is_multicast_ether_addr(filter->dst_mac) ||
>> +	    is_multicast_ether_addr(filter->src_mac)) {
>> +		/* MAC + IP : unsupported mode */
>> +		if (filter->dst_ip[0])
>> +			return -EINVAL;
>> +
>> +		/* since we validated that L4 port must be valid before
>> +		 * we get here, start with respective "flags" value
>> +		 * and update if vlan is present or not
>> +		 */
>> +		cld_filter.element.flags =
>> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
>> +
>> +		if (filter->vlan_id) {
>> +			cld_filter.element.flags =
>> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
>> +		}
> 
> 		cld_filter.element.flags = cpu_to_le16(filter->vlan_id
> 				? I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT;
> 				: I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
> 
> 
>> +
>> +	} else if (filter->dst_ip[0] || filter->is_ipv6) {
>> +		cld_filter.element.flags =
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
>> +		if (filter->is_ipv6)
>> +			cld_filter.element.flags |=
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
>> +		else
>> +			cld_filter.element.flags |=
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
> 
> 		cld_filter.element.flags |= cpu_to_le16(filter->is_ipv6
> 					? I40E_AQC_ADD_CLOUD_FLAGS_IPV6
> 					: I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
> 
> 
>> +	} else {
>> +		dev_err(&pf->pdev->dev,
>> +			"either mac or ip has to be valid for cloud filter\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Now copy L4 port in Byte 6..7 in general fields */
>> +	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
>> +						be16_to_cpu(filter->dst_port);
>> +
>> +	if (add) {
>> +		bool proto_type, port_type;
>> +
>> +		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
>> +		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
>> +			     true : false;
>> +
>> +		/* For now, src port based cloud filter for channel is not
>> +		 * supported
>> +		 */
>> +		if (!port_type) {
>> +			dev_err(&pf->pdev->dev,
>> +				"unsupported port type (src port)\n");
>> +			return -EOPNOTSUPP;
>> +		}
>> +
>> +		/* Validate current device switch mode, change if necessary */
>> +		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
>> +							port_type);
>> +		if (ret) {
>> +			dev_err(&pf->pdev->dev,
>> +				"fail to and set switch mode, ret %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		ret = i40e_aq_add_cloud_filters_big_buffer(&pf->hw,
>> +							   filter->seid,
>> +							   &cld_filter, 1);
>> +	} else {
>> +		ret = i40e_aq_remove_cloud_filters_big_buffer(&pf->hw,
>> +							      filter->seid,
>> +							      &cld_filter, 1);
>> +	}
>> +
>> +	if (ret)
>> +		dev_dbg(&pf->pdev->dev,
>> +			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
>> +			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
>> +
>> +	dev_info(&pf->pdev->dev,
>> +		 "%s cloud filter for VSI: %d, L4 port: %d\n",
>> +		 add ? "add" : "delete", filter->seid, ntohs(filter->dst_port));
> 
> If there was an error, this dev_info is possibly lying to the user.

Will fix this in v2.

> 
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
>> +				 struct tc_cls_flower_offload *f,
>> +				 struct i40e_cloud_filter *filter)
>> +{
>> +	struct i40e_pf *pf = vsi->back;
>> +	u16 addr_type = 0;
>> +	u8 field_flags = 0;
>> +
>> +	if (f->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_PORTS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) {
>> +		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
>> +			f->dissector->used_keys);
>> +		return -EOPNOTSUPP;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
>> +		struct flow_dissector_key_keyid *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
>> +						  f->key);
>> +
>> +		struct flow_dissector_key_keyid *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
>> +						  f->mask);
>> +
>> +		if (mask->keyid != 0)
>> +			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
>> +
>> +		filter->tenant_id = be32_to_cpu(key->keyid);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
>> +		struct flow_dissector_key_basic *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_BASIC,
>> +						  f->key);
>> +
>> +		filter->ip_proto = key->ip_proto;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
>> +		struct flow_dissector_key_eth_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
>> +						  f->key);
>> +
>> +		struct flow_dissector_key_eth_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
>> +						  f->mask);
>> +
>> +		/* use is_broadcast and is_zero to check for all 0xf or 0 */
>> +		if (!is_zero_ether_addr(mask->dst)) {
>> +			if (is_broadcast_ether_addr(mask->dst)) {
>> +				field_flags |= I40E_CLOUD_FIELD_OMAC;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
>> +					mask->dst);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (!is_zero_ether_addr(mask->src)) {
>> +			if (is_broadcast_ether_addr(mask->src)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IMAC;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
>> +					mask->src);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +		ether_addr_copy(filter->dst_mac, key->dst);
>> +		ether_addr_copy(filter->src_mac, key->src);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
>> +		struct flow_dissector_key_vlan *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_VLAN,
>> +						  f->key);
>> +		struct flow_dissector_key_vlan *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_VLAN,
>> +						  f->mask);
>> +
>> +		if (mask->vlan_id) {
>> +			if (mask->vlan_id == VLAN_VID_MASK) {
>> +				field_flags |= I40E_CLOUD_FIELD_IVLAN;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad vlan mask %u\n",
> 
> Should this be a %04x rather than %u?

That's right. Will fix this in v2.

> 
>> +					mask->vlan_id);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		filter->vlan_id = cpu_to_be16(key->vlan_id);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
>> +		struct flow_dissector_key_control *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_CONTROL,
>> +						  f->key);
>> +
>> +		addr_type = key->addr_type;
>> +	}
>> +
>> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
>> +		struct flow_dissector_key_ipv4_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
>> +						  f->key);
>> +		struct flow_dissector_key_ipv4_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
>> +						  f->mask);
>> +
>> +		if (mask->dst) {
>> +			if (mask->dst == cpu_to_be32(0xffffffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
>> +					be32_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (mask->src) {
>> +			if (mask->src == cpu_to_be32(0xffffffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
>> +					be32_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
>> +			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		filter->dst_ip[0] = key->dst;
>> +		filter->src_ip[0] = key->src;
>> +	}
>> +
>> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
>> +		struct flow_dissector_key_ipv6_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
>> +						  f->key);
>> +		struct flow_dissector_key_ipv6_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
>> +						  f->mask);
>> +
>> +		/* validate mask, make sure it is not IPV6_ADDR_ANY */
>> +		if (ipv6_addr_any(&mask->dst)) {
>> +			dev_err(&pf->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
>> +				IPV6_ADDR_ANY);
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +
>> +		/* validate src and dest IPV6 address, make sure they are not
>> +		 * ANY (0:0:0:0:0:0:0:0) or LOOPBACK (0:0:0:0:0:0:0:1), which
>> +		 * can be represented as ::1
>> +		 */
>> +		if (ipv6_addr_any(&key->dst) || ipv6_addr_loopback(&key->dst)) {
>> +			dev_err(&pf->pdev->dev,
>> +				"Bad ipv6 dst addr is ANY or LOOPBACK\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		if (ipv6_addr_loopback(&key->src)) {
>> +			dev_err(&pf->pdev->dev,
>> +				"Bad ipv6 src addr is ANY or LOOPBACK\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		memcpy(&filter->src_ipv6, &key->src.s6_addr,
>> +		       ARRAY_SIZE(filter->src_ipv6));
>> +		memcpy(&filter->dst_ipv6, &key->dst.s6_addr,
>> +		       ARRAY_SIZE(filter->dst_ipv6));
>> +
>> +		/* mark it as IPv6 filter, to be used later */
>> +		filter->is_ipv6 = true;
>> +
>> +		/* and it is IP[4|6] filter type */
>> +		field_flags |= I40E_CLOUD_FIELD_IIP;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
>> +		struct flow_dissector_key_ports *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_PORTS,
>> +						  f->key);
>> +		struct flow_dissector_key_ports *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_PORTS,
>> +						  f->mask);
>> +
>> +		if (mask->src) {
>> +			if (mask->src == cpu_to_be16(0xffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad src port mask %u\n",
> 
> Should this be a %04x rather than %u?

Will fix this in v2.

> 
>> +					be16_to_cpu(mask->src));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (mask->dst) {
>> +			if (mask->dst == cpu_to_be16(0xffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad dst port mask %u\n",
> 
> Should this be a %04x rather than %u?
> 
>> +					be16_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		filter->dst_port = key->dst;
>> +		filter->src_port = key->src;
>> +
>> +		/* For now, only supports destination port*/
>> +		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
>> +
>> +		switch (filter->ip_proto) {
>> +		case IPPROTO_TCP:
>> +		case IPPROTO_UDP:
>> +			break;
>> +		default:
>> +			dev_err(&pf->pdev->dev,
>> +				"Only UDP and TCP transport are supported\n");
>> +			return -EINVAL;
>> +		}
>> +	}
>> +	filter->flags = field_flags;
>> +	return 0;
>> +}
>> +
>> +/**
>> + * i40e_handle_redirect_action: Forward to a traffic class on the device
>> + * @vsi: Pointer to VSI
>> + * @ifindex: ifindex of the device to forwared to
>> + * @tc: traffic class index on the device
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
>> +				       struct i40e_cloud_filter *filter)
>> +{
>> +	struct i40e_channel *ch, *ch_tmp;
>> +
>> +	/* redirect to a traffic class on the same device */
>> +	if (vsi->netdev->ifindex == ifindex) {
>> +		if (tc == 0) {
>> +			filter->seid = vsi->seid;
>> +			return 0;
>> +		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
>> +			if (!filter->dst_port) {
>> +				dev_err(&vsi->back->pdev->dev,
>> +					"Specify destination port to redirect to traffic class that is not default\n");
>> +				return -EINVAL;
>> +			}
>> +			if (list_empty(&vsi->ch_list))
>> +				return -EINVAL;
>> +			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
>> +						 list) {
>> +				if (ch->seid == vsi->tc_seid_map[tc])
>> +					filter->seid = ch->seid;
>> +			}
>> +			return 0;
>> +		}
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * i40e_parse_tc_actions - Parse tc actions
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
>> +				 struct i40e_cloud_filter *filter)
>> +{
>> +	const struct tc_action *a;
>> +	LIST_HEAD(actions);
>> +	int err;
>> +
>> +	if (tc_no_actions(exts))
>> +		return -EINVAL;
>> +
>> +	tcf_exts_to_list(exts, &actions);
>> +	list_for_each_entry(a, &actions, list) {
>> +		/* Drop action */
>> +		if (is_tcf_gact_shot(a)) {
>> +			dev_err(&vsi->back->pdev->dev,
>> +				"Cloud filters do not support the drop action.\n");
>> +			return -EOPNOTSUPP;
>> +		}
>> +
>> +		/* Redirect to a traffic class on the same device */
>> +		if (!is_tcf_mirred_egress_redirect(a)) {
>> +			int ifindex = tcf_mirred_ifindex(a);
>> +			int tc = tcf_mirred_tc(a);
>> +
>> +			err = i40e_handle_redirect_action(vsi, ifindex, tc,
>> +							  filter);
>> +			if (err == 0)
>> +				return err;
>> +		}
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * i40e_configure_clsflower - Configure tc flower filters
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + *
>> + **/
>> +static int i40e_configure_clsflower(struct i40e_vsi *vsi,
>> +				    struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int err = 0;
>> +
>> +	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
>> +	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
>> +		return -EBUSY;
>> +
>> +	if (pf->fdir_pf_active_filters ||
>> +	    (!hlist_empty(&pf->fdir_filter_list))) {
>> +		dev_err(&vsi->back->pdev->dev,
>> +			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
>> +		dev_err(&vsi->back->pdev->dev,
>> +			"Disable Flow Director Sideband while configuring Cloud filters via tc-flower\n");
>> +		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
>> +	}
>> +
>> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
>> +	if (!filter)
>> +		return -ENOMEM;
>> +
>> +	filter->cookie = cls_flower->cookie;
>> +
>> +	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
>> +	if (err < 0)
>> +		goto err;
>> +
>> +	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
>> +	if (err < 0)
>> +		goto err;
>> +
>> +	/* Add cloud filter */
>> +	if (filter->dst_port)
>> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
>> +	else
>> +		err = i40e_add_del_cloud_filter(vsi, filter, true);
>> +
>> +	if (err) {
>> +		dev_err(&pf->pdev->dev,
>> +			"Failed to add cloud filter, err %s\n",
>> +			i40e_stat_str(&pf->hw, err));
>> +		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
>> +		goto err;
>> +	}
>> +
>> +	/* add filter to the ordered list */
>> +	INIT_HLIST_NODE(&filter->cloud_node);
>> +
>> +	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
>> +
>> +	pf->num_cloud_filters++;
>> +
>> +	return err;
>> +err:
>> +	kfree(filter);
>> +	return err;
>> +}
>> +
>> +/**
>> + * i40e_find_cloud_filter - Find the could filter in the list
>> + * @vsi: Pointer to VSI
>> + * @cookie: filter specific cookie
>> + *
>> + **/
>> +static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
>> +							unsigned long *cookie)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct hlist_node *node2;
>> +
>> +	hlist_for_each_entry_safe(filter, node2,
>> +				  &vsi->back->cloud_filter_list, cloud_node)
>> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
>> +			return filter;
>> +	return NULL;
>> +}
>> +
>> +/**
>> + * i40e_delete_clsflower - Remove tc flower filters
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + *
>> + **/
>> +static int i40e_delete_clsflower(struct i40e_vsi *vsi,
>> +				 struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int err = 0;
>> +
>> +	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
>> +
>> +	if (!filter)
>> +		return -EINVAL;
>> +
>> +	hash_del(&filter->cloud_node);
>> +
>> +	if (filter->dst_port)
>> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
>> +	else
>> +		err = i40e_add_del_cloud_filter(vsi, filter, false);
>> +	if (err) {
>> +		kfree(filter);
>> +		dev_err(&pf->pdev->dev,
>> +			"Failed to delete cloud filter, err %s\n",
>> +			i40e_stat_str(&pf->hw, err));
>> +		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
> 
> Hmmm... okay, maybe those earlier functions are not really lying if they 
> have this followup to check them.
> 
>> +	}
>> +
>> +	kfree(filter);
>> +	pf->num_cloud_filters--;
>> +
>> +	if (!pf->num_cloud_filters) {
>> +		/* Re-enable FD-SB that was disabled while configuring cloud
>> +		 * filters
>> +		 */
>> +		if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
>> +		    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
>> +			if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
>> +			      pf->hw.num_partitions > 1))
>> +				pf->flags |= I40E_FLAG_FD_SB_ENABLED;
> 
> What if FD_SB was disabled for other reasons, like not enough msix 
> vectors or queues, or some other odd circumstance that turns them off?

I agree, this doesn't totally work to keep FD SB and Cloud filters
exclusive. I'll fix this in v2, perhaps with new PF flags to keep track
of the status of FD SB and Cloud filters.

> 
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int __i40e_setup_tc(struct net_device *netdev, u32 handle,
>>   			   u32 chain_index, __be16 proto,
>>   			   struct tc_to_netdev *tc)
>>   {
>> +	struct i40e_netdev_priv *np = netdev_priv(netdev);
>> +	struct i40e_vsi *vsi = np->vsi;
>> +
>> +	if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) &&
>> +	    tc->type == TC_SETUP_CLSFLOWER) {
>> +		switch (tc->cls_flower->command) {
>> +		case TC_CLSFLOWER_REPLACE:
>> +			return i40e_configure_clsflower(vsi, tc->cls_flower);
>> +		case TC_CLSFLOWER_DESTROY:
>> +			return i40e_delete_clsflower(vsi, tc->cls_flower);
>> +		case TC_CLSFLOWER_STATS:
>> +			return -EOPNOTSUPP;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>>   	return i40e_setup_tc(netdev, tc);
>>   }
>>   
>> @@ -6948,6 +7829,16 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>>   		kfree(cfilter);
>>   	}
>>   	pf->num_cloud_filters = 0;
>> +
>> +	/* Re-enable FD-SB that was disabled while configuring cloud
>> +	 * filters
>> +	 */
>> +	if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
>> +	    (pf->hw.func_caps.fd_filters_best_effort > 0)) {
>> +		if (!(pf->flags & I40E_FLAG_MFP_ENABLED &&
>> +		      pf->hw.num_partitions > 1))
>> +			pf->flags |= I40E_FLAG_FD_SB_ENABLED;
> 
> What if FD_SB was disabled for other reasons, like not enough msix 
> vectors or queues, or some other odd circumstance that turns them off?
> 
> This is getting complex and repetative.  Maybe there needs to be a 
> little function that checks all the FD_SB conditions and can be used 
> from anywhere needed.
> 
>> +	}
>>   }
>>   
>>   /**
>> @@ -8157,6 +9048,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
>>   }
>>   
>>   /**
>> + * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
>> + * @vsi: PF main vsi
>> + * @seid: seid of main or channel VSIs
>> + *
>> + * Rebuilds cloud filters associated with main VSI and channel VSIs if they
>> + * existed before reset
>> + **/
>> +static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
>> +{
>> +	struct i40e_cloud_filter *cfilter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct hlist_node *node;
>> +	i40e_status ret;
>> +
>> +	/* Add cloud filters back if they exist */
>> +	if (hlist_empty(&pf->cloud_filter_list))
>> +		return 0;
>> +
>> +	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
>> +				  cloud_node) {
>> +		if (cfilter->seid != seid)
>> +			continue;
>> +
>> +		if (cfilter->dst_port)
>> +			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
>> +								true);
>> +		else
>> +			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
>> +
>> +		if (ret) {
>> +			dev_dbg(&pf->pdev->dev,
>> +				"Failed to rebuild cloud filter, err %s aq_err %s\n",
>> +				i40e_stat_str(&pf->hw, ret),
>> +				i40e_aq_str(&pf->hw,
>> +					    pf->hw.aq.asq_last_status));
>> +			return ret;
>> +		}
>> +	}
>> +	return 0;
>> +}
>> +
>> +/**
>>    * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
>>    * @vsi: PF main vsi
>>    *
>> @@ -8193,6 +9126,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
>>   						I40E_BW_CREDIT_DIVISOR,
>>   				ch->seid);
>>   		}
>> +		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
>> +		if (ret) {
>> +			dev_dbg(&vsi->back->pdev->dev,
>> +				"Failed to rebuild cloud filters for channel VSI %u\n",
>> +				ch->seid);
>> +			return ret;
>> +		}
>>   	}
>>   	return 0;
>>   }
>> @@ -8476,6 +9416,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>>   			goto end_unlock;
>>   	}
>>   
>> +	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
>> +	if (ret)
>> +		goto end_unlock;
>> +
>>   	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
>>   	 * for this main VSI if they exist
>>   	 */
>> @@ -10846,7 +11790,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
>>   		netdev->hw_features |= NETIF_F_NTUPLE;
>>   	hw_features = hw_enc_features		|
>>   		      NETIF_F_HW_VLAN_CTAG_TX	|
>> -		      NETIF_F_HW_VLAN_CTAG_RX;
>> +		      NETIF_F_HW_VLAN_CTAG_RX	|
>> +		      NETIF_F_HW_TC;
>>   
>>   	netdev->hw_features |= hw_features;
>>   
>> @@ -12123,8 +13068,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>>   	*/
>>   
>>   	if ((pf->hw.pf_id == 0) &&
>> -	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
>> +	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
>>   		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
>> +		pf->last_sw_conf_flags = flags;
>> +	}
>>   
>>   	if (pf->hw.pf_id == 0) {
>>   		u16 valid_flags;
>> @@ -12140,6 +13087,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>>   					     pf->hw.aq.asq_last_status));
>>   			/* not a fatal problem, just keep going */
>>   		}
>> +		pf->last_sw_conf_valid_flags = valid_flags;
>>   	}
>>   
>>   	/* first time setup */
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> index 9142d0d..e24f1ce 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> @@ -283,6 +283,23 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
>>   		struct i40e_asq_cmd_details *cmd_details);
>>   i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
>>   				   struct i40e_asq_cmd_details *cmd_details);
>> +i40e_status i40e_aq_add_cloud_filters_big_buffer(struct i40e_hw *hw,
>> +	u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count);
>> +enum i40e_status_code i40e_aq_add_cloud_filters(struct i40e_hw *hw,
>> +		u16 vsi,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count);
>> +
>> +enum i40e_status_code i40e_aq_remove_cloud_filters(struct i40e_hw *hw,
>> +		u16 vsi,
>> +		struct i40e_aqc_add_remove_cloud_filters_element_data *filters,
>> +		u8 filter_count);
>> +i40e_status i40e_aq_remove_cloud_filters_big_buffer(
>> +	struct i40e_hw *hw, u16 seid,
>> +	struct i40e_aqc_add_remove_cloud_filters_element_big_data *filters,
>> +	u8 filter_count);
>>   i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
>>   			       struct i40e_lldp_variables *lldp_cfg);
>>   /* i40e_common */
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan at osuosl.org
>> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>>

^ permalink raw reply	[flat|nested] 58+ messages in thread

end of thread, other threads:[~2017-08-14 19:21 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-01  0:36 [PATCH net-next RFC 0/6] Configure cloud filters in i40e via tc/flower classifier Amritha Nambiar
2017-08-01  0:36 ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01  0:37 ` [PATCH 1/6] [net-next]net: sched: act_mirred: Extend redirect action to accept a traffic class Amritha Nambiar
2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01 10:22   ` Jamal Hadi Salim
2017-08-01 10:22     ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-02  1:12     ` Nambiar, Amritha
2017-08-02  1:12       ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-01 10:44   ` Jamal Hadi Salim
2017-08-01 10:44     ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-02  1:42     ` Nambiar, Amritha
2017-08-02  1:42       ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-01 11:12   ` Jiri Pirko
2017-08-01 11:12     ` [Intel-wired-lan] " Jiri Pirko
2017-08-02  2:20     ` Nambiar, Amritha
2017-08-02  2:20       ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-01  0:37 ` [PATCH 2/6] [net-next]net: i40e: Maintain a mapping of TCs with the VSI seids Amritha Nambiar
2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01  0:37 ` [PATCH 3/6] [net-next]net: i40e: Extend set switch config command to accept cloud filter mode Amritha Nambiar
2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01 10:48   ` Jamal Hadi Salim
2017-08-01 10:48     ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-01  0:37 ` [PATCH 4/6] [net-next]net: i40e: Admin queue definitions for cloud filters Amritha Nambiar
2017-08-01  0:37   ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01 19:16   ` Shannon Nelson
2017-08-01 19:16     ` Shannon Nelson
2017-08-14 18:59     ` Nambiar, Amritha
2017-08-14 18:59       ` Nambiar, Amritha
2017-08-01  0:38 ` [PATCH 5/6] [net-next]net: i40e: Clean up of " Amritha Nambiar
2017-08-01  0:38   ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01 19:16   ` Shannon Nelson
2017-08-01 19:16     ` Shannon Nelson
2017-08-14 19:06     ` Nambiar, Amritha
2017-08-14 19:06       ` Nambiar, Amritha
2017-08-01  0:38 ` [PATCH 6/6] [net-next]net: i40e: Enable cloud filters in i40e via tc/flower classifier Amritha Nambiar
2017-08-01  0:38   ` [Intel-wired-lan] " Amritha Nambiar
2017-08-01 10:56   ` Jamal Hadi Salim
2017-08-01 10:56     ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-02  2:13     ` Nambiar, Amritha
2017-08-02  2:13       ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-02 12:02       ` Jamal Hadi Salim
2017-08-02 12:02         ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-02 18:20         ` Nambiar, Amritha
2017-08-02 18:20           ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-01 19:16   ` Shannon Nelson
2017-08-01 19:16     ` Shannon Nelson
2017-08-14 19:21     ` Nambiar, Amritha
2017-08-14 19:21       ` Nambiar, Amritha
2017-08-01 10:15 ` [PATCH net-next RFC 0/6] Configure " Jamal Hadi Salim
2017-08-01 10:15   ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-02  0:57   ` Nambiar, Amritha
2017-08-02  0:57     ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-02 12:01     ` Jamal Hadi Salim
2017-08-02 12:01       ` [Intel-wired-lan] " Jamal Hadi Salim
2017-08-02 18:17       ` Nambiar, Amritha
2017-08-02 18:17         ` [Intel-wired-lan] " Nambiar, Amritha
2017-08-01 19:16 ` Shannon Nelson
2017-08-01 19:16   ` Shannon Nelson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.