diff mbox

[net-next,1/8] net/flower: Introduce hardware offload support

Message ID 1456842290-7844-2-git-send-email-amir@vadai.me
State Rejected, archived
Delegated to: David Miller
Headers show

Commit Message

Amir Vadai March 1, 2016, 2:24 p.m. UTC
This patch is based on a patch made by John Fastabend.
It adds support for offloading cls_flower.
A filter that is offloaded successfuly by hardware, will not be added to
the hashtable and won't be processed by software.

Suggested-by: John Fastabend <john.r.fastabend@intel.com>
Signed-off-by: Amir Vadai <amir@vadai.me>
---
 include/linux/netdevice.h    |  2 ++
 include/net/pkt_cls.h        | 14 +++++++++
 include/uapi/linux/pkt_cls.h |  2 ++
 net/sched/cls_flower.c       | 75 +++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 88 insertions(+), 5 deletions(-)

Comments

Jiri Pirko March 1, 2016, 2:47 p.m. UTC | #1
Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>This patch is based on a patch made by John Fastabend.
>It adds support for offloading cls_flower.
>A filter that is offloaded successfuly by hardware, will not be added to
>the hashtable and won't be processed by software.

That is wrong. User should explitly specify to not include rule into sw
by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
have only SKIP_HW). Please add that in this patchset.


