Whamcloud - gitweb
a31e0b3e94ede9940ba8be7e2b9b589ec763d69e
[fs/lustre-release.git] / lustre / tests / it_test.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  */
30 /*
31  * This file is part of Lustre, http://www.lustre.org/
32  * Lustre is a trademark of Sun Microsystems, Inc.
33  *
34  * lustre/tests/it_test.c
35  *
36  * Unit test tool for interval tree.
37  *
38  * Author: jay <jxiong@clusterfs.com>
39  */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <time.h>
44 #include <sys/time.h>
45
46 #define EXPORT_SYMBOL(s)
47
48 #include <../ldlm/interval_tree.c>
49
50 #define dprintf(fmt, args...) //printf(fmt, ##args)
51 #define error(fmt, args...) do {                        \
52         fflush(stdout), fflush(stderr);                 \
53         fprintf(stderr, "\nError:" fmt, ##args);        \
54         abort();                                        \
55 } while(0)
56
57 #define __F(ext)         (ext)->start, (ext)->end
58 #define __S              "["LPX64":"LPX64"]"
59
60 #define ALIGN_SIZE       4096
61 #define ALIGN_MASK       (~(ALIGN_SIZE - 1))
62
63 static struct it_node {
64         struct interval_node node;
65         struct list_head list;
66         int hit, valid;
67 } *it_array;
68 static int it_count;
69 static struct list_head header = LIST_HEAD_INIT(header);
70 static unsigned long max_count = ULONG_MAX & ALIGN_MASK;
71 static int have_wide_lock = 0;
72
73 static void it_test_clear(void)
74 {
75         int i = 0;
76         for (i = 0; i < it_count; i++)
77                 it_array[i].hit = 0;
78 }
79
80 static enum interval_iter cb(struct interval_node *n, void *args)
81 {
82         struct it_node *node = (struct it_node *)n;
83         static int count = 1;
84
85         if (node->hit == 1) {
86                 error("A duplicate node "__S" access found\n",
87                        __F(&n->in_extent));
88                 return INTERVAL_ITER_CONT;
89         }
90
91         if (node->valid == 0) {
92                 error("A deleted node "__S" being accessed\n",
93                        __F(&n->in_extent));
94                 return INTERVAL_ITER_STOP;
95         }
96
97         if (count++ == 8) {
98                 dprintf("\n");
99                 count = 1;
100         }
101         dprintf(""__S" ", __F(&n->in_extent));
102         fflush(stdout);
103
104         node->hit = 1;
105         return INTERVAL_ITER_CONT;
106 }
107
108 static int it_test_search(struct interval_node *root)
109 {
110         struct it_node *n;
111         struct interval_node_extent ext;
112         int times = 10, i, err = 0;
113
114         while (times--) {
115                 it_test_clear();
116                 ext.start = (random() % max_count) & ALIGN_MASK;
117                 ext.end = random() % (max_count - ext.start + 2) + ext.start;
118                 ext.end &= ALIGN_MASK;
119                 if (ext.end > max_count)
120                         ext.end = max_count;
121
122                 dprintf("\n\nSearching the node overlapped "__S" ..\n",
123                         __F(&ext));
124
125                 interval_search(root, &ext, cb, NULL);
126
127                 dprintf("\nverifing ...");
128
129                 /* verify */
130                 for (i = 0; i < it_count; i++) {
131                         n = &it_array[i];
132                         if (n->valid == 0)
133                                 continue;
134
135                         if (extent_overlapped(&ext, &n->node.in_extent) &&
136                             n->hit == 0)
137                                 error("node "__S" overlaps" __S","
138                                       "but never to be hit.\n",
139                                       __F(&n->node.in_extent),
140                                       __F(&ext));
141
142                         if (!extent_overlapped(&ext, &n->node.in_extent) &&
143                             n->hit)
144                                 error("node "__S" overlaps" __S", but hit.\n",
145                                       __F(&n->node.in_extent),
146                                       __F(&ext));
147                 }
148                 if (err) error("search error\n");
149                 dprintf("ok.\n");
150         }
151
152         return 0;
153 }
154
155 static int it_test_iterate(struct interval_node *root)
156 {
157         int i;
158
159         dprintf("\n\nIterate testing start..\n");
160
161         it_test_clear();
162         interval_iterate(root, cb, NULL);
163
164         /* verify */
165         for (i = 0; i < it_count; i++) {
166                 if (it_array[i].valid == 0)
167                         continue;
168                 if (it_array[i].hit == 0)
169                         error("Node "__S" is not accessed\n",
170                               __F(&it_array[i].node.in_extent));
171         }
172
173         return 0;
174 }
175
176 static int it_test_iterate_reverse(struct interval_node *root)
177 {
178         int i;
179
180         dprintf("\n\niterate reverse testing start..\n");
181         it_test_clear();
182         interval_iterate_reverse(root, cb, NULL);
183
184         /* verify */
185         for (i = 0; i < it_count; i++) {
186                 if (it_array[i].valid == 0)
187                         continue;
188                 if (it_array[i].hit == 0)
189                         error("Not every extent is accessed\n");
190         }
191
192         return 0;
193 }
194
195 static int it_test_find(struct interval_node *root)
196 {
197         int idx;
198         struct interval_node_extent *ext;
199
200         dprintf("\ninterval_find testing start ..\n");
201         for (idx = 0; idx < it_count; idx++) {
202                 if (it_array[idx].valid == 0)
203                         continue;
204
205                 ext = &it_array[idx].node.in_extent;
206                 dprintf("Try to find "__S"\n", __F(ext));
207                 if (!interval_find(root, ext))
208                         error("interval_find, try to find "__S"\n", __F(ext));
209         }
210         return 0;
211 }
212
213 /* sanity test is tightly coupled with implementation, so when you changed
214  * the interval tree implementation, change this code also. */
215 static enum interval_iter sanity_cb(struct interval_node *node, void *args)
216 {
217         __u64 max_high = node->in_max_high;
218         struct interval_node *tmp, *parent;
219         int left = 1, has = 0, nr = 0;
220
221         parent = node->in_parent;
222         node->in_parent = NULL;
223         interval_for_each(tmp, node) {
224                 if ((left && node_compare(tmp, node) > 0) ||
225                     (!left && node_compare(tmp, node) < 0))
226                         error("interval tree sanity test\n");
227
228                 if (tmp->in_max_high > max_high) {
229                         dprintf("max high sanity check, max_high is %llu,"
230                                 "child max_high: %llu"__S"\n",
231                                 max_high, tmp->in_max_high,
232                                 __F(&tmp->in_extent));
233                         goto err;
234                 } else if (tmp->in_max_high == max_high) {
235                         has = 1;
236                 }
237
238                 if (tmp == node) {
239                         left = 0;
240                         continue;
241                 }
242         }
243
244         if (!has) {
245                 int count;
246 err:
247                 count = 1;
248                 dprintf("node"__S":%llu Child list:\n",
249                         node->in_extent.start,
250                         node->in_extent.end,
251                         node->in_max_high);
252
253                 interval_for_each(tmp, node) {
254                         dprintf(""__S":%llu ",
255                                 __F(&tmp->in_extent),
256                                 tmp->in_max_high);
257                         if (count++ == 8) {
258                                 dprintf("\n");
259                                 count = 1;
260                         }
261                 }
262
263                 error("max high sanity check, has == %d\n", has);
264         }
265         node->in_parent = parent;
266
267         tmp = node;
268         while (tmp) {
269                 if (node_is_black(tmp))
270                         nr++;
271                 else if ((tmp->in_left && node_is_red(tmp->in_left)) ||
272                          (tmp->in_right && node_is_red(tmp->in_right)))
273                         error("wrong tree, a red node has red child\n");
274                 tmp = tmp->in_left;
275         }
276
277         tmp = node;
278         while (tmp) {
279                 if (node_is_black(tmp))
280                         nr--;
281                 tmp = tmp->in_right;
282         }
283         if (nr)
284                 error("wrong tree, unbalanced!\n");
285
286         return 0;
287 }
288
289 static int it_test_sanity(struct interval_node *root)
290 {
291         it_test_clear();
292         interval_iterate(root, sanity_cb, NULL);
293         return 0;
294 }
295
296 static int it_test_search_hole(struct interval_node *root)
297 {
298         int i, count = 10;
299         struct interval_node_extent ext, ext2;
300         struct it_node *n;
301         __u64 low = 0, high = ~0;
302
303         do {
304                 if (--count == 0)
305                         return 0;
306
307                 ext.start = random() % max_count;
308                 ext.end = ext.start;
309         } while (interval_is_overlapped(root, &ext));
310         ext2 = ext;
311
312         interval_expand(root, &ext, NULL);
313         dprintf("Extending "__S" to .."__S"\n", __F(&ext2), __F(&ext));
314         for (i = 0; i < it_count; i++) {
315                 n = &it_array[i];
316                 if (n->valid == 0)
317                         continue;
318
319                 if (extent_overlapped(&ext, &n->node.in_extent)) {
320                         error("Extending "__S" to .."__S" overlaps node"__S"\n",
321                                 __F(&ext2), __F(&ext), __F(&n->node.in_extent));
322                 }
323
324                 if (n->node.in_extent.end < ext2.start)
325                         low = max_u64(n->node.in_extent.end + 1, low);
326
327                 if (n->node.in_extent.start > ext2.end)
328                         high = min_u64(n->node.in_extent.start - 1, high);
329         }
330
331         /* only expanding high right now */
332         if (ext2.start != ext.start || high != ext.end) {
333                 ext2.start = low, ext2.end = high;
334                 error("Real extending result:"__S", expected:"__S"\n",
335                        __F(&ext), __F(&ext2));
336         }
337
338         return 0;
339 }
340
341 static int contended_count = 0;
342 #define LOOP_COUNT 1000
343 static enum interval_iter perf_cb(struct interval_node *n, void *args)
344 {
345         unsigned long count = LOOP_COUNT;
346         while (count--);
347         contended_count++;
348         return INTERVAL_ITER_CONT;
349 }
350
351 static inline long tv_delta(struct timeval *s, struct timeval *e)
352 {
353         long c = e->tv_sec - s->tv_sec;
354         c *= 1000;
355         c += (long int)(e->tv_usec - s->tv_usec) / 1000;
356         dprintf("\tStart: %lu:%lu -> End: %lu:%lu\n",
357                 s->tv_sec, s->tv_usec, e->tv_sec, e->tv_usec);
358         return c;
359 }
360
361 static int it_test_performance(struct interval_node *root, unsigned long len)
362 {
363         int i = 0, interval_time, list_time;
364         struct interval_node_extent ext;
365         struct it_node *n;
366         struct timeval start, end;
367         unsigned long count;
368
369         ext.start = (random() % (max_count - len)) & ALIGN_MASK;
370         ext.end = (ext.start + len) & ALIGN_MASK;
371         if (have_wide_lock) {
372                 ext.start = (max_count - len) & ALIGN_MASK;
373                 ext.end = max_count;
374         }
375
376         dprintf("Extent search"__S"\n", __F(&ext));
377
378         /* list */
379         contended_count = 0;
380         gettimeofday(&start, NULL);
381         list_for_each_entry(n, &header, list) {
382                 if (extent_overlapped(&ext, &n->node.in_extent)) {
383                         count = LOOP_COUNT;
384                         while (count--);
385                         contended_count++;
386                 }
387         }
388         gettimeofday(&end, NULL);
389         list_time = tv_delta(&start, &end);
390         i = contended_count;
391
392         /* interval */
393         contended_count = 0;
394         gettimeofday(&start, NULL);
395         interval_search(root, &ext, perf_cb, &contended_count);
396         gettimeofday(&end, NULL);
397         interval_time = tv_delta(&start, &end);
398
399         if (i != contended_count)
400                 error("count of contended lock don't match(%d: %d)\n",
401                       i, contended_count);
402
403         printf("\tList vs Int. search: \n\t\t"
404                "(%d vs %d)ms, %d contended lock.\n",
405                 list_time, interval_time, contended_count);
406
407         return 0;
408 }
409
410 static struct interval_node *it_test_helper(struct interval_node *root)
411 {
412         int idx, count = 0;
413         struct it_node *n;
414
415         count = random() % it_count;
416         while (count--) {
417                 idx = random() % it_count;
418                 n = &it_array[idx];
419                 if (n->valid) {
420                         if (!interval_find(root, &n->node.in_extent))
421                                 error("Cannot find an existent node\n");
422                         dprintf("Erasing a node "__S"\n",
423                                 __F(&n->node.in_extent));
424                         interval_erase(&n->node, &root);
425                         n->valid = 0;
426                         list_del_init(&n->list);
427                 } else {
428                         __u64 low, high;
429                         low = (random() % max_count) & ALIGN_MASK;
430                         high = ((random() % max_count + 1) & ALIGN_MASK) + low;
431                         if (high > max_count)
432                                 high = max_count;
433                         interval_set(&n->node, low, high);
434                         while (interval_insert(&n->node, &root))
435                                 interval_set(&n->node, low, ++high);
436                         dprintf("Adding a node "__S"\n",
437                                 __F(&n->node.in_extent));
438                         n->valid = 1;
439                         list_add(&n->list, &header);
440                 }
441         }
442
443         return root;
444 }
445
446 static struct interval_node *it_test_init(int count)
447 {
448         int i;
449         uint64_t high, low, len;
450         struct it_node *n;
451         struct interval_node *root = NULL;
452
453         it_count = count;
454         it_array = (struct it_node *)malloc(sizeof(struct it_node) * count);
455         if (it_array == NULL)
456                 error("it_array == NULL, no memory\n");
457
458         have_wide_lock = 0;
459         for (i = 0; i < count; i++) {
460                 n = &it_array[i];
461                 do {
462                         low = (random() % max_count + 1) & ALIGN_MASK;
463                         len = (random() % 256 + 1) * ALIGN_SIZE;
464                         if (!have_wide_lock && !(random() % count)) {
465                                 low = 0;
466                                 len = max_count;
467                                 have_wide_lock = 1;
468                         }
469                         high = low + (len & ALIGN_MASK);
470
471                         interval_set(&n->node, low, high);
472                 } while (interval_insert(&n->node, &root));
473                 n->hit = 0;
474                 n->valid = 1;
475                 if (i == 0)
476                         list_add_tail(&n->list, &header);
477                 else
478                         list_add_tail(&n->list, &it_array[rand()%i].list);
479         }
480
481         return root;
482 }
483
484 static void it_test_fini(void)
485 {
486         free(it_array);
487         it_array = NULL;
488         it_count = 0;
489         max_count = 0;
490 }
491
492 int main(int argc, char *argv[])
493 {
494         int count = 5, perf = 0;
495         struct interval_node *root;
496         struct timeval tv;
497
498         gettimeofday(&tv, NULL);
499         srandom(tv.tv_usec);
500
501         if (argc == 2) {
502                 if (strcmp(argv[1], "-p"))
503                         error("Unknow options, usage: %s [-p]\n", argv[0]);
504                 perf = 1;
505                 count = 1;
506         }
507
508         if (perf) {
509                 int M = 1024 * 1024;
510                 root = it_test_init(1000000);
511                 printf("1M locks with 4K request size\n");
512                 it_test_performance(root, 4096);
513                 printf("1M locks with 128K request size\n");
514                 it_test_performance(root, 128 * 1024);
515                 printf("1M locks with 256K request size\n");
516                 it_test_performance(root, 256 * 1024);
517                 printf("1M locks with 1M request size\n");
518                 it_test_performance(root, 1 * M);
519                 printf("1M locks with 16M request size\n");
520                 it_test_performance(root, 16 * M);
521                 printf("1M locks with 32M request size\n");
522                 it_test_performance(root, 32 * M);
523                 printf("1M locks with 64M request size\n");
524                 it_test_performance(root, 64 * M);
525                 printf("1M locks with 128M request size\n");
526                 it_test_performance(root, 128 * M);
527                 printf("1M locks with 256M request size\n");
528                 it_test_performance(root, 256 * M);
529                 printf("1M locks with 512M request size\n");
530                 it_test_performance(root, 512 * M);
531                 printf("1M locks with 1G request size\n");
532                 it_test_performance(root, 1024 * M);
533                 printf("1M locks with 2G request size\n");
534                 it_test_performance(root, 2048 * M);
535                 printf("1M locks with 3G request size\n");
536                 it_test_performance(root, 3072 * M);
537                 printf("1M locks with 4G request size\n");
538                 it_test_performance(root, max_count - 1);
539                 it_test_fini();
540                 return 0;
541         }
542
543         root = it_test_init(random() % 100000 + 1000);
544         while (count--) {
545                 it_test_sanity(root);
546                 it_test_iterate(root);
547                 it_test_iterate_reverse(root);
548                 it_test_find(root);
549                 it_test_search_hole(root);
550                 it_test_search(root);
551                 root = it_test_helper(root);
552         }
553         it_test_fini();
554
555         return 0;
556 }