Replace call_rcu() with synchronize_rcu(), except in fl_destroy() we have to use list_splice_init_rcu().
As a bonus, this also drops the ugly code in commit d936377414fa ("net, sched: respect rcu grace period on cls destruction"). Reported-by: Chris Mi <chr...@mellanox.com> Cc: Daniel Borkmann <dan...@iogearbox.net> Cc: Jiri Pirko <j...@resnulli.us> Cc: John Fastabend <john.fastab...@gmail.com> Cc: Jamal Hadi Salim <j...@mojatatu.com> Cc: "Paul E. McKenney" <paul...@linux.vnet.ibm.com> Signed-off-by: Cong Wang <xiyou.wangc...@gmail.com> --- net/sched/cls_flower.c | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index b480d7c792ba..ad33bd00b4f0 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -61,7 +61,6 @@ struct fl_flow_mask_range { struct fl_flow_mask { struct fl_flow_key key; struct fl_flow_mask_range range; - struct rcu_head rcu; }; struct cls_fl_head { @@ -71,10 +70,6 @@ struct cls_fl_head { bool mask_assigned; struct list_head filters; struct rhashtable_params ht_params; - union { - struct work_struct work; - struct rcu_head rcu; - }; struct idr handle_idr; }; @@ -87,7 +82,6 @@ struct cls_fl_filter { struct list_head list; u32 handle; u32 flags; - struct rcu_head rcu; struct net_device *hw_dev; }; @@ -215,10 +209,8 @@ static int fl_init(struct tcf_proto *tp) return 0; } -static void fl_destroy_filter(struct rcu_head *head) +static void fl_destroy_filter(struct cls_fl_filter *f) { - struct cls_fl_filter *f = container_of(head, struct cls_fl_filter, rcu); - tcf_exts_destroy(&f->exts); kfree(f); } @@ -305,38 +297,25 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) if (!tc_skip_hw(f->flags)) fl_hw_destroy_filter(tp, f); tcf_unbind_filter(tp, &f->res); - call_rcu(&f->rcu, fl_destroy_filter); -} - -static void fl_destroy_sleepable(struct work_struct *work) -{ - struct cls_fl_head *head = container_of(work, struct cls_fl_head, - work); - if (head->mask_assigned) - rhashtable_destroy(&head->ht); - kfree(head); - module_put(THIS_MODULE); -} - -static void fl_destroy_rcu(struct rcu_head *rcu) -{ - struct cls_fl_head *head = container_of(rcu, struct cls_fl_head, rcu); - - INIT_WORK(&head->work, fl_destroy_sleepable); - schedule_work(&head->work); } static void fl_destroy(struct tcf_proto *tp) { struct cls_fl_head *head = rtnl_dereference(tp->root); struct cls_fl_filter *f, *next; + LIST_HEAD(local); + + list_splice_init_rcu(&head->filters, &local, synchronize_rcu); - list_for_each_entry_safe(f, next, &head->filters, list) + list_for_each_entry_safe(f, next, &local, list) { __fl_delete(tp, f); + fl_destroy_filter(f); + } idr_destroy(&head->handle_idr); - __module_get(THIS_MODULE); - call_rcu(&head->rcu, fl_destroy_rcu); + if (head->mask_assigned) + rhashtable_destroy(&head->ht); + kfree(head); } static void *fl_get(struct tcf_proto *tp, u32 handle) @@ -975,7 +954,8 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, idr_replace_ext(&head->handle_idr, fnew, fnew->handle); list_replace_rcu(&fold->list, &fnew->list); tcf_unbind_filter(tp, &fold->res); - call_rcu(&fold->rcu, fl_destroy_filter); + synchronize_rcu(); + fl_destroy_filter(fold); } else { list_add_tail_rcu(&fnew->list, &head->filters); } @@ -1003,6 +983,8 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last) rhashtable_remove_fast(&head->ht, &f->ht_node, head->ht_params); __fl_delete(tp, f); + synchronize_rcu(); + fl_destroy_filter(f); *last = list_empty(&head->filters); return 0; } -- 2.13.0