>
>Suggested-by: John Fastabend <john.r.fastabend@intel.com>
>Signed-off-by: Amir Vadai <amir@vadai.me>
>---
> include/linux/netdevice.h    |  2 ++
> include/net/pkt_cls.h        | 14 +++++++++
> include/uapi/linux/pkt_cls.h |  2 ++
> net/sched/cls_flower.c       | 75 +++++++++++++++++++++++++++++++++++++++++---
> 4 files changed, 88 insertions(+), 5 deletions(-)
>
>diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>index e52077f..0fd329a 100644
>--- a/include/linux/netdevice.h
>+++ b/include/linux/netdevice.h
>@@ -785,6 +785,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
> enum {
> 	TC_SETUP_MQPRIO,
> 	TC_SETUP_CLSU32,
>+	TC_SETUP_CLSFLOWER,
> };
> 
> struct tc_cls_u32_offload;
>@@ -794,6 +795,7 @@ struct tc_to_netdev {
> 	union {
> 		u8 tc;
> 		struct tc_cls_u32_offload *cls_u32;
>+		struct tc_cls_flower_offload *cls_flower;
> 	};
> };
> 
>diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
>index bea14ee..beb2ee1 100644
>--- a/include/net/pkt_cls.h
>+++ b/include/net/pkt_cls.h
>@@ -409,4 +409,18 @@ static inline bool tc_should_offload(struct net_device *dev, u32 flags)
> 	return true;
> }
> 
>+enum {
>+	TC_CLSFLOWER_REPLACE,
>+	TC_CLSFLOWER_DESTROY,
>+};
>+
>+struct tc_cls_flower_offload {
>+	int command;
>+	u64 cookie;
>+	struct flow_dissector *dissector;
>+	struct fl_flow_key *mask;
>+	struct fl_flow_key *key;
>+	struct tcf_exts *exts;
>+};
>+
> #endif
>diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
>index 9874f568..c43c5f7 100644
>--- a/include/uapi/linux/pkt_cls.h
>+++ b/include/uapi/linux/pkt_cls.h
>@@ -417,6 +417,8 @@ enum {
> 	TCA_FLOWER_KEY_TCP_DST,		/* be16 */
> 	TCA_FLOWER_KEY_UDP_SRC,		/* be16 */
> 	TCA_FLOWER_KEY_UDP_DST,		/* be16 */
>+
>+	TCA_FLOWER_FLAGS,
> 	__TCA_FLOWER_MAX,
> };
> 
>diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
>index 95b0212..e599bea 100644
>--- a/net/sched/cls_flower.c
>+++ b/net/sched/cls_flower.c
>@@ -165,6 +165,53 @@ static void fl_destroy_filter(struct rcu_head *head)
> 	kfree(f);
> }
> 
>+static int fl_hw_destroy_filter(struct tcf_proto *tp, u64 cookie)
>+{
>+	struct net_device *dev = tp->q->dev_queue->dev;
>+	struct tc_cls_flower_offload offload = {0};
>+	struct tc_to_netdev tc;
>+
>+	if (!tc_should_offload(dev, 0))
>+		return -ENOTSUPP;
>+
>+	offload.command = TC_CLSFLOWER_DESTROY;
>+	offload.cookie = cookie;
>+
>+	tc.type = TC_SETUP_CLSFLOWER;
>+	tc.cls_flower = &offload;
>+
>+	return dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
>+					     &tc);
>+}
>+
>+static int fl_hw_replace_filter(struct tcf_proto *tp,
>+				struct flow_dissector *dissector,
>+				struct fl_flow_key *mask,
>+				struct fl_flow_key *key,
>+				struct tcf_exts *actions,
>+				u64 cookie, u32 flags)
>+{
>+	struct net_device *dev = tp->q->dev_queue->dev;
>+	struct tc_cls_flower_offload offload = {0};
>+	struct tc_to_netdev tc;
>+
>+	if (!tc_should_offload(dev, flags))
>+		return -ENOTSUPP;
>+
>+	offload.command = TC_CLSFLOWER_REPLACE;
>+	offload.cookie = cookie;
>+	offload.dissector = dissector;
>+	offload.mask = mask;
>+	offload.key = key;
>+	offload.exts = actions;
>+
>+	tc.type = TC_SETUP_CLSFLOWER;
>+	tc.cls_flower = &offload;
>+
>+	return dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
>+					     &tc);
>+}
>+
> static bool fl_destroy(struct tcf_proto *tp, bool force)
> {
> 	struct cls_fl_head *head = rtnl_dereference(tp->root);
>@@ -174,6 +221,7 @@ static bool fl_destroy(struct tcf_proto *tp, bool force)
> 		return false;
> 
> 	list_for_each_entry_safe(f, next, &head->filters, list) {
>+		fl_hw_destroy_filter(tp, (u64)f);
> 		list_del_rcu(&f->list);
> 		call_rcu(&f->rcu, fl_destroy_filter);
> 	}
>@@ -459,6 +507,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
> 	struct cls_fl_filter *fnew;
> 	struct nlattr *tb[TCA_FLOWER_MAX + 1];
> 	struct fl_flow_mask mask = {};
>+	u32 flags = 0;
> 	int err;
> 
> 	if (!tca[TCA_OPTIONS])
>@@ -494,13 +543,28 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
> 	if (err)
> 		goto errout;
> 
>-	err = rhashtable_insert_fast(&head->ht, &fnew->ht_node,
>-				     head->ht_params);
>-	if (err)
>-		goto errout;
>-	if (fold)
>+	if (tb[TCA_FLOWER_FLAGS])
>+		flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
>+
>+	err = fl_hw_replace_filter(tp,
>+				   &head->dissector,
>+				   &mask.key,
>+				   &fnew->key,
>+				   &fnew->exts,
>+				   (u64)fnew,
>+				   flags);
>+	if (err) {
>+		err = rhashtable_insert_fast(&head->ht, &fnew->ht_node,
>+					     head->ht_params);
>+		if (err)
>+			goto errout;
>+	}
>+
>+	if (fold) {
> 		rhashtable_remove_fast(&head->ht, &fold->ht_node,
> 				       head->ht_params);
>+		fl_hw_destroy_filter(tp, (u64)fold);
>+	}
> 
> 	*arg = (unsigned long) fnew;
> 
>@@ -527,6 +591,7 @@ static int fl_delete(struct tcf_proto *tp, unsigned long arg)
> 	rhashtable_remove_fast(&head->ht, &f->ht_node,
> 			       head->ht_params);
> 	list_del_rcu(&f->list);
>+	fl_hw_destroy_filter(tp, (u64)f);
> 	tcf_unbind_filter(tp, &f->res);
> 	call_rcu(&f->rcu, fl_destroy_filter);
> 	return 0;
>-- 
>2.7.0
>
Jiri Pirko March 1, 2016, 2:53 p.m. UTC | #2
Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>This patch is based on a patch made by John Fastabend.
>It adds support for offloading cls_flower.
>A filter that is offloaded successfuly by hardware, will not be added to
>the hashtable and won't be processed by software.
>

<snip>

>+enum {
>+	TC_CLSFLOWER_REPLACE,
>+	TC_CLSFLOWER_DESTROY,
>+};

Name this enum

>+
>+struct tc_cls_flower_offload {
>+	int command;

        ^^^ and use it here

>+	u64 cookie;
>+	struct flow_dissector *dissector;
>+	struct fl_flow_key *mask;
>+	struct fl_flow_key *key;
>+	struct tcf_exts *exts;
>+};
Amir Vadai March 1, 2016, 4:49 p.m. UTC | #3
On Tue, Mar 01, 2016 at 03:47:19PM +0100, Jiri Pirko wrote:
> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
> >This patch is based on a patch made by John Fastabend.
> >It adds support for offloading cls_flower.
> >A filter that is offloaded successfuly by hardware, will not be added to
> >the hashtable and won't be processed by software.
> 
> That is wrong. User should explitly specify to not include rule into sw
> by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
> have only SKIP_HW). Please add that in this patchset.
Why? If a rule is offloaded, why would the user want to reprocess it by
software?
If the user use SKIP_HW, it will be processed by SW. Else, the user
would want it to be processed by HW or fallback to SW. I don't
understand in which case the user would like to have it done twice.

