Replace call_rcu() with synchronize_rcu(), except in flow_destroy() we have to use list_splice_init_rcu().
Reported-by: Chris Mi <chr...@mellanox.com> Cc: Daniel Borkmann <dan...@iogearbox.net> 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_flow.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 2a3a60ec5b86..c33643289fa9 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -35,7 +35,6 @@ struct flow_head { struct list_head filters; - struct rcu_head rcu; }; struct flow_filter { @@ -57,7 +56,6 @@ struct flow_filter { u32 divisor; u32 baseclass; u32 hashrnd; - struct rcu_head rcu; }; static inline u32 addr_fold(void *addr) @@ -369,10 +367,8 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, }; -static void flow_destroy_filter(struct rcu_head *head) +static void flow_destroy_filter(struct flow_filter *f) { - struct flow_filter *f = container_of(head, struct flow_filter, rcu); - del_timer_sync(&f->perturb_timer); tcf_exts_destroy(&f->exts); tcf_em_tree_destroy(&f->ematches); @@ -539,8 +535,10 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, *arg = fnew; - if (fold) - call_rcu(&fold->rcu, flow_destroy_filter); + if (fold) { + synchronize_rcu(); + flow_destroy_filter(fold); + } return 0; err2: @@ -557,7 +555,8 @@ static int flow_delete(struct tcf_proto *tp, void *arg, bool *last) struct flow_filter *f = arg; list_del_rcu(&f->list); - call_rcu(&f->rcu, flow_destroy_filter); + synchronize_rcu(); + flow_destroy_filter(f); *last = list_empty(&head->filters); return 0; } @@ -578,12 +577,15 @@ static void flow_destroy(struct tcf_proto *tp) { struct flow_head *head = rtnl_dereference(tp->root); struct flow_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) { list_del_rcu(&f->list); - call_rcu(&f->rcu, flow_destroy_filter); + flow_destroy_filter(f); } - kfree_rcu(head, rcu); + kfree(head); } static void *flow_get(struct tcf_proto *tp, u32 handle) -- 2.13.0