Commit 9cb36fae authored by Victor Nogueira's avatar Victor Nogueira Committed by David S. Miller
Browse files

net: sched: cls_u32: Undo tcf_bind_filter if u32_replace_hw_knode



When u32_replace_hw_knode fails, we need to undo the tcf_bind_filter
operation done at u32_set_parms.

Fixes: d34e3e18 ("net: cls_u32: Add support for skip-sw flag to tc u32 classifier.")
Signed-off-by: default avatarVictor Nogueira <victor@mojatatu.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: default avatarPedro Tammela <pctammela@mojatatu.com>
Reviewed-by: default avatarSimon Horman <simon.horman@corigine.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b3d0e048
Loading
Loading
Loading
Loading
+30 −11
Original line number Original line Diff line number Diff line
@@ -712,8 +712,23 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
	[TCA_U32_FLAGS]		= { .type = NLA_U32 },
	[TCA_U32_FLAGS]		= { .type = NLA_U32 },
};
};


static void u32_unbind_filter(struct tcf_proto *tp, struct tc_u_knode *n,
			      struct nlattr **tb)
{
	if (tb[TCA_U32_CLASSID])
		tcf_unbind_filter(tp, &n->res);
}

static void u32_bind_filter(struct tcf_proto *tp, struct tc_u_knode *n,
			    unsigned long base, struct nlattr **tb)
{
	if (tb[TCA_U32_CLASSID]) {
		n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
		tcf_bind_filter(tp, &n->res, base);
	}
}

static int u32_set_parms(struct net *net, struct tcf_proto *tp,
static int u32_set_parms(struct net *net, struct tcf_proto *tp,
			 unsigned long base,
			 struct tc_u_knode *n, struct nlattr **tb,
			 struct tc_u_knode *n, struct nlattr **tb,
			 struct nlattr *est, u32 flags, u32 fl_flags,
			 struct nlattr *est, u32 flags, u32 fl_flags,
			 struct netlink_ext_ack *extack)
			 struct netlink_ext_ack *extack)
@@ -760,10 +775,6 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
		if (ht_old)
		if (ht_old)
			ht_old->refcnt--;
			ht_old->refcnt--;
	}
	}
	if (tb[TCA_U32_CLASSID]) {
		n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
		tcf_bind_filter(tp, &n->res, base);
	}


	if (ifindex >= 0)
	if (ifindex >= 0)
		n->ifindex = ifindex;
		n->ifindex = ifindex;
@@ -903,17 +914,20 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
		if (!new)
		if (!new)
			return -ENOMEM;
			return -ENOMEM;


		err = u32_set_parms(net, tp, base, new, tb,
		err = u32_set_parms(net, tp, new, tb, tca[TCA_RATE],
				    tca[TCA_RATE], flags, new->flags,
				    flags, new->flags, extack);
				    extack);


		if (err) {
		if (err) {
			__u32_destroy_key(new);
			__u32_destroy_key(new);
			return err;
			return err;
		}
		}


		u32_bind_filter(tp, new, base, tb);

		err = u32_replace_hw_knode(tp, new, flags, extack);
		err = u32_replace_hw_knode(tp, new, flags, extack);
		if (err) {
		if (err) {
			u32_unbind_filter(tp, new, tb);

			__u32_destroy_key(new);
			__u32_destroy_key(new);
			return err;
			return err;
		}
		}
@@ -1074,15 +1088,18 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
	}
	}
#endif
#endif


	err = u32_set_parms(net, tp, base, n, tb, tca[TCA_RATE],
	err = u32_set_parms(net, tp, n, tb, tca[TCA_RATE],
			    flags, n->flags, extack);
			    flags, n->flags, extack);

	u32_bind_filter(tp, n, base, tb);

	if (err == 0) {
	if (err == 0) {
		struct tc_u_knode __rcu **ins;
		struct tc_u_knode __rcu **ins;
		struct tc_u_knode *pins;
		struct tc_u_knode *pins;


		err = u32_replace_hw_knode(tp, n, flags, extack);
		err = u32_replace_hw_knode(tp, n, flags, extack);
		if (err)
		if (err)
			goto errhw;
			goto errunbind;


		if (!tc_in_hw(n->flags))
		if (!tc_in_hw(n->flags))
			n->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
			n->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
@@ -1100,7 +1117,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
		return 0;
		return 0;
	}
	}


errhw:
errunbind:
	u32_unbind_filter(tp, n, tb);

#ifdef CONFIG_CLS_U32_MARK
#ifdef CONFIG_CLS_U32_MARK
	free_percpu(n->pcpu_success);
	free_percpu(n->pcpu_success);
#endif
#endif