> 

[..]
Amir Vadai March 1, 2016, 4:50 p.m. UTC | #4
On Tue, Mar 01, 2016 at 03:53:44PM +0100, Jiri Pirko wrote:
> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
> >This patch is based on a patch made by John Fastabend.
> >It adds support for offloading cls_flower.
> >A filter that is offloaded successfuly by hardware, will not be added to
> >the hashtable and won't be processed by software.
> >
> 
> <snip>
> 
> >+enum {
> >+	TC_CLSFLOWER_REPLACE,
> >+	TC_CLSFLOWER_DESTROY,
> >+};
> 
> Name this enum
Right,
Thanks

> 
> >+
> >+struct tc_cls_flower_offload {
> >+	int command;
> 
>         ^^^ and use it here
> 
> >+	u64 cookie;
> >+	struct flow_dissector *dissector;
> >+	struct fl_flow_key *mask;
> >+	struct fl_flow_key *key;
> >+	struct tcf_exts *exts;
> >+};
Jiri Pirko March 1, 2016, 5:01 p.m. UTC | #5
Tue, Mar 01, 2016 at 05:49:27PM CET, amir@vadai.me wrote:
>On Tue, Mar 01, 2016 at 03:47:19PM +0100, Jiri Pirko wrote:
>> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>> >This patch is based on a patch made by John Fastabend.
>> >It adds support for offloading cls_flower.
>> >A filter that is offloaded successfuly by hardware, will not be added to
>> >the hashtable and won't be processed by software.
>> 
>> That is wrong. User should explitly specify to not include rule into sw
>> by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
>> have only SKIP_HW). Please add that in this patchset.
>Why? If a rule is offloaded, why would the user want to reprocess it by
>software?
>If the user use SKIP_HW, it will be processed by SW. Else, the user
>would want it to be processed by HW or fallback to SW. I don't
>understand in which case the user would like to have it done twice.

For example if you turn on the offloading by unsetting NETIF_F_HW_TC.
Or if someone inserts skbs into rx path directly, for example pktgen.
We need SKIP_KERNEL to be set by user, not implicit.
Or Gerlitz March 2, 2016, 11:14 a.m. UTC | #6
On Tue, Mar 1, 2016 at 7:01 PM, Jiri Pirko <jiri@resnulli.us> wrote:
> Tue, Mar 01, 2016 at 05:49:27PM CET, amir@vadai.me wrote:
>>On Tue, Mar 01, 2016 at 03:47:19PM +0100, Jiri Pirko wrote:
>>> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>>> >This patch is based on a patch made by John Fastabend.

>>> >It adds support for offloading cls_flower.
>>> >A filter that is offloaded successfully by hardware, will not be added to
>>> >the hashtable and won't be processed by software.

>>> That is wrong. User should explicitly specify to not include rule into sw
>>> by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
>>> have only SKIP_HW). Please add that in this patchset.

>> Why? If a rule is offloaded, why would the user want to reprocess it by software?

