|
@@ -4,99 +4,213 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/list.h>
|
|
|
|
|
|
+#define MAX_LIST_LENGTH_BITS 20
|
|
|
+
|
|
|
+/*
|
|
|
+ * Returns a list organized in an intermediate format suited
|
|
|
+ * to chaining of merge() calls: null-terminated, no reserved or
|
|
|
+ * sentinel head node, "prev" links not maintained.
|
|
|
+ */
|
|
|
+static struct list_head *merge(void *priv,
|
|
|
+ int (*cmp)(void *priv, struct list_head *a,
|
|
|
+ struct list_head *b),
|
|
|
+ struct list_head *a, struct list_head *b)
|
|
|
+{
|
|
|
+ struct list_head head, *tail = &head;
|
|
|
+
|
|
|
+ while (a && b) {
|
|
|
+ /* if equal, take 'a' -- important for sort stability */
|
|
|
+ if ((*cmp)(priv, a, b) <= 0) {
|
|
|
+ tail->next = a;
|
|
|
+ a = a->next;
|
|
|
+ } else {
|
|
|
+ tail->next = b;
|
|
|
+ b = b->next;
|
|
|
+ }
|
|
|
+ tail = tail->next;
|
|
|
+ }
|
|
|
+ tail->next = a?:b;
|
|
|
+ return head.next;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Combine final list merge with restoration of standard doubly-linked
|
|
|
+ * list structure. This approach duplicates code from merge(), but
|
|
|
+ * runs faster than the tidier alternatives of either a separate final
|
|
|
+ * prev-link restoration pass, or maintaining the prev links
|
|
|
+ * throughout.
|
|
|
+ */
|
|
|
+static void merge_and_restore_back_links(void *priv,
|
|
|
+ int (*cmp)(void *priv, struct list_head *a,
|
|
|
+ struct list_head *b),
|
|
|
+ struct list_head *head,
|
|
|
+ struct list_head *a, struct list_head *b)
|
|
|
+{
|
|
|
+ struct list_head *tail = head;
|
|
|
+
|
|
|
+ while (a && b) {
|
|
|
+ /* if equal, take 'a' -- important for sort stability */
|
|
|
+ if ((*cmp)(priv, a, b) <= 0) {
|
|
|
+ tail->next = a;
|
|
|
+ a->prev = tail;
|
|
|
+ a = a->next;
|
|
|
+ } else {
|
|
|
+ tail->next = b;
|
|
|
+ b->prev = tail;
|
|
|
+ b = b->next;
|
|
|
+ }
|
|
|
+ tail = tail->next;
|
|
|
+ }
|
|
|
+ tail->next = a ? : b;
|
|
|
+
|
|
|
+ do {
|
|
|
+ /*
|
|
|
+ * In worst cases this loop may run many iterations.
|
|
|
+ * Continue callbacks to the client even though no
|
|
|
+ * element comparison is needed, so the client's cmp()
|
|
|
+ * routine can invoke cond_resched() periodically.
|
|
|
+ */
|
|
|
+ (*cmp)(priv, tail, tail);
|
|
|
+
|
|
|
+ tail->next->prev = tail;
|
|
|
+ tail = tail->next;
|
|
|
+ } while (tail->next);
|
|
|
+
|
|
|
+ tail->next = head;
|
|
|
+ head->prev = tail;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* list_sort - sort a list.
|
|
|
* @priv: private data, passed to @cmp
|
|
|
* @head: the list to sort
|
|
|
* @cmp: the elements comparison function
|
|
|
*
|
|
|
- * This function has been implemented by Mark J Roberts <mjr@znex.org>. It
|
|
|
- * implements "merge sort" which has O(nlog(n)) complexity. The list is sorted
|
|
|
- * in ascending order.
|
|
|
+ * This function implements "merge sort" which has O(nlog(n)) complexity.
|
|
|
+ * The list is sorted in ascending order.
|
|
|
*
|
|
|
* The comparison function @cmp is supposed to return a negative value if @a is
|
|
|
* less than @b, and a positive value if @a is greater than @b. If @a and @b
|
|
|
* are equivalent, then it does not matter what this function returns.
|
|
|
*/
|
|
|
void list_sort(void *priv, struct list_head *head,
|
|
|
- int (*cmp)(void *priv, struct list_head *a,
|
|
|
- struct list_head *b))
|
|
|
+ int (*cmp)(void *priv, struct list_head *a,
|
|
|
+ struct list_head *b))
|
|
|
{
|
|
|
- struct list_head *p, *q, *e, *list, *tail, *oldhead;
|
|
|
- int insize, nmerges, psize, qsize, i;
|
|
|
+ struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
|
|
|
+ -- last slot is a sentinel */
|
|
|
+ int lev; /* index into part[] */
|
|
|
+ int max_lev = 0;
|
|
|
+ struct list_head *list;
|
|
|
|
|
|
if (list_empty(head))
|
|
|
return;
|
|
|
|
|
|
+ memset(part, 0, sizeof(part));
|
|
|
+
|
|
|
+ head->prev->next = NULL;
|
|
|
list = head->next;
|
|
|
- list_del(head);
|
|
|
- insize = 1;
|
|
|
- for (;;) {
|
|
|
- p = oldhead = list;
|
|
|
- list = tail = NULL;
|
|
|
- nmerges = 0;
|
|
|
-
|
|
|
- while (p) {
|
|
|
- nmerges++;
|
|
|
- q = p;
|
|
|
- psize = 0;
|
|
|
- for (i = 0; i < insize; i++) {
|
|
|
- psize++;
|
|
|
- q = q->next == oldhead ? NULL : q->next;
|
|
|
- if (!q)
|
|
|
- break;
|
|
|
- }
|
|
|
|
|
|
- qsize = insize;
|
|
|
- while (psize > 0 || (qsize > 0 && q)) {
|
|
|
- if (!psize) {
|
|
|
- e = q;
|
|
|
- q = q->next;
|
|
|
- qsize--;
|
|
|
- if (q == oldhead)
|
|
|
- q = NULL;
|
|
|
- } else if (!qsize || !q) {
|
|
|
- e = p;
|
|
|
- p = p->next;
|
|
|
- psize--;
|
|
|
- if (p == oldhead)
|
|
|
- p = NULL;
|
|
|
- } else if (cmp(priv, p, q) <= 0) {
|
|
|
- e = p;
|
|
|
- p = p->next;
|
|
|
- psize--;
|
|
|
- if (p == oldhead)
|
|
|
- p = NULL;
|
|
|
- } else {
|
|
|
- e = q;
|
|
|
- q = q->next;
|
|
|
- qsize--;
|
|
|
- if (q == oldhead)
|
|
|
- q = NULL;
|
|
|
- }
|
|
|
- if (tail)
|
|
|
- tail->next = e;
|
|
|
- else
|
|
|
- list = e;
|
|
|
- e->prev = tail;
|
|
|
- tail = e;
|
|
|
+ while (list) {
|
|
|
+ struct list_head *cur = list;
|
|
|
+ list = list->next;
|
|
|
+ cur->next = NULL;
|
|
|
+
|
|
|
+ for (lev = 0; part[lev]; lev++) {
|
|
|
+ cur = merge(priv, cmp, part[lev], cur);
|
|
|
+ part[lev] = NULL;
|
|
|
+ }
|
|
|
+ if (lev > max_lev) {
|
|
|
+ if (unlikely(lev >= ARRAY_SIZE(part)-1)) {
|
|
|
+ printk_once(KERN_DEBUG "list passed to"
|
|
|
+ " list_sort() too long for"
|
|
|
+ " efficiency\n");
|
|
|
+ lev--;
|
|
|
}
|
|
|
- p = q;
|
|
|
+ max_lev = lev;
|
|
|
}
|
|
|
+ part[lev] = cur;
|
|
|
+ }
|
|
|
|
|
|
- tail->next = list;
|
|
|
- list->prev = tail;
|
|
|
+ for (lev = 0; lev < max_lev; lev++)
|
|
|
+ if (part[lev])
|
|
|
+ list = merge(priv, cmp, part[lev], list);
|
|
|
|
|
|
- if (nmerges <= 1)
|
|
|
- break;
|
|
|
+ merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(list_sort);
|
|
|
|
|
|
- insize *= 2;
|
|
|
- }
|
|
|
+#ifdef DEBUG_LIST_SORT
|
|
|
+struct debug_el {
|
|
|
+ struct list_head l_h;
|
|
|
+ int value;
|
|
|
+ unsigned serial;
|
|
|
+};
|
|
|
|
|
|
- head->next = list;
|
|
|
- head->prev = list->prev;
|
|
|
- list->prev->next = head;
|
|
|
- list->prev = head;
|
|
|
+static int cmp(void *priv, struct list_head *a, struct list_head *b)
|
|
|
+{
|
|
|
+ return container_of(a, struct debug_el, l_h)->value
|
|
|
+ - container_of(b, struct debug_el, l_h)->value;
|
|
|
}
|
|
|
|
|
|
-EXPORT_SYMBOL(list_sort);
|
|
|
+/*
|
|
|
+ * The pattern of set bits in the list length determines which cases
|
|
|
+ * are hit in list_sort().
|
|
|
+ */
|
|
|
+#define LIST_SORT_TEST_LENGTH (512+128+2) /* not including head */
|
|
|
+
|
|
|
+static int __init list_sort_test(void)
|
|
|
+{
|
|
|
+ int i, r = 1, count;
|
|
|
+ struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL);
|
|
|
+ struct list_head *cur;
|
|
|
+
|
|
|
+ printk(KERN_WARNING "testing list_sort()\n");
|
|
|
+
|
|
|
+ cur = head;
|
|
|
+ for (i = 0; i < LIST_SORT_TEST_LENGTH; i++) {
|
|
|
+ struct debug_el *el = kmalloc(sizeof(*el), GFP_KERNEL);
|
|
|
+ BUG_ON(!el);
|
|
|
+ /* force some equivalencies */
|
|
|
+ el->value = (r = (r * 725861) % 6599) % (LIST_SORT_TEST_LENGTH/3);
|
|
|
+ el->serial = i;
|
|
|
+
|
|
|
+ el->l_h.prev = cur;
|
|
|
+ cur->next = &el->l_h;
|
|
|
+ cur = cur->next;
|
|
|
+ }
|
|
|
+ head->prev = cur;
|
|
|
+
|
|
|
+ list_sort(NULL, head, cmp);
|
|
|
+
|
|
|
+ count = 1;
|
|
|
+ for (cur = head->next; cur->next != head; cur = cur->next) {
|
|
|
+ struct debug_el *el = container_of(cur, struct debug_el, l_h);
|
|
|
+ int cmp_result = cmp(NULL, cur, cur->next);
|
|
|
+ if (cur->next->prev != cur) {
|
|
|
+ printk(KERN_EMERG "list_sort() returned "
|
|
|
+ "a corrupted list!\n");
|
|
|
+ return 1;
|
|
|
+ } else if (cmp_result > 0) {
|
|
|
+ printk(KERN_EMERG "list_sort() failed to sort!\n");
|
|
|
+ return 1;
|
|
|
+ } else if (cmp_result == 0 &&
|
|
|
+ el->serial >= container_of(cur->next,
|
|
|
+ struct debug_el, l_h)->serial) {
|
|
|
+ printk(KERN_EMERG "list_sort() failed to preserve order"
|
|
|
+ " of equivalent elements!\n");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ kfree(cur->prev);
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+ kfree(cur);
|
|
|
+ if (count != LIST_SORT_TEST_LENGTH) {
|
|
|
+ printk(KERN_EMERG "list_sort() returned list of"
|
|
|
+ "different length!\n");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+module_init(list_sort_test);
|
|
|
+#endif
|