0001
0002 #include <stdlib.h>
0003 #include <assert.h>
0004 #include <stdio.h>
0005 #include <string.h>
0006
0007 #include <linux/slab.h>
0008 #include <linux/radix-tree.h>
0009
0010 #include "test.h"
0011
0012
0013 static void
0014 __simple_checks(struct radix_tree_root *tree, unsigned long index, int tag)
0015 {
0016 unsigned long first = 0;
0017 int ret;
0018
0019 item_check_absent(tree, index);
0020 assert(item_tag_get(tree, index, tag) == 0);
0021
0022 item_insert(tree, index);
0023 assert(item_tag_get(tree, index, tag) == 0);
0024 item_tag_set(tree, index, tag);
0025 ret = item_tag_get(tree, index, tag);
0026 assert(ret != 0);
0027 ret = tag_tagged_items(tree, first, ~0UL, 10, tag, !tag);
0028 assert(ret == 1);
0029 ret = item_tag_get(tree, index, !tag);
0030 assert(ret != 0);
0031 ret = item_delete(tree, index);
0032 assert(ret != 0);
0033 item_insert(tree, index);
0034 ret = item_tag_get(tree, index, tag);
0035 assert(ret == 0);
0036 ret = item_delete(tree, index);
0037 assert(ret != 0);
0038 ret = item_delete(tree, index);
0039 assert(ret == 0);
0040 }
0041
0042 void simple_checks(void)
0043 {
0044 unsigned long index;
0045 RADIX_TREE(tree, GFP_KERNEL);
0046
0047 for (index = 0; index < 10000; index++) {
0048 __simple_checks(&tree, index, 0);
0049 __simple_checks(&tree, index, 1);
0050 }
0051 verify_tag_consistency(&tree, 0);
0052 verify_tag_consistency(&tree, 1);
0053 printv(2, "before item_kill_tree: %d allocated\n", nr_allocated);
0054 item_kill_tree(&tree);
0055 rcu_barrier();
0056 printv(2, "after item_kill_tree: %d allocated\n", nr_allocated);
0057 }
0058
0059
0060
0061
0062 static void extend_checks(void)
0063 {
0064 RADIX_TREE(tree, GFP_KERNEL);
0065
0066 item_insert(&tree, 43);
0067 assert(item_tag_get(&tree, 43, 0) == 0);
0068 item_tag_set(&tree, 43, 0);
0069 assert(item_tag_get(&tree, 43, 0) == 1);
0070 item_insert(&tree, 1000000);
0071 assert(item_tag_get(&tree, 43, 0) == 1);
0072
0073 item_insert(&tree, 0);
0074 item_tag_set(&tree, 0, 0);
0075 item_delete(&tree, 1000000);
0076 assert(item_tag_get(&tree, 43, 0) != 0);
0077 item_delete(&tree, 43);
0078 assert(item_tag_get(&tree, 43, 0) == 0);
0079 assert(item_tag_get(&tree, 0, 0) == 1);
0080
0081 verify_tag_consistency(&tree, 0);
0082
0083 item_kill_tree(&tree);
0084 }
0085
0086
0087
0088
0089 static void contract_checks(void)
0090 {
0091 struct item *item;
0092 int tmp;
0093 RADIX_TREE(tree, GFP_KERNEL);
0094
0095 tmp = 1<<RADIX_TREE_MAP_SHIFT;
0096 item_insert(&tree, tmp);
0097 item_insert(&tree, tmp+1);
0098 item_tag_set(&tree, tmp, 0);
0099 item_tag_set(&tree, tmp, 1);
0100 item_tag_set(&tree, tmp+1, 0);
0101 item_delete(&tree, tmp+1);
0102 item_tag_clear(&tree, tmp, 1);
0103
0104 assert(radix_tree_gang_lookup_tag(&tree, (void **)&item, 0, 1, 0) == 1);
0105 assert(radix_tree_gang_lookup_tag(&tree, (void **)&item, 0, 1, 1) == 0);
0106
0107 assert(item_tag_get(&tree, tmp, 0) == 1);
0108 assert(item_tag_get(&tree, tmp, 1) == 0);
0109
0110 verify_tag_consistency(&tree, 0);
0111 item_kill_tree(&tree);
0112 }
0113
0114
0115
0116
0117
0118
0119
0120
0121 enum {
0122 NODE_ABSENT = 0,
0123 NODE_PRESENT = 1,
0124 NODE_TAGGED = 2,
0125 };
0126
0127 #define THRASH_SIZE (1000 * 1000)
0128 #define N 127
0129 #define BATCH 33
0130
0131 static void gang_check(struct radix_tree_root *tree,
0132 char *thrash_state, int tag)
0133 {
0134 struct item *items[BATCH];
0135 int nr_found;
0136 unsigned long index = 0;
0137 unsigned long last_index = 0;
0138
0139 while ((nr_found = radix_tree_gang_lookup_tag(tree, (void **)items,
0140 index, BATCH, tag))) {
0141 int i;
0142
0143 for (i = 0; i < nr_found; i++) {
0144 struct item *item = items[i];
0145
0146 while (last_index < item->index) {
0147 assert(thrash_state[last_index] != NODE_TAGGED);
0148 last_index++;
0149 }
0150 assert(thrash_state[last_index] == NODE_TAGGED);
0151 last_index++;
0152 }
0153 index = items[nr_found - 1]->index + 1;
0154 }
0155 }
0156
0157 static void do_thrash(struct radix_tree_root *tree, char *thrash_state, int tag)
0158 {
0159 int insert_chunk;
0160 int delete_chunk;
0161 int tag_chunk;
0162 int untag_chunk;
0163 int total_tagged = 0;
0164 int total_present = 0;
0165
0166 for (insert_chunk = 1; insert_chunk < THRASH_SIZE; insert_chunk *= N)
0167 for (delete_chunk = 1; delete_chunk < THRASH_SIZE; delete_chunk *= N)
0168 for (tag_chunk = 1; tag_chunk < THRASH_SIZE; tag_chunk *= N)
0169 for (untag_chunk = 1; untag_chunk < THRASH_SIZE; untag_chunk *= N) {
0170 int i;
0171 unsigned long index;
0172 int nr_inserted = 0;
0173 int nr_deleted = 0;
0174 int nr_tagged = 0;
0175 int nr_untagged = 0;
0176 int actual_total_tagged;
0177 int actual_total_present;
0178
0179 for (i = 0; i < insert_chunk; i++) {
0180 index = rand() % THRASH_SIZE;
0181 if (thrash_state[index] != NODE_ABSENT)
0182 continue;
0183 item_check_absent(tree, index);
0184 item_insert(tree, index);
0185 assert(thrash_state[index] != NODE_PRESENT);
0186 thrash_state[index] = NODE_PRESENT;
0187 nr_inserted++;
0188 total_present++;
0189 }
0190
0191 for (i = 0; i < delete_chunk; i++) {
0192 index = rand() % THRASH_SIZE;
0193 if (thrash_state[index] == NODE_ABSENT)
0194 continue;
0195 item_check_present(tree, index);
0196 if (item_tag_get(tree, index, tag)) {
0197 assert(thrash_state[index] == NODE_TAGGED);
0198 total_tagged--;
0199 } else {
0200 assert(thrash_state[index] == NODE_PRESENT);
0201 }
0202 item_delete(tree, index);
0203 assert(thrash_state[index] != NODE_ABSENT);
0204 thrash_state[index] = NODE_ABSENT;
0205 nr_deleted++;
0206 total_present--;
0207 }
0208
0209 for (i = 0; i < tag_chunk; i++) {
0210 index = rand() % THRASH_SIZE;
0211 if (thrash_state[index] != NODE_PRESENT) {
0212 if (item_lookup(tree, index))
0213 assert(item_tag_get(tree, index, tag));
0214 continue;
0215 }
0216 item_tag_set(tree, index, tag);
0217 item_tag_set(tree, index, tag);
0218 assert(thrash_state[index] != NODE_TAGGED);
0219 thrash_state[index] = NODE_TAGGED;
0220 nr_tagged++;
0221 total_tagged++;
0222 }
0223
0224 for (i = 0; i < untag_chunk; i++) {
0225 index = rand() % THRASH_SIZE;
0226 if (thrash_state[index] != NODE_TAGGED)
0227 continue;
0228 item_check_present(tree, index);
0229 assert(item_tag_get(tree, index, tag));
0230 item_tag_clear(tree, index, tag);
0231 item_tag_clear(tree, index, tag);
0232 assert(thrash_state[index] != NODE_PRESENT);
0233 thrash_state[index] = NODE_PRESENT;
0234 nr_untagged++;
0235 total_tagged--;
0236 }
0237
0238 actual_total_tagged = 0;
0239 actual_total_present = 0;
0240 for (index = 0; index < THRASH_SIZE; index++) {
0241 switch (thrash_state[index]) {
0242 case NODE_ABSENT:
0243 item_check_absent(tree, index);
0244 break;
0245 case NODE_PRESENT:
0246 item_check_present(tree, index);
0247 assert(!item_tag_get(tree, index, tag));
0248 actual_total_present++;
0249 break;
0250 case NODE_TAGGED:
0251 item_check_present(tree, index);
0252 assert(item_tag_get(tree, index, tag));
0253 actual_total_present++;
0254 actual_total_tagged++;
0255 break;
0256 }
0257 }
0258
0259 gang_check(tree, thrash_state, tag);
0260
0261 printv(2, "%d(%d) %d(%d) %d(%d) %d(%d) / "
0262 "%d(%d) present, %d(%d) tagged\n",
0263 insert_chunk, nr_inserted,
0264 delete_chunk, nr_deleted,
0265 tag_chunk, nr_tagged,
0266 untag_chunk, nr_untagged,
0267 total_present, actual_total_present,
0268 total_tagged, actual_total_tagged);
0269 }
0270 }
0271
0272 static void thrash_tags(void)
0273 {
0274 RADIX_TREE(tree, GFP_KERNEL);
0275 char *thrash_state;
0276
0277 thrash_state = malloc(THRASH_SIZE);
0278 memset(thrash_state, 0, THRASH_SIZE);
0279
0280 do_thrash(&tree, thrash_state, 0);
0281
0282 verify_tag_consistency(&tree, 0);
0283 item_kill_tree(&tree);
0284 free(thrash_state);
0285 }
0286
0287 static void leak_check(void)
0288 {
0289 RADIX_TREE(tree, GFP_KERNEL);
0290
0291 item_insert(&tree, 1000000);
0292 item_delete(&tree, 1000000);
0293 item_kill_tree(&tree);
0294 }
0295
0296 static void __leak_check(void)
0297 {
0298 RADIX_TREE(tree, GFP_KERNEL);
0299
0300 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
0301 item_insert(&tree, 1000000);
0302 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
0303 item_delete(&tree, 1000000);
0304 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
0305 item_kill_tree(&tree);
0306 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
0307 }
0308
0309 static void single_check(void)
0310 {
0311 struct item *items[BATCH];
0312 RADIX_TREE(tree, GFP_KERNEL);
0313 int ret;
0314 unsigned long first = 0;
0315
0316 item_insert(&tree, 0);
0317 item_tag_set(&tree, 0, 0);
0318 ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0);
0319 assert(ret == 1);
0320 ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 1, BATCH, 0);
0321 assert(ret == 0);
0322 verify_tag_consistency(&tree, 0);
0323 verify_tag_consistency(&tree, 1);
0324 ret = tag_tagged_items(&tree, first, 10, 10, XA_MARK_0, XA_MARK_1);
0325 assert(ret == 1);
0326 ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 1);
0327 assert(ret == 1);
0328 item_tag_clear(&tree, 0, 0);
0329 ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0);
0330 assert(ret == 0);
0331 item_kill_tree(&tree);
0332 }
0333
0334 void tag_check(void)
0335 {
0336 single_check();
0337 extend_checks();
0338 contract_checks();
0339 rcu_barrier();
0340 printv(2, "after extend_checks: %d allocated\n", nr_allocated);
0341 __leak_check();
0342 leak_check();
0343 rcu_barrier();
0344 printv(2, "after leak_check: %d allocated\n", nr_allocated);
0345 simple_checks();
0346 rcu_barrier();
0347 printv(2, "after simple_checks: %d allocated\n", nr_allocated);
0348 thrash_tags();
0349 rcu_barrier();
0350 printv(2, "after thrash_tags: %d allocated\n", nr_allocated);
0351 }