Merge branch 'for-linus/2639/i2c-1' of git://git.fluff.org/bjdooks/linux
[pandora-kernel.git] / lib / plist.c
index 1471988..0ae7e64 100644 (file)
@@ -28,6 +28,8 @@
 
 #ifdef CONFIG_DEBUG_PI_LIST
 
+static struct plist_head test_head;
+
 static void plist_check_prev_next(struct list_head *t, struct list_head *p,
                                  struct list_head *n)
 {
@@ -54,12 +56,13 @@ static void plist_check_list(struct list_head *top)
 
 static void plist_check_head(struct plist_head *head)
 {
-       WARN_ON(!head->rawlock && !head->spinlock);
+       WARN_ON(head != &test_head && !head->rawlock && !head->spinlock);
        if (head->rawlock)
                WARN_ON_SMP(!raw_spin_is_locked(head->rawlock));
        if (head->spinlock)
                WARN_ON_SMP(!spin_is_locked(head->spinlock));
-       plist_check_list(&head->prio_list);
+       if (!plist_head_empty(head))
+               plist_check_list(&plist_first(head)->prio_list);
        plist_check_list(&head->node_list);
 }
 
@@ -75,25 +78,33 @@ static void plist_check_head(struct plist_head *head)
  */
 void plist_add(struct plist_node *node, struct plist_head *head)
 {
-       struct plist_node *iter;
+       struct plist_node *first, *iter, *prev = NULL;
+       struct list_head *node_next = &head->node_list;
 
        plist_check_head(head);
        WARN_ON(!plist_node_empty(node));
+       WARN_ON(!list_empty(&node->prio_list));
+
+       if (plist_head_empty(head))
+               goto ins_node;
 
-       list_for_each_entry(iter, &head->prio_list, plist.prio_list) {
-               if (node->prio < iter->prio)
-                       goto lt_prio;
-               else if (node->prio == iter->prio) {
-                       iter = list_entry(iter->plist.prio_list.next,
-                                       struct plist_node, plist.prio_list);
-                       goto eq_prio;
+       first = iter = plist_first(head);
+
+       do {
+               if (node->prio < iter->prio) {
+                       node_next = &iter->node_list;
+                       break;
                }
-       }
 
-lt_prio:
-       list_add_tail(&node->plist.prio_list, &iter->plist.prio_list);
-eq_prio:
-       list_add_tail(&node->plist.node_list, &iter->plist.node_list);
+               prev = iter;
+               iter = list_entry(iter->prio_list.next,
+                               struct plist_node, prio_list);
+       } while (iter != first);
+
+       if (!prev || prev->prio != node->prio)
+               list_add_tail(&node->prio_list, &iter->prio_list);
+ins_node:
+       list_add_tail(&node->node_list, node_next);
 
        plist_check_head(head);
 }
@@ -108,14 +119,98 @@ void plist_del(struct plist_node *node, struct plist_head *head)
 {
        plist_check_head(head);
 
-       if (!list_empty(&node->plist.prio_list)) {
-               struct plist_node *next = plist_first(&node->plist);
+       if (!list_empty(&node->prio_list)) {
+               if (node->node_list.next != &head->node_list) {
+                       struct plist_node *next;
+
+                       next = list_entry(node->node_list.next,
+                                       struct plist_node, node_list);
 
-               list_move_tail(&next->plist.prio_list, &node->plist.prio_list);
-               list_del_init(&node->plist.prio_list);
+                       /* add the next plist_node into prio_list */
+                       if (list_empty(&next->prio_list))
+                               list_add(&next->prio_list, &node->prio_list);
+               }
+               list_del_init(&node->prio_list);
        }
 
-       list_del_init(&node->plist.node_list);
+       list_del_init(&node->node_list);
 
        plist_check_head(head);
 }
+
+#ifdef CONFIG_DEBUG_PI_LIST
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+static struct plist_node __initdata test_node[241];
+
+static void __init plist_test_check(int nr_expect)
+{
+       struct plist_node *first, *prio_pos, *node_pos;
+
+       if (plist_head_empty(&test_head)) {
+               BUG_ON(nr_expect != 0);
+               return;
+       }
+
+       prio_pos = first = plist_first(&test_head);
+       plist_for_each(node_pos, &test_head) {
+               if (nr_expect-- < 0)
+                       break;
+               if (node_pos == first)
+                       continue;
+               if (node_pos->prio == prio_pos->prio) {
+                       BUG_ON(!list_empty(&node_pos->prio_list));
+                       continue;
+               }
+
+               BUG_ON(prio_pos->prio > node_pos->prio);
+               BUG_ON(prio_pos->prio_list.next != &node_pos->prio_list);
+               prio_pos = node_pos;
+       }
+
+       BUG_ON(nr_expect != 0);
+       BUG_ON(prio_pos->prio_list.next != &first->prio_list);
+}
+
+static int  __init plist_test(void)
+{
+       int nr_expect = 0, i, loop;
+       unsigned int r = local_clock();
+
+       printk(KERN_INFO "start plist test\n");
+       plist_head_init(&test_head, NULL);
+       for (i = 0; i < ARRAY_SIZE(test_node); i++)
+               plist_node_init(test_node + i, 0);
+
+       for (loop = 0; loop < 1000; loop++) {
+               r = r * 193939 % 47629;
+               i = r % ARRAY_SIZE(test_node);
+               if (plist_node_empty(test_node + i)) {
+                       r = r * 193939 % 47629;
+                       test_node[i].prio = r % 99;
+                       plist_add(test_node + i, &test_head);
+                       nr_expect++;
+               } else {
+                       plist_del(test_node + i, &test_head);
+                       nr_expect--;
+               }
+               plist_test_check(nr_expect);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(test_node); i++) {
+               if (plist_node_empty(test_node + i))
+                       continue;
+               plist_del(test_node + i, &test_head);
+               nr_expect--;
+               plist_test_check(nr_expect);
+       }
+
+       printk(KERN_INFO "end plist test\n");
+       return 0;
+}
+
+module_init(plist_test);
+
+#endif