@@ -431,7 +431,8 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
static int fl_hw_replace_filter(struct tcf_proto *tp,
struct cls_fl_filter *f, bool rtnl_held,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ unsigned long cookie)
{
struct tcf_block *block = tp->chain->block;
struct flow_cls_offload cls_flower = {};
@@ -444,7 +445,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack);
cls_flower.command = FLOW_CLS_REPLACE;
- cls_flower.cookie = (unsigned long) f;
+ cls_flower.cookie = cookie;
cls_flower.rule->match.dissector = &f->mask->dissector;
cls_flower.rule->match.mask = &f->mask->key;
cls_flower.rule->match.key = &f->mkey;
@@ -2024,11 +2025,25 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
fl_init_unmasked_key_dissector(&fnew->unmasked_key_dissector);
err = fl_ht_insert_unique(fnew, fold, &in_ht);
- if (err)
+ if (err) {
+ /* It is possible Hardware lost the flow even though TC has it,
+ * and flow miss in hardware causes controller to offload flow again.
+ */
+ if (err == -EEXIST && !tc_skip_hw(fnew->flags)) {
+ struct cls_fl_filter *f =
+ __fl_lookup(fnew->mask, &fnew->mkey);
+
+ if (f)
+ fl_hw_replace_filter(tp, fnew, rtnl_held,
+ extack,
+ (unsigned long)(f));
+ }
goto errout_mask;
+ }
if (!tc_skip_hw(fnew->flags)) {
- err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack);
+ err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack,
+ (unsigned long)fnew);
if (err)
goto errout_ht;
}