Blame view

lib/list_sort.c 6.82 KB
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
1
2
  
  #define pr_fmt(fmt) "list_sort_test: " fmt
2c761270d   Dave Chinner   lib: Introduce ge...
3
  #include <linux/kernel.h>
7259fa042   Rasmus Villemoes   lib/list_sort.c: ...
4
5
6
7
  #include <linux/bug.h>
  #include <linux/compiler.h>
  #include <linux/export.h>
  #include <linux/string.h>
2c761270d   Dave Chinner   lib: Introduce ge...
8
  #include <linux/list_sort.h>
2c761270d   Dave Chinner   lib: Introduce ge...
9
  #include <linux/list.h>
835cc0c84   Don Mullis   lib: more scalabl...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  #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;
61b3d6c48   Rasmus Villemoes   lib: list_sort.c:...
53
  	u8 count = 0;
835cc0c84   Don Mullis   lib: more scalabl...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  
  	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.
  		 */
61b3d6c48   Rasmus Villemoes   lib: list_sort.c:...
77
78
  		if (unlikely(!(++count)))
  			(*cmp)(priv, tail->next, tail->next);
835cc0c84   Don Mullis   lib: more scalabl...
79
80
81
82
83
84
85
86
  
  		tail->next->prev = tail;
  		tail = tail->next;
  	} while (tail->next);
  
  	tail->next = head;
  	head->prev = tail;
  }
2c761270d   Dave Chinner   lib: Introduce ge...
87
  /**
02b12b7a2   Don Mullis   lib: revise list_...
88
89
   * list_sort - sort a list
   * @priv: private data, opaque to list_sort(), passed to @cmp
2c761270d   Dave Chinner   lib: Introduce ge...
90
91
92
   * @head: the list to sort
   * @cmp: the elements comparison function
   *
02b12b7a2   Don Mullis   lib: revise list_...
93
94
   * This function implements "merge sort", which has O(nlog(n))
   * complexity.
2c761270d   Dave Chinner   lib: Introduce ge...
95
   *
02b12b7a2   Don Mullis   lib: revise list_...
96
97
98
99
   * 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.
2c761270d   Dave Chinner   lib: Introduce ge...
100
101
   */
  void list_sort(void *priv, struct list_head *head,
835cc0c84   Don Mullis   lib: more scalabl...
102
103
  		int (*cmp)(void *priv, struct list_head *a,
  			struct list_head *b))
2c761270d   Dave Chinner   lib: Introduce ge...
104
  {
835cc0c84   Don Mullis   lib: more scalabl...
105
106
107
108
109
  	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;
2c761270d   Dave Chinner   lib: Introduce ge...
110
111
112
  
  	if (list_empty(head))
  		return;
835cc0c84   Don Mullis   lib: more scalabl...
113
114
115
  	memset(part, 0, sizeof(part));
  
  	head->prev->next = NULL;
2c761270d   Dave Chinner   lib: Introduce ge...
116
  	list = head->next;
2c761270d   Dave Chinner   lib: Introduce ge...
117

835cc0c84   Don Mullis   lib: more scalabl...
118
119
120
121
122
123
124
125
126
127
128
  	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)) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
129
130
  				printk_once(KERN_DEBUG "list too long for efficiency
  ");
835cc0c84   Don Mullis   lib: more scalabl...
131
  				lev--;
2c761270d   Dave Chinner   lib: Introduce ge...
132
  			}
835cc0c84   Don Mullis   lib: more scalabl...
133
  			max_lev = lev;
2c761270d   Dave Chinner   lib: Introduce ge...
134
  		}
835cc0c84   Don Mullis   lib: more scalabl...
135
136
  		part[lev] = cur;
  	}
2c761270d   Dave Chinner   lib: Introduce ge...
137

835cc0c84   Don Mullis   lib: more scalabl...
138
139
140
  	for (lev = 0; lev < max_lev; lev++)
  		if (part[lev])
  			list = merge(priv, cmp, part[lev], list);
2c761270d   Dave Chinner   lib: Introduce ge...
141

835cc0c84   Don Mullis   lib: more scalabl...
142
143
144
  	merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
  }
  EXPORT_SYMBOL(list_sort);
2c761270d   Dave Chinner   lib: Introduce ge...
145

6d411e6c0   Artem Bityutskiy   lib/Kconfig.debug...
146
  #ifdef CONFIG_TEST_LIST_SORT
eeee9ebb5   Artem Bityutskiy   lib/list_sort: te...
147

