1,101 → 1,145 |
|
#define pr_fmt(fmt) "list_sort_test: " fmt |
|
#include <linux/kernel.h> |
#include <linux/module.h> |
#include <linux/list_sort.h> |
#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; |
u8 count = 0; |
|
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. |
*/ |
if (unlikely(!(++count))) |
(*cmp)(priv, tail->next, tail->next); |
|
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 |
* list_sort - sort a list |
* @priv: private data, opaque to list_sort(), 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 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. |
* The comparison function @cmp must return a negative value if @a |
* should sort before @b, and a positive value if @a should sort after |
* @b. If @a and @b are equivalent, and their original relative |
* ordering is to be preserved, @cmp must return 0. |
*/ |
void list_sort(void *priv, struct list_head *head, |
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; |
} |
while (list) { |
struct list_head *cur = list; |
list = list->next; |
cur->next = NULL; |
|
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; |
for (lev = 0; part[lev]; lev++) { |
cur = merge(priv, cmp, part[lev], cur); |
part[lev] = NULL; |
} |
if (tail) |
tail->next = e; |
else |
list = e; |
e->prev = tail; |
tail = e; |
if (lev > max_lev) { |
if (unlikely(lev >= ARRAY_SIZE(part)-1)) { |
printk_once(KERN_DEBUG "list 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; |
|
insize *= 2; |
merge_and_restore_back_links(priv, cmp, head, part[max_lev], list); |
} |
|
head->next = list; |
head->prev = list->prev; |
list->prev->next = head; |
list->prev = head; |
} |
|
EXPORT_SYMBOL(list_sort); |