From: "Matthew Wilcox (Oracle)" <wi...@infradead.org>

Inline __fl_get() into fl_get().  Use the RCU lock explicitly for
lookups and walks instead of relying on RTNL.  The xa_lock protects us,
but remains nested under the RTNL for now.

Signed-off-by: Matthew Wilcox (Oracle) <wi...@infradead.org>
---
 net/sched/cls_flower.c | 54 ++++++++++++++++++++----------------------
 1 file changed, 26 insertions(+), 28 deletions(-)

diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 054123742e32..54026c9e9b05 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -91,7 +91,7 @@ struct cls_fl_head {
        struct list_head masks;
        struct list_head hw_filters;
        struct rcu_work rwork;
-       struct idr handle_idr;
+       struct xarray filters;
 };
 
 struct cls_fl_filter {
@@ -334,7 +334,7 @@ static int fl_init(struct tcf_proto *tp)
        INIT_LIST_HEAD_RCU(&head->masks);
        INIT_LIST_HEAD(&head->hw_filters);
        rcu_assign_pointer(tp->root, head);
-       idr_init(&head->handle_idr);
+       xa_init_flags(&head->filters, XA_FLAGS_ALLOC1);
 
        return rhashtable_init(&head->ht, &mask_ht_params);
 }
@@ -530,19 +530,6 @@ static void __fl_put(struct cls_fl_filter *f)
                __fl_destroy_filter(f);
 }
 
-static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle)
-{
-       struct cls_fl_filter *f;
-
-       rcu_read_lock();
-       f = idr_find(&head->handle_idr, handle);
-       if (f && !refcount_inc_not_zero(&f->refcnt))
-               f = NULL;
-       rcu_read_unlock();
-
-       return f;
-}
-
 static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
                       bool *last, bool rtnl_held,
                       struct netlink_ext_ack *extack)
@@ -560,7 +547,7 @@ static int __fl_delete(struct tcf_proto *tp, struct 
cls_fl_filter *f,
        f->deleted = true;
        rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
                               f->mask->filter_ht_params);
-       idr_remove(&head->handle_idr, f->handle);
+       xa_erase(&head->filters, f->handle);
        list_del_rcu(&f->list);
        spin_unlock(&tp->lock);
 
@@ -599,7 +586,7 @@ static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
                                break;
                }
        }
-       idr_destroy(&head->handle_idr);
+       xa_destroy(&head->filters);
 
        __module_get(THIS_MODULE);
        tcf_queue_work(&head->rwork, fl_destroy_sleepable);
@@ -615,8 +602,15 @@ static void fl_put(struct tcf_proto *tp, void *arg)
 static void *fl_get(struct tcf_proto *tp, u32 handle)
 {
        struct cls_fl_head *head = fl_head_dereference(tp);
+       struct cls_fl_filter *f;
+
+       rcu_read_lock();
+       f = xa_load(&head->filters, handle);
+       if (f && !refcount_inc_not_zero(&f->refcnt))
+               f = NULL;
+       rcu_read_unlock();
 
-       return __fl_get(head, handle);
+       return f;
 }
 
 static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = {
@@ -1663,7 +1657,7 @@ static int fl_change(struct net *net, struct sk_buff 
*in_skb,
                rhashtable_remove_fast(&fold->mask->ht,
                                       &fold->ht_node,
                                       fold->mask->filter_ht_params);
-               idr_replace(&head->handle_idr, fnew, fnew->handle);
+               xa_store(&head->filters, fnew->handle, fnew, 0);
                list_replace_rcu(&fold->list, &fnew->list);
                fold->deleted = true;
 
@@ -1681,8 +1675,9 @@ static int fl_change(struct net *net, struct sk_buff 
*in_skb,
        } else {
                if (handle) {
                        /* user specifies a handle and it doesn't exist */
-                       err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
-                                           handle, GFP_ATOMIC);
+                       fnew->handle = handle;
+                       err = xa_insert(&head->filters, handle, fnew,
+                                       GFP_ATOMIC);
 
                        /* Filter with specified handle was concurrently
                         * inserted after initial check in cls_api. This is not
@@ -1690,18 +1685,16 @@ static int fl_change(struct net *net, struct sk_buff 
*in_skb,
                         * message flags. Returning EAGAIN will cause cls_api to
                         * try to update concurrently inserted rule.
                         */
-                       if (err == -ENOSPC)
+                       if (err == -EBUSY)
                                err = -EAGAIN;
                } else {
-                       handle = 1;
-                       err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
-                                           INT_MAX, GFP_ATOMIC);
+                       err = xa_alloc(&head->filters, &fnew->handle, fnew,
+                                       xa_limit_31b, GFP_ATOMIC);
                }
                if (err)
                        goto errout_hw;
 
                refcount_inc(&fnew->refcnt);
-               fnew->handle = handle;
                list_add_tail_rcu(&fnew->list, &fnew->mask->filters);
                spin_unlock(&tp->lock);
        }
@@ -1755,23 +1748,28 @@ static void fl_walk(struct tcf_proto *tp, struct 
tcf_walker *arg,
                    bool rtnl_held)
 {
        struct cls_fl_head *head = fl_head_dereference(tp);
-       unsigned long id = arg->cookie, tmp;
+       unsigned long id;
        struct cls_fl_filter *f;
 
        arg->count = arg->skip;
 
-       idr_for_each_entry_continue_ul(&head->handle_idr, f, tmp, id) {
+       rcu_read_lock();
+       xa_for_each_start(&head->filters, id, f, arg->cookie) {
                /* don't return filters that are being deleted */
                if (!refcount_inc_not_zero(&f->refcnt))
                        continue;
+               rcu_read_unlock();
                if (arg->fn(tp, f, arg) < 0) {
                        __fl_put(f);
                        arg->stop = 1;
+                       rcu_read_lock();
                        break;
                }
                __fl_put(f);
                arg->count++;
+               rcu_read_lock();
        }
+       rcu_read_unlock();
        arg->cookie = id;
 }
 
-- 
2.23.0.rc1

Reply via email to