Revert "mm/z3fold.c: fix race between migration and destruction"
[linux-2.6-microblaze.git] / mm / backing-dev.c
index e8e8915..d9daa3e 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include <linux/wait.h>
+#include <linux/rbtree.h>
 #include <linux/backing-dev.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
@@ -22,10 +23,12 @@ EXPORT_SYMBOL_GPL(noop_backing_dev_info);
 static struct class *bdi_class;
 
 /*
- * bdi_lock protects updates to bdi_list. bdi_list has RCU reader side
- * locking.
+ * bdi_lock protects bdi_tree and updates to bdi_list. bdi_list has RCU
+ * reader side locking.
  */
 DEFINE_SPINLOCK(bdi_lock);
+static u64 bdi_id_cursor;
+static struct rb_root bdi_tree = RB_ROOT;
 LIST_HEAD(bdi_list);
 
 /* bdi_wq serves all asynchronous writeback tasks */
@@ -615,13 +618,12 @@ out_put:
 }
 
 /**
- * wb_get_create - get wb for a given memcg, create if necessary
+ * wb_get_lookup - get wb for a given memcg
  * @bdi: target bdi
  * @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref)
- * @gfp: allocation mask to use
  *
- * Try to get the wb for @memcg_css on @bdi.  If it doesn't exist, try to
- * create one.  The returned wb has its refcount incremented.
+ * Try to get the wb for @memcg_css on @bdi.  The returned wb has its
+ * refcount incremented.
  *
  * This function uses css_get() on @memcg_css and thus expects its refcnt
  * to be positive on invocation.  IOW, rcu_read_lock() protection on
@@ -638,6 +640,39 @@ out_put:
  * each lookup.  On mismatch, the existing wb is discarded and a new one is
  * created.
  */
+struct bdi_writeback *wb_get_lookup(struct backing_dev_info *bdi,
+                                   struct cgroup_subsys_state *memcg_css)
+{
+       struct bdi_writeback *wb;
+
+       if (!memcg_css->parent)
+               return &bdi->wb;
+
+       rcu_read_lock();
+       wb = radix_tree_lookup(&bdi->cgwb_tree, memcg_css->id);
+       if (wb) {
+               struct cgroup_subsys_state *blkcg_css;
+
+               /* see whether the blkcg association has changed */
+               blkcg_css = cgroup_get_e_css(memcg_css->cgroup, &io_cgrp_subsys);
+               if (unlikely(wb->blkcg_css != blkcg_css || !wb_tryget(wb)))
+                       wb = NULL;
+               css_put(blkcg_css);
+       }
+       rcu_read_unlock();
+
+       return wb;
+}
+
+/**
+ * wb_get_create - get wb for a given memcg, create if necessary
+ * @bdi: target bdi
+ * @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref)
+ * @gfp: allocation mask to use
+ *
+ * Try to get the wb for @memcg_css on @bdi.  If it doesn't exist, try to
+ * create one.  See wb_get_lookup() for more details.
+ */
 struct bdi_writeback *wb_get_create(struct backing_dev_info *bdi,
                                    struct cgroup_subsys_state *memcg_css,
                                    gfp_t gfp)
@@ -650,20 +685,7 @@ struct bdi_writeback *wb_get_create(struct backing_dev_info *bdi,
                return &bdi->wb;
 
        do {
-               rcu_read_lock();
-               wb = radix_tree_lookup(&bdi->cgwb_tree, memcg_css->id);
-               if (wb) {
-                       struct cgroup_subsys_state *blkcg_css;
-
-                       /* see whether the blkcg association has changed */
-                       blkcg_css = cgroup_get_e_css(memcg_css->cgroup,
-                                                    &io_cgrp_subsys);
-                       if (unlikely(wb->blkcg_css != blkcg_css ||
-                                    !wb_tryget(wb)))
-                               wb = NULL;
-                       css_put(blkcg_css);
-               }
-               rcu_read_unlock();
+               wb = wb_get_lookup(bdi, memcg_css);
        } while (!wb && !cgwb_create(bdi, memcg_css, gfp));
 
        return wb;
@@ -859,9 +881,58 @@ struct backing_dev_info *bdi_alloc_node(gfp_t gfp_mask, int node_id)
 }
 EXPORT_SYMBOL(bdi_alloc_node);
 
+static struct rb_node **bdi_lookup_rb_node(u64 id, struct rb_node **parentp)
+{
+       struct rb_node **p = &bdi_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct backing_dev_info *bdi;
+
+       lockdep_assert_held(&bdi_lock);
+
+       while (*p) {
+               parent = *p;
+               bdi = rb_entry(parent, struct backing_dev_info, rb_node);
+
+               if (bdi->id > id)
+                       p = &(*p)->rb_left;
+               else if (bdi->id < id)
+                       p = &(*p)->rb_right;
+               else
+                       break;
+       }
+
+       if (parentp)
+               *parentp = parent;
+       return p;
+}
+
+/**
+ * bdi_get_by_id - lookup and get bdi from its id
+ * @id: bdi id to lookup
+ *
+ * Find bdi matching @id and get it.  Returns NULL if the matching bdi
+ * doesn't exist or is already unregistered.
+ */
+struct backing_dev_info *bdi_get_by_id(u64 id)
+{
+       struct backing_dev_info *bdi = NULL;
+       struct rb_node **p;
+
+       spin_lock_bh(&bdi_lock);
+       p = bdi_lookup_rb_node(id, NULL);
+       if (*p) {
+               bdi = rb_entry(*p, struct backing_dev_info, rb_node);
+               bdi_get(bdi);
+       }
+       spin_unlock_bh(&bdi_lock);
+
+       return bdi;
+}
+
 int bdi_register_va(struct backing_dev_info *bdi, const char *fmt, va_list args)
 {
        struct device *dev;
+       struct rb_node *parent, **p;
 
        if (bdi->dev)   /* The driver needs to use separate queues per device */
                return 0;
@@ -877,7 +948,15 @@ int bdi_register_va(struct backing_dev_info *bdi, const char *fmt, va_list args)
        set_bit(WB_registered, &bdi->wb.state);
 
        spin_lock_bh(&bdi_lock);
+
+       bdi->id = ++bdi_id_cursor;
+
+       p = bdi_lookup_rb_node(bdi->id, &parent);
+       rb_link_node(&bdi->rb_node, parent, p);
+       rb_insert_color(&bdi->rb_node, &bdi_tree);
+
        list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
+
        spin_unlock_bh(&bdi_lock);
 
        trace_writeback_bdi_register(bdi);
@@ -918,6 +997,7 @@ EXPORT_SYMBOL(bdi_register_owner);
 static void bdi_remove_from_list(struct backing_dev_info *bdi)
 {
        spin_lock_bh(&bdi_lock);
+       rb_erase(&bdi->rb_node, &bdi_tree);
        list_del_rcu(&bdi->bdi_list);
        spin_unlock_bh(&bdi_lock);