7259fa042   Rasmus Villemoes   lib/list_sort.c: ...
148
  #include <linux/slab.h>
eeee9ebb5   Artem Bityutskiy   lib/list_sort: te...
149
  #include <linux/random.h>
041b78f23   Artem Bityutskiy   lib/list_sort: te...
150
151
152
153
154
155
156
157
  /*
   * The pattern of set bits in the list length determines which cases
   * are hit in list_sort().
   */
  #define TEST_LIST_LEN (512+128+2) /* not including head */
  
  #define TEST_POISON1 0xDEADBEEF
  #define TEST_POISON2 0xA324354C
835cc0c84   Don Mullis   lib: more scalabl...
158
  struct debug_el {
041b78f23   Artem Bityutskiy   lib/list_sort: te...
159
  	unsigned int poison1;
eeee9ebb5   Artem Bityutskiy   lib/list_sort: te...
160
  	struct list_head list;
041b78f23   Artem Bityutskiy   lib/list_sort: te...
161
  	unsigned int poison2;
835cc0c84   Don Mullis   lib: more scalabl...
162
163
164
  	int value;
  	unsigned serial;
  };
2c761270d   Dave Chinner   lib: Introduce ge...
165

041b78f23   Artem Bityutskiy   lib/list_sort: te...
166
167
168
169
  /* Array, containing pointers to all elements in the test list */
  static struct debug_el **elts __initdata;
  
  static int __init check(struct debug_el *ela, struct debug_el *elb)
835cc0c84   Don Mullis   lib: more scalabl...
170
  {
041b78f23   Artem Bityutskiy   lib/list_sort: te...
171
  	if (ela->serial >= TEST_LIST_LEN) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
172
173
  		pr_err("error: incorrect serial %d
  ", ela->serial);
041b78f23   Artem Bityutskiy   lib/list_sort: te...
174
175
176
  		return -EINVAL;
  	}
  	if (elb->serial >= TEST_LIST_LEN) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
177
178
  		pr_err("error: incorrect serial %d
  ", elb->serial);
041b78f23   Artem Bityutskiy   lib/list_sort: te...
179
180
181
  		return -EINVAL;
  	}
  	if (elts[ela->serial] != ela || elts[elb->serial] != elb) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
182
183
  		pr_err("error: phantom element
  ");
041b78f23   Artem Bityutskiy   lib/list_sort: te...
184
185
186
  		return -EINVAL;
  	}
  	if (ela->poison1 != TEST_POISON1 || ela->poison2 != TEST_POISON2) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
187
188
189
  		pr_err("error: bad poison: %#x/%#x
  ",
  			ela->poison1, ela->poison2);
041b78f23   Artem Bityutskiy   lib/list_sort: te...
190
191
192
  		return -EINVAL;
  	}
  	if (elb->poison1 != TEST_POISON1 || elb->poison2 != TEST_POISON2) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
193
194
195
  		pr_err("error: bad poison: %#x/%#x
  ",
  			elb->poison1, elb->poison2);
041b78f23   Artem Bityutskiy   lib/list_sort: te...
196
197
198
  		return -EINVAL;
  	}
  	return 0;
2c761270d   Dave Chinner   lib: Introduce ge...
199
  }
041b78f23   Artem Bityutskiy   lib/list_sort: te...
200
201
202
203
204
205
206
207
208
209
  static int __init cmp(void *priv, struct list_head *a, struct list_head *b)
  {
  	struct debug_el *ela, *elb;
  
  	ela = container_of(a, struct debug_el, list);
  	elb = container_of(b, struct debug_el, list);
  
  	check(ela, elb);
  	return ela->value - elb->value;
  }
835cc0c84   Don Mullis   lib: more scalabl...
210
211
212
  
  static int __init list_sort_test(void)
  {
27d555d10   Rasmus Villemoes   lib: list_sort_te...
213
  	int i, count = 1, err = -ENOMEM;
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
214
  	struct debug_el *el;
694123031   Rasmus Villemoes   lib: list_sort_te...
215
  	struct list_head *cur;
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
216
  	LIST_HEAD(head);
835cc0c84   Don Mullis   lib: more scalabl...
217

d0da23b0d   Andrew Morton   lib/list_sort.c: ...
218
219
  	pr_debug("start testing list_sort()
  ");
835cc0c84   Don Mullis   lib: more scalabl...
220

694123031   Rasmus Villemoes   lib: list_sort_te...
221
  	elts = kcalloc(TEST_LIST_LEN, sizeof(*elts), GFP_KERNEL);
041b78f23   Artem Bityutskiy   lib/list_sort: te...
222
  	if (!elts) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
223
224
  		pr_err("error: cannot allocate memory
  ");
694123031   Rasmus Villemoes   lib: list_sort_te...
225
  		return err;
041b78f23   Artem Bityutskiy   lib/list_sort: te...
226
  	}
eeee9ebb5   Artem Bityutskiy   lib/list_sort: te...
227
  	for (i = 0; i < TEST_LIST_LEN; i++) {
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
228
229
  		el = kmalloc(sizeof(*el), GFP_KERNEL);
  		if (!el) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
230
231
  			pr_err("error: cannot allocate memory
  ");
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
232
233
  			goto exit;
  		}
835cc0c84   Don Mullis   lib: more scalabl...
234
  		 /* force some equivalencies */
f39fee5f1   Akinobu Mita   lib/: rename rand...
235
  		el->value = prandom_u32() % (TEST_LIST_LEN / 3);
835cc0c84   Don Mullis   lib: more scalabl...
236
  		el->serial = i;
041b78f23   Artem Bityutskiy   lib/list_sort: te...
237
238
239
  		el->poison1 = TEST_POISON1;
  		el->poison2 = TEST_POISON2;
  		elts[i] = el;
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
240
  		list_add_tail(&el->list, &head);
835cc0c84   Don Mullis   lib: more scalabl...
241
  	}
835cc0c84   Don Mullis   lib: more scalabl...
242

f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
243
  	list_sort(NULL, &head, cmp);
27d555d10   Rasmus Villemoes   lib: list_sort_te...
244
  	err = -EINVAL;
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
245
246
247
  	for (cur = head.next; cur->next != &head; cur = cur->next) {
  		struct debug_el *el1;
  		int cmp_result;
835cc0c84   Don Mullis   lib: more scalabl...
248

835cc0c84   Don Mullis   lib: more scalabl...
249
  		if (cur->next->prev != cur) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
250
251
  			pr_err("error: list is corrupted
  ");
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
252
253
254
255
256
  			goto exit;
  		}
  
  		cmp_result = cmp(NULL, cur, cur->next);
  		if (cmp_result > 0) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
257
258
  			pr_err("error: list is not sorted
  ");
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
259
260
261
262
263
264
  			goto exit;
  		}
  
  		el = container_of(cur, struct debug_el, list);
  		el1 = container_of(cur->next, struct debug_el, list);
  		if (cmp_result == 0 && el->serial >= el1->serial) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
265
266
267
  			pr_err("error: order of equivalent elements not "
  				"preserved
  ");
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
268
  			goto exit;
835cc0c84   Don Mullis   lib: more scalabl...
269
  		}
041b78f23   Artem Bityutskiy   lib/list_sort: te...
270
271
  
  		if (check(el, el1)) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
272
273
  			pr_err("error: element check failed
  ");
041b78f23   Artem Bityutskiy   lib/list_sort: te...
274
275
  			goto exit;
  		}
835cc0c84   Don Mullis   lib: more scalabl...
276
277
  		count++;
  	}
9d418dcc6   Rasmus Villemoes   lib: list_sort_te...
278
  	if (head.prev != cur) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
279
280
  		pr_err("error: list is corrupted
  ");
9d418dcc6   Rasmus Villemoes   lib: list_sort_te...
281
282
  		goto exit;
  	}
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
283

eeee9ebb5   Artem Bityutskiy   lib/list_sort: te...
284
  	if (count != TEST_LIST_LEN) {
d0da23b0d   Andrew Morton   lib/list_sort.c: ...
285
  		pr_err("error: bad list length %d", count);
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
286
287
288
289
290
  		goto exit;
  	}
  
  	err = 0;
  exit:
694123031   Rasmus Villemoes   lib: list_sort_te...
291
292
  	for (i = 0; i < TEST_LIST_LEN; i++)
  		kfree(elts[i]);
041b78f23   Artem Bityutskiy   lib/list_sort: te...
293
  	kfree(elts);
f3dc0e384   Artem Bityutskiy   lib/list_sort: te...
294
  	return err;
835cc0c84   Don Mullis   lib: more scalabl...
295
  }
4c7217f1f   Paul Gortmaker   lib/list_sort: us...
296
  late_initcall(list_sort_test);
6d411e6c0   Artem Bityutskiy   lib/Kconfig.debug...
297
  #endif /* CONFIG_TEST_LIST_SORT */