>> If the user use SKIP_HW, it will be processed by SW. Else, the user
>> would want it to be processed by HW or fallback to SW. I don't
>> understand in which case the user would like to have it done twice.

> For example if you turn on the offloading by unsetting NETIF_F_HW_TC.
> Or if someone inserts skbs into rx path directly, for example pktgen.
> We need SKIP_KERNEL to be set by user, not implicit.

As discussed in netdev, we want to have three modes for TC offloads

1. SW only
2. HW only (and err if can't)
3. HW and if not supported fallback to SW

Now, from your reply, I understand we want a fourth mode

4. Both (HW and SW)

Do we agree that these four policies/modes make sense?

So you want #4 to be the default? can you elaborate why? note that for
the HW marking
case a "both" mode will be very inefficient, also for actions like
vlan push/pop, encap/decap, etc
the result will be just wrong... I don't think this should be the default.

Or.
Jiri Pirko March 2, 2016, 1:22 p.m. UTC | #7
Wed, Mar 02, 2016 at 12:14:39PM CET, gerlitz.or@gmail.com wrote:
>On Tue, Mar 1, 2016 at 7:01 PM, Jiri Pirko <jiri@resnulli.us> wrote:
>> Tue, Mar 01, 2016 at 05:49:27PM CET, amir@vadai.me wrote:
>>>On Tue, Mar 01, 2016 at 03:47:19PM +0100, Jiri Pirko wrote:
>>>> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>>>> >This patch is based on a patch made by John Fastabend.
>
>>>> >It adds support for offloading cls_flower.
>>>> >A filter that is offloaded successfully by hardware, will not be added to
>>>> >the hashtable and won't be processed by software.
>
>>>> That is wrong. User should explicitly specify to not include rule into sw
>>>> by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
>>>> have only SKIP_HW). Please add that in this patchset.
>
>>> Why? If a rule is offloaded, why would the user want to reprocess it by software?
>
>>> If the user use SKIP_HW, it will be processed by SW. Else, the user
>>> would want it to be processed by HW or fallback to SW. I don't
>>> understand in which case the user would like to have it done twice.
>
>> For example if you turn on the offloading by unsetting NETIF_F_HW_TC.
>> Or if someone inserts skbs into rx path directly, for example pktgen.
>> We need SKIP_KERNEL to be set by user, not implicit.
>
>As discussed in netdev, we want to have three modes for TC offloads
>
>1. SW only
>2. HW only (and err if can't)
>3. HW and if not supported fallback to SW
>
>Now, from your reply, I understand we want a fourth mode
>
>4. Both (HW and SW)

I would perhaps do it a litte bit differently:
NO FLAG (default)- insert into kernel and HW now:
		if NETIF_F_HW_TC is off (default)
			-> push to kernel only (current behaviour)
		if NETIF_F_HW_TC is on AND push to HW fails
			-> return error
SKIP_HW - flag to tell kernel not to insert into HW
SKIP_SW - flag to tell kernel not to insert into kernel

to achieve hw only, user has to turn on the NETIF_F_HW_TC and
pass SKIP_SW flag.


>
>Do we agree that these four policies/modes make sense?
>
>So you want #4 to be the default? can you elaborate why? note that for
>the HW marking
>case a "both" mode will be very inefficient, also for actions like
>vlan push/pop, encap/decap, etc

Well when you push/pop vlan of encap/decap tunnel header in hw, you won't
match the packet in kernel again. Most likely. But anyway, if you turn
on NETIF_F_HW_TC you know what you are doing and you adjust the
included rules (flags) accordingly.


>the result will be just wrong... I don't think this should be the default.
>
>Or.
John Fastabend March 2, 2016, 4:22 p.m. UTC | #8
On 16-03-02 05:22 AM, Jiri Pirko wrote:
> Wed, Mar 02, 2016 at 12:14:39PM CET, gerlitz.or@gmail.com wrote:
>> On Tue, Mar 1, 2016 at 7:01 PM, Jiri Pirko <jiri@resnulli.us> wrote:
>>> Tue, Mar 01, 2016 at 05:49:27PM CET, amir@vadai.me wrote:
>>>> On Tue, Mar 01, 2016 at 03:47:19PM +0100, Jiri Pirko wrote:
>>>>> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>>>>>> This patch is based on a patch made by John Fastabend.
>>
>>>>>> It adds support for offloading cls_flower.
>>>>>> A filter that is offloaded successfully by hardware, will not be added to
>>>>>> the hashtable and won't be processed by software.
>>
>>>>> That is wrong. User should explicitly specify to not include rule into sw
>>>>> by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
>>>>> have only SKIP_HW). Please add that in this patchset.
>>
>>>> Why? If a rule is offloaded, why would the user want to reprocess it by software?
>>
>>>> If the user use SKIP_HW, it will be processed by SW. Else, the user
>>>> would want it to be processed by HW or fallback to SW. I don't
>>>> understand in which case the user would like to have it done twice.
>>
>>> For example if you turn on the offloading by unsetting NETIF_F_HW_TC.
>>> Or if someone inserts skbs into rx path directly, for example pktgen.
>>> We need SKIP_KERNEL to be set by user, not implicit.
>>
>> As discussed in netdev, we want to have three modes for TC offloads
>>
>> 1. SW only
>> 2. HW only (and err if can't)
>> 3. HW and if not supported fallback to SW
>>
>> Now, from your reply, I understand we want a fourth mode
>>
>> 4. Both (HW and SW)
> 
> I would perhaps do it a litte bit differently:
> NO FLAG (default)- insert into kernel and HW now:
> 		if NETIF_F_HW_TC is off (default)
> 			-> push to kernel only (current behaviour)
> 		if NETIF_F_HW_TC is on AND push to HW fails
> 			-> return error
> SKIP_HW - flag to tell kernel not to insert into HW
> SKIP_SW - flag to tell kernel not to insert into kernel
> 
> to achieve hw only, user has to turn on the NETIF_F_HW_TC and
> pass SKIP_SW flag.
> 

The modes Jiri describes here is exactly how I planned to build
this. And at the moment the only one we are missing is SKIP_HW
which I'm reworking now and should have in a few days.

To resolve the error handling if the rule is SKIP_HW or NO_FLAG
an error will be thrown if it can not be applied to software.
Notice if an error happens on the software insert with NO_FLAG then
the hardware insert is not attempted either. With SKIP_SW I will
throw an error if the hardware insert fails because there is
no software fallback in this mode.

The only mode I haven't looked at doing is

	3. HW and if not supported fallback to SW

I'm not sure I have a use case for it at the moment. It is sufficient
for me to just do a SKIP_SW command followed by a SKIP_HW command
if needed. I guess someone else could implement it if they really need
it.

> 
>>
>> Do we agree that these four policies/modes make sense?
>>
>> So you want #4 to be the default? can you elaborate why? note that for
>> the HW marking
>> case a "both" mode will be very inefficient, also for actions like
>> vlan push/pop, encap/decap, etc
> 
> Well when you push/pop vlan of encap/decap tunnel header in hw, you won't
> match the packet in kernel again. Most likely. But anyway, if you turn
> on NETIF_F_HW_TC you know what you are doing and you adjust the
> included rules (flags) accordingly.
> 
> 
>> the result will be just wrong... I don't think this should be the default.
>>
>> Or.
Jiri Pirko March 2, 2016, 4:30 p.m. UTC | #9
Wed, Mar 02, 2016 at 05:22:48PM CET, john.fastabend@gmail.com wrote:
>On 16-03-02 05:22 AM, Jiri Pirko wrote:
>> Wed, Mar 02, 2016 at 12:14:39PM CET, gerlitz.or@gmail.com wrote:
>>> On Tue, Mar 1, 2016 at 7:01 PM, Jiri Pirko <jiri@resnulli.us> wrote:
>>>> Tue, Mar 01, 2016 at 05:49:27PM CET, amir@vadai.me wrote:
>>>>> On Tue, Mar 01, 2016 at 03:47:19PM +0100, Jiri Pirko wrote:
>>>>>> Tue, Mar 01, 2016 at 03:24:43PM CET, amir@vadai.me wrote:
>>>>>>> This patch is based on a patch made by John Fastabend.
>>>
>>>>>>> It adds support for offloading cls_flower.
>>>>>>> A filter that is offloaded successfully by hardware, will not be added to
>>>>>>> the hashtable and won't be processed by software.
>>>
>>>>>> That is wrong. User should explicitly specify to not include rule into sw
>>>>>> by SKIP_KERNEL flag (does not exist now, with John's recent patch we'll
>>>>>> have only SKIP_HW). Please add that in this patchset.
>>>
>>>>> Why? If a rule is offloaded, why would the user want to reprocess it by software?
>>>
>>>>> If the user use SKIP_HW, it will be processed by SW. Else, the user
>>>>> would want it to be processed by HW or fallback to SW. I don't
>>>>> understand in which case the user would like to have it done twice.
>>>
>>>> For example if you turn on the offloading by unsetting NETIF_F_HW_TC.
>>>> Or if someone inserts skbs into rx path directly, for example pktgen.
>>>> We need SKIP_KERNEL to be set by user, not implicit.
>>>
>>> As discussed in netdev, we want to have three modes for TC offloads
>>>
>>> 1. SW only
>>> 2. HW only (and err if can't)
>>> 3. HW and if not supported fallback to SW
>>>
>>> Now, from your reply, I understand we want a fourth mode
>>>
>>> 4. Both (HW and SW)
>> 
>> I would perhaps do it a litte bit differently:
>> NO FLAG (default)- insert into kernel and HW now:
>> 		if NETIF_F_HW_TC is off (default)
>> 			-> push to kernel only (current behaviour)
>> 		if NETIF_F_HW_TC is on AND push to HW fails
>> 			-> return error
>> SKIP_HW - flag to tell kernel not to insert into HW
>> SKIP_SW - flag to tell kernel not to insert into kernel
>> 
>> to achieve hw only, user has to turn on the NETIF_F_HW_TC and
>> pass SKIP_SW flag.
>> 
>
>The modes Jiri describes here is exactly how I planned to build
>this. And at the moment the only one we are missing is SKIP_HW
>which I'm reworking now and should have in a few days.
>
>To resolve the error handling if the rule is SKIP_HW or NO_FLAG
>an error will be thrown if it can not be applied to software.
>Notice if an error happens on the software insert with NO_FLAG then
>the hardware insert is not attempted either. With SKIP_SW I will
>throw an error if the hardware insert fails because there is
>no software fallback in this mode.
>
>The only mode I haven't looked at doing is
>
>	3. HW and if not supported fallback to SW
>
>I'm not sure I have a use case for it at the moment. It is sufficient
>for me to just do a SKIP_SW command followed by a SKIP_HW command
>if needed. I guess someone else could implement it if they really need
>it.

I'd let user to resolve this as you described.
diff mbox

Patch

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e52077f..0fd329a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -785,6 +785,7 @@  typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
 enum {
 	TC_SETUP_MQPRIO,
 	TC_SETUP_CLSU32,
+	TC_SETUP_CLSFLOWER,
 };
 
 struct tc_cls_u32_offload;
@@ -794,6 +795,7 @@  struct tc_to_netdev {
 	union {
 		u8 tc;
 		struct tc_cls_u32_offload *cls_u32;
+		struct tc_cls_flower_offload *cls_flower;
 	};
 };
 
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index bea14ee..beb2ee1 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -409,4 +409,18 @@  static inline bool tc_should_offload(struct net_device *dev, u32 flags)
 	return true;
 }
 
+enum {
+	TC_CLSFLOWER_REPLACE,
+	TC_CLSFLOWER_DESTROY,
+};
+
+struct tc_cls_flower_offload {
+	int command;
+	u64 cookie;
+	struct flow_dissector *dissector;
+	struct fl_flow_key *mask;
+	struct fl_flow_key *key;
+	struct tcf_exts *exts;
+};
+
 #endif
diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
index 9874f568..c43c5f7 100644
--- a/include/uapi/linux/pkt_cls.h
+++ b/include/uapi/linux/pkt_cls.h
@@ -417,6 +417,8 @@  enum {
 	TCA_FLOWER_KEY_TCP_DST,		/* be16 */
 	TCA_FLOWER_KEY_UDP_SRC,		/* be16 */
 	TCA_FLOWER_KEY_UDP_DST,		/* be16 */
+
+	TCA_FLOWER_FLAGS,
 	__TCA_FLOWER_MAX,
 };
 
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 95b0212..e599bea 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -165,6 +165,53 @@  static void fl_destroy_filter(struct rcu_head *head)
 	kfree(f);
 }
 
