On Thu, 05 Mar 2020 10:45:10 -0800
Add a put callback for cls_route4_ops in an attempt to pair with get.
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -48,6 +48,8 @@ struct route4_filter {
u32 id;
int iif;
+ atomic_t ref;
+
struct tcf_result res;
struct tcf_exts exts;
u32 handle;
@@ -56,6 +58,12 @@ struct route4_filter {
struct rcu_work rwork;
};
+static inline void route4_get_filter(struct route4_filter *filter)
+{
+ if (filter)
+ atomic_inc(&filter->ref);
+}
+
#define ROUTE4_FAILURE ((struct route4_filter *)(-1L))
static inline int route4_fastmap_hash(u32 id, int iif)
@@ -232,8 +240,10 @@ static void *route4_get(struct tcf_proto
for (f = rtnl_dereference(b->ht[h2]);
f;
f = rtnl_dereference(f->next))
- if (f->handle == handle)
+ if (f->handle == handle) {
+ route4_get_filter(f);
return f;
+ }
}
return NULL;
}
@@ -267,9 +277,14 @@ static void route4_delete_filter_work(st
rtnl_unlock();
}
-static void route4_queue_work(struct route4_filter *f)
+static inline void route4_put_filter(struct tcf_proto *tp,
+ struct route4_filter *filter)
{
- tcf_queue_work(&f->rwork, route4_delete_filter_work);
+ if (!filter || !atomic_dec_and_test(&filter->ref))
+ return;
+ tcf_unbind_filter(tp, &filter->res);
+ tcf_exts_get_net(&filter->exts);
+ tcf_queue_work(&filter->rwork, route4_delete_filter_work);
}
static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
@@ -294,11 +309,7 @@ static void route4_destroy(struct tcf_pr
next = rtnl_dereference(f->next);
RCU_INIT_POINTER(b->ht[h2], next);
- tcf_unbind_filter(tp, &f->res);
- if (tcf_exts_get_net(&f->exts))
- route4_queue_work(f);
- else
- __route4_delete_filter(f);
+ route4_put_filter(tp, f);
}
}
RCU_INIT_POINTER(head->table[h1], NULL);
@@ -338,10 +349,7 @@ static int route4_delete(struct tcf_prot
*/
route4_reset_fastmap(head);
- /* Delete it */
- tcf_unbind_filter(tp, &f->res);
- tcf_exts_get_net(&f->exts);
- tcf_queue_work(&f->rwork, route4_delete_filter_work);
+ route4_put_filter(tp, f);
/* Strip RTNL protected tree */
for (i = 0; i <= 32; i++) {
@@ -523,6 +531,8 @@ static int route4_change(struct net *net
break;
tcf_block_netif_keep_dst(tp->chain->block);
+ route4_get_filter(f); /* for adding filter in hash table */
+ route4_get_filter(f); /* for putting fold as filter will be put by caller */
rcu_assign_pointer(f->next, f1);
rcu_assign_pointer(*fp, f);
@@ -536,6 +546,7 @@ static int route4_change(struct net *net
fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
if (pfp == f) {
*fp = f->next;
+ route4_put_filter(tp, f);
break;
}
}
@@ -544,11 +555,7 @@ static int route4_change(struct net *net
route4_reset_fastmap(head);
*arg = f;
- if (fold) {
- tcf_unbind_filter(tp, &fold->res);
- tcf_exts_get_net(&fold->exts);
- tcf_queue_work(&fold->rwork, route4_delete_filter_work);
- }
+ route4_put_filter(tp, fold);
return 0;
errout:
@@ -649,12 +656,18 @@ static void route4_bind_class(void *fh,
f->res.class = cl;
}
+static void route4_put(struct tcf_proto *tp, void *f)
+{
+ route4_put_filter(tp, (struct route4_filter *) f);
+}
+
static struct tcf_proto_ops cls_route4_ops __read_mostly = {
.kind = "route",
.classify = route4_classify,
.init = route4_init,
.destroy = route4_destroy,
.get = route4_get,
+ .put = route4_put,
.change = route4_change,
.delete = route4_delete,
.walk = route4_walk,
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -1964,6 +1964,7 @@ static int tc_new_tfilter(struct sk_buff
return -EPERM;
replay:
+ fh = NULL;
tp_created = 0;
err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
@@ -2110,7 +2111,6 @@ replay:
goto errout;
}
} else if (n->nlmsg_flags & NLM_F_EXCL) {
- tfilter_put(tp, fh);
NL_SET_ERR_MSG(extack, "Filter already exists");
err = -EEXIST;
goto errout;
@@ -2128,13 +2128,13 @@ replay:
if (err == 0) {
tfilter_notify(net, skb, n, tp, block, q, parent, fh,
RTM_NEWTFILTER, false, rtnl_held);
- tfilter_put(tp, fh);
/* q pointer is NULL for shared blocks */
if (q)
q->flags &= ~TCQ_F_CAN_BYPASS;
}
errout:
+ tfilter_put(tp, fh);
if (err && tp_created)
tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
errout_tp: