klist: implement klist_prev()
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Mon, 27 Jul 2015 15:03:59 +0000 (18:03 +0300)
committerLee Jones <lee.jones@linaro.org>
Tue, 28 Jul 2015 07:50:42 +0000 (08:50 +0100)
klist_prev() gets the previous element in the list. It is useful to traverse
through the list in reverse order, for example, to provide LIFO (last in first
out) variant of access.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
include/linux/klist.h
lib/klist.c

index 61e5b723ae73dbb1a424665c6e8a5d0fd8f9b2a7..953f283f8451daef34283770ff3c9c0a14db7804 100644 (file)
@@ -63,6 +63,7 @@ extern void klist_iter_init(struct klist *k, struct klist_iter *i);
 extern void klist_iter_init_node(struct klist *k, struct klist_iter *i,
                                 struct klist_node *n);
 extern void klist_iter_exit(struct klist_iter *i);
+extern struct klist_node *klist_prev(struct klist_iter *i);
 extern struct klist_node *klist_next(struct klist_iter *i);
 
 #endif
index 89b485a2a58d1755850d9baa2c50a1dc58fa3a15..d74cf7a29afdb043112fee5c9ad7b37a631a9985 100644 (file)
@@ -323,6 +323,47 @@ static struct klist_node *to_klist_node(struct list_head *n)
        return container_of(n, struct klist_node, n_node);
 }
 
+/**
+ * klist_prev - Ante up prev node in list.
+ * @i: Iterator structure.
+ *
+ * First grab list lock. Decrement the reference count of the previous
+ * node, if there was one. Grab the prev node, increment its reference
+ * count, drop the lock, and return that prev node.
+ */
+struct klist_node *klist_prev(struct klist_iter *i)
+{
+       void (*put)(struct klist_node *) = i->i_klist->put;
+       struct klist_node *last = i->i_cur;
+       struct klist_node *prev;
+
+       spin_lock(&i->i_klist->k_lock);
+
+       if (last) {
+               prev = to_klist_node(last->n_node.prev);
+               if (!klist_dec_and_del(last))
+                       put = NULL;
+       } else
+               prev = to_klist_node(i->i_klist->k_list.prev);
+
+       i->i_cur = NULL;
+       while (prev != to_klist_node(&i->i_klist->k_list)) {
+               if (likely(!knode_dead(prev))) {
+                       kref_get(&prev->n_ref);
+                       i->i_cur = prev;
+                       break;
+               }
+               prev = to_klist_node(prev->n_node.prev);
+       }
+
+       spin_unlock(&i->i_klist->k_lock);
+
+       if (put && last)
+               put(last);
+       return i->i_cur;
+}
+EXPORT_SYMBOL_GPL(klist_prev);
+
 /**
  * klist_next - Ante up next node in list.
  * @i: Iterator structure.