+static int fl_hw_destroy_filter(struct tcf_proto *tp, u64 cookie)
+{
+	struct net_device *dev = tp->q->dev_queue->dev;
+	struct tc_cls_flower_offload offload = {0};
+	struct tc_to_netdev tc;
+
+	if (!tc_should_offload(dev, 0))
+		return -ENOTSUPP;
+
+	offload.command = TC_CLSFLOWER_DESTROY;
+	offload.cookie = cookie;
+
+	tc.type = TC_SETUP_CLSFLOWER;
+	tc.cls_flower = &offload;
+
+	return dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
+					     &tc);
+}
+
+static int fl_hw_replace_filter(struct tcf_proto *tp,
+				struct flow_dissector *dissector,
+				struct fl_flow_key *mask,
+				struct fl_flow_key *key,
+				struct tcf_exts *actions,
+				u64 cookie, u32 flags)
+{
+	struct net_device *dev = tp->q->dev_queue->dev;
+	struct tc_cls_flower_offload offload = {0};
+	struct tc_to_netdev tc;
+
+	if (!tc_should_offload(dev, flags))
+		return -ENOTSUPP;
+
+	offload.command = TC_CLSFLOWER_REPLACE;
+	offload.cookie = cookie;
+	offload.dissector = dissector;
+	offload.mask = mask;
+	offload.key = key;
+	offload.exts = actions;
+
+	tc.type = TC_SETUP_CLSFLOWER;
+	tc.cls_flower = &offload;
+
+	return dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
+					     &tc);
+}
+
 static bool fl_destroy(struct tcf_proto *tp, bool force)
 {
 	struct cls_fl_head *head = rtnl_dereference(tp->root);
@@ -174,6 +221,7 @@  static bool fl_destroy(struct tcf_proto *tp, bool force)
 		return false;
 
 	list_for_each_entry_safe(f, next, &head->filters, list) {
+		fl_hw_destroy_filter(tp, (u64)f);
 		list_del_rcu(&f->list);
 		call_rcu(&f->rcu, fl_destroy_filter);
 	}
@@ -459,6 +507,7 @@  static int fl_change(struct net *net, struct sk_buff *in_skb,
 	struct cls_fl_filter *fnew;
 	struct nlattr *tb[TCA_FLOWER_MAX + 1];
 	struct fl_flow_mask mask = {};
+	u32 flags = 0;
 	int err;
 
 	if (!tca[TCA_OPTIONS])
@@ -494,13 +543,28 @@  static int fl_change(struct net *net, struct sk_buff *in_skb,
 	if (err)
 		goto errout;
 
-	err = rhashtable_insert_fast(&head->ht, &fnew->ht_node,
-				     head->ht_params);
-	if (err)
-		goto errout;
-	if (fold)
+	if (tb[TCA_FLOWER_FLAGS])
+		flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
+
+	err = fl_hw_replace_filter(tp,
+				   &head->dissector,
+				   &mask.key,
+				   &fnew->key,
+				   &fnew->exts,
+				   (u64)fnew,
+				   flags);
+	if (err) {
+		err = rhashtable_insert_fast(&head->ht, &fnew->ht_node,
+					     head->ht_params);
+		if (err)
+			goto errout;
+	}
+
+	if (fold) {
 		rhashtable_remove_fast(&head->ht, &fold->ht_node,
 				       head->ht_params);
+		fl_hw_destroy_filter(tp, (u64)fold);
+	}
 
 	*arg = (unsigned long) fnew;
 
@@ -527,6 +591,7 @@  static int fl_delete(struct tcf_proto *tp, unsigned long arg)
 	rhashtable_remove_fast(&head->ht, &f->ht_node,
 			       head->ht_params);
 	list_del_rcu(&f->list);
+	fl_hw_destroy_filter(tp, (u64)f);
 	tcf_unbind_filter(tp, &f->res);
 	call_rcu(&f->rcu, fl_destroy_filter);
 	return 0;