On Thu 14 Feb 2019 at 20:34, Stefano Brivio <sbri...@redhat.com> wrote: > On Thu, 14 Feb 2019 09:47:03 +0200 > Vlad Buslov <vla...@mellanox.com> wrote: > >> +static struct cls_fl_filter *fl_get_next_filter(struct tcf_proto *tp, >> + unsigned long *handle) >> +{ >> + struct cls_fl_head *head = fl_head_dereference(tp); >> + struct cls_fl_filter *f; >> + >> + rcu_read_lock(); >> + /* don't return filters that are being deleted */ >> + while ((f = idr_get_next_ul(&head->handle_idr, >> + handle)) != NULL && >> + !refcount_inc_not_zero(&f->refcnt)) >> + ++(*handle); > > This... hurts :) What about: > > while ((f = idr_get_next_ul(&head->handle_idr, &handle))) { > if (refcount_inc_not_zero(&f->refcnt)) > break; > ++(*handle); > } > > ?
I prefer to avoid using value of assignment as boolean and non-structured jumps, when possible. In this case it seems OK either way, but how about: for (f = idr_get_next_ul(&head->handle_idr, handle); f && !refcount_inc_not_zero(&f->refcnt); f = idr_get_next_ul(&head->handle_idr, handle)) ++(*handle); > >> + rcu_read_unlock(); >> + >> + return f; >> +} >> + >> static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, >> struct netlink_ext_ack *extack) >> { >> @@ -456,10 +503,7 @@ static bool __fl_delete(struct tcf_proto *tp, struct >> cls_fl_filter *f, >> if (!tc_skip_hw(f->flags)) >> fl_hw_destroy_filter(tp, f, extack); >> tcf_unbind_filter(tp, &f->res); >> - if (async) >> - tcf_queue_work(&f->rwork, fl_destroy_filter_work); >> - else >> - __fl_destroy_filter(f); >> + __fl_put(f); >> >> return last; >> } >> @@ -494,11 +538,18 @@ static void fl_destroy(struct tcf_proto *tp, bool >> rtnl_held, >> tcf_queue_work(&head->rwork, fl_destroy_sleepable); >> } >> >> +static void fl_put(struct tcf_proto *tp, void *arg) >> +{ >> + struct cls_fl_filter *f = arg; >> + >> + __fl_put(f); >> +} >> + >> static void *fl_get(struct tcf_proto *tp, u32 handle) >> { >> struct cls_fl_head *head = fl_head_dereference(tp); >> >> - return idr_find(&head->handle_idr, handle); >> + return __fl_get(head, handle); >> } >> >> static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { >> @@ -1321,12 +1372,16 @@ static int fl_change(struct net *net, struct sk_buff >> *in_skb, >> struct nlattr **tb; >> int err; >> >> - if (!tca[TCA_OPTIONS]) >> - return -EINVAL; >> + if (!tca[TCA_OPTIONS]) { >> + err = -EINVAL; >> + goto errout_fold; >> + } >> >> mask = kzalloc(sizeof(struct fl_flow_mask), GFP_KERNEL); >> - if (!mask) >> - return -ENOBUFS; >> + if (!mask) { >> + err = -ENOBUFS; >> + goto errout_fold; >> + } >> >> tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); >> if (!tb) { >> @@ -1349,6 +1404,7 @@ static int fl_change(struct net *net, struct sk_buff >> *in_skb, >> err = -ENOBUFS; >> goto errout_tb; >> } >> + refcount_set(&fnew->refcnt, 1); >> >> err = tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0); >> if (err < 0) >> @@ -1381,6 +1437,7 @@ static int fl_change(struct net *net, struct sk_buff >> *in_skb, >> if (!tc_in_hw(fnew->flags)) >> fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW; >> >> + refcount_inc(&fnew->refcnt); > > I guess I'm not getting the semantics but... why is it 2 now? As soon as fnew is inserted into head->handle_idr (one reference), it becomes accessible to concurrent users, which means that it can be deleted at any time. However, tp->change() returns a reference to newly created filter to cls_api by assigning "arg" parameter to it (second reference). After tp->change() returns, cls API continues to use fnew and releases it with tfilter_put() when finished.