IB/hfi1: Improve performance of interval RB trees
authorMitko Haralanov <mitko.haralanov@intel.com>
Thu, 12 May 2016 17:23:09 +0000 (10:23 -0700)
committerDoug Ledford <dledford@redhat.com>
Fri, 13 May 2016 23:39:16 +0000 (19:39 -0400)
The interval RB tree management functions use handlers to
store user-specific callback for the various tree operations.
These handlers are put on a doubly-linked list. When a RB
tree function is called, the list is searched for the handler
of the particular tree.

The list which holds the handlers is modified very rarely - when
a handler is created and when a handler is removed. On the other
hand, it is searched very often. This a perfect usage scenario
for RCU.

The result is a much lower overhead of traversing the list as most
of the time no locking will be required.

Reviewed-by: Dean Luick <dean.luick@intel.com>
Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: Mitko Haralanov <mitko.haralanov@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/staging/rdma/hfi1/mmu_rb.c

index 2b0e91d3093dfe284aeffbe12a48df4002082073..b7a80aa1ae30601b3fdb67501ca7f5a4ba1d812e 100644 (file)
@@ -45,6 +45,7 @@
  *
  */
 #include <linux/list.h>
+#include <linux/rculist.h>
 #include <linux/mmu_notifier.h>
 #include <linux/interval_tree_generic.h>
 
@@ -97,7 +98,6 @@ static unsigned long mmu_node_last(struct mmu_rb_node *node)
 int hfi1_mmu_rb_register(struct rb_root *root, struct mmu_rb_ops *ops)
 {
        struct mmu_rb_handler *handlr;
-       unsigned long flags;
 
        if (!ops->invalidate)
                return -EINVAL;
@@ -111,9 +111,9 @@ int hfi1_mmu_rb_register(struct rb_root *root, struct mmu_rb_ops *ops)
        INIT_HLIST_NODE(&handlr->mn.hlist);
        spin_lock_init(&handlr->lock);
        handlr->mn.ops = &mn_opts;
-       spin_lock_irqsave(&mmu_rb_lock, flags);
-       list_add_tail(&handlr->list, &mmu_rb_handlers);
-       spin_unlock_irqrestore(&mmu_rb_lock, flags);
+       spin_lock(&mmu_rb_lock);
+       list_add_tail_rcu(&handlr->list, &mmu_rb_handlers);
+       spin_unlock(&mmu_rb_lock);
 
        return mmu_notifier_register(&handlr->mn, current->mm);
 }
@@ -130,9 +130,10 @@ void hfi1_mmu_rb_unregister(struct rb_root *root)
        if (current->mm)
                mmu_notifier_unregister(&handler->mn, current->mm);
 
-       spin_lock_irqsave(&mmu_rb_lock, flags);
-       list_del(&handler->list);
-       spin_unlock_irqrestore(&mmu_rb_lock, flags);
+       spin_lock(&mmu_rb_lock);
+       list_del_rcu(&handler->list);
+       spin_unlock(&mmu_rb_lock);
+       synchronize_rcu();
 
        spin_lock_irqsave(&handler->lock, flags);
        if (!RB_EMPTY_ROOT(root)) {
@@ -271,16 +272,15 @@ void hfi1_mmu_rb_remove(struct rb_root *root, struct mmu_rb_node *node)
 static struct mmu_rb_handler *find_mmu_handler(struct rb_root *root)
 {
        struct mmu_rb_handler *handler;
-       unsigned long flags;
 
-       spin_lock_irqsave(&mmu_rb_lock, flags);
-       list_for_each_entry(handler, &mmu_rb_handlers, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(handler, &mmu_rb_handlers, list) {
                if (handler->root == root)
                        goto unlock;
        }
        handler = NULL;
 unlock:
-       spin_unlock_irqrestore(&mmu_rb_lock, flags);
+       rcu_read_unlock();
        return handler;
 }