|
23 | 23 | #include <linux/semaphore.h>
|
24 | 24 | #include <linux/slab.h>
|
25 | 25 | #include <linux/sched.h>
|
| 26 | +#include <linux/random.h> |
26 | 27 | #include <linux/vmalloc.h>
|
27 | 28 |
|
28 | 29 | #define MAX_ENTRIES 1000000
|
@@ -66,6 +67,11 @@ struct test_obj {
|
66 | 67 | struct rhash_head node;
|
67 | 68 | };
|
68 | 69 |
|
| 70 | +struct test_obj_rhl { |
| 71 | + struct test_obj_val value; |
| 72 | + struct rhlist_head list_node; |
| 73 | +}; |
| 74 | + |
69 | 75 | struct thread_data {
|
70 | 76 | unsigned int entries;
|
71 | 77 | int id;
|
@@ -245,6 +251,186 @@ static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
|
245 | 251 | }
|
246 | 252 |
|
247 | 253 | static struct rhashtable ht;
|
| 254 | +static struct rhltable rhlt __initdata; |
| 255 | + |
| 256 | +static int __init test_rhltable(unsigned int entries) |
| 257 | +{ |
| 258 | + struct test_obj_rhl *rhl_test_objects; |
| 259 | + unsigned long *obj_in_table; |
| 260 | + unsigned int i, j, k; |
| 261 | + int ret, err; |
| 262 | + |
| 263 | + if (entries == 0) |
| 264 | + entries = 1; |
| 265 | + |
| 266 | + rhl_test_objects = vzalloc(sizeof(*rhl_test_objects) * entries); |
| 267 | + if (!rhl_test_objects) |
| 268 | + return -ENOMEM; |
| 269 | + |
| 270 | + ret = -ENOMEM; |
| 271 | + obj_in_table = vzalloc(BITS_TO_LONGS(entries) * sizeof(unsigned long)); |
| 272 | + if (!obj_in_table) |
| 273 | + goto out_free; |
| 274 | + |
| 275 | + /* nulls_base not supported in rhlist interface */ |
| 276 | + test_rht_params.nulls_base = 0; |
| 277 | + err = rhltable_init(&rhlt, &test_rht_params); |
| 278 | + if (WARN_ON(err)) |
| 279 | + goto out_free; |
| 280 | + |
| 281 | + k = prandom_u32(); |
| 282 | + ret = 0; |
| 283 | + for (i = 0; i < entries; i++) { |
| 284 | + rhl_test_objects[i].value.id = k; |
| 285 | + err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, |
| 286 | + test_rht_params); |
| 287 | + if (WARN(err, "error %d on element %d\n", err, i)) |
| 288 | + break; |
| 289 | + if (err == 0) |
| 290 | + set_bit(i, obj_in_table); |
| 291 | + } |
| 292 | + |
| 293 | + if (err) |
| 294 | + ret = err; |
| 295 | + |
| 296 | + pr_info("test %d add/delete pairs into rhlist\n", entries); |
| 297 | + for (i = 0; i < entries; i++) { |
| 298 | + struct rhlist_head *h, *pos; |
| 299 | + struct test_obj_rhl *obj; |
| 300 | + struct test_obj_val key = { |
| 301 | + .id = k, |
| 302 | + }; |
| 303 | + bool found; |
| 304 | + |
| 305 | + rcu_read_lock(); |
| 306 | + h = rhltable_lookup(&rhlt, &key, test_rht_params); |
| 307 | + if (WARN(!h, "key not found during iteration %d of %d", i, entries)) { |
| 308 | + rcu_read_unlock(); |
| 309 | + break; |
| 310 | + } |
| 311 | + |
| 312 | + if (i) { |
| 313 | + j = i - 1; |
| 314 | + rhl_for_each_entry_rcu(obj, pos, h, list_node) { |
| 315 | + if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone")) |
| 316 | + break; |
| 317 | + } |
| 318 | + } |
| 319 | + |
| 320 | + cond_resched_rcu(); |
| 321 | + |
| 322 | + found = false; |
| 323 | + |
| 324 | + rhl_for_each_entry_rcu(obj, pos, h, list_node) { |
| 325 | + if (pos == &rhl_test_objects[i].list_node) { |
| 326 | + found = true; |
| 327 | + break; |
| 328 | + } |
| 329 | + } |
| 330 | + |
| 331 | + rcu_read_unlock(); |
| 332 | + |
| 333 | + if (WARN(!found, "element %d not found", i)) |
| 334 | + break; |
| 335 | + |
| 336 | + err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); |
| 337 | + WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i); |
| 338 | + if (err == 0) |
| 339 | + clear_bit(i, obj_in_table); |
| 340 | + } |
| 341 | + |
| 342 | + if (ret == 0 && err) |
| 343 | + ret = err; |
| 344 | + |
| 345 | + for (i = 0; i < entries; i++) { |
| 346 | + WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i); |
| 347 | + |
| 348 | + err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, |
| 349 | + test_rht_params); |
| 350 | + if (WARN(err, "error %d on element %d\n", err, i)) |
| 351 | + break; |
| 352 | + if (err == 0) |
| 353 | + set_bit(i, obj_in_table); |
| 354 | + } |
| 355 | + |
| 356 | + pr_info("test %d random rhlist add/delete operations\n", entries); |
| 357 | + for (j = 0; j < entries; j++) { |
| 358 | + u32 i = prandom_u32_max(entries); |
| 359 | + u32 prand = prandom_u32(); |
| 360 | + |
| 361 | + cond_resched(); |
| 362 | + |
| 363 | + if (prand == 0) |
| 364 | + prand = prandom_u32(); |
| 365 | + |
| 366 | + if (prand & 1) { |
| 367 | + prand >>= 1; |
| 368 | + continue; |
| 369 | + } |
| 370 | + |
| 371 | + err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); |
| 372 | + if (test_bit(i, obj_in_table)) { |
| 373 | + clear_bit(i, obj_in_table); |
| 374 | + if (WARN(err, "cannot remove element at slot %d", i)) |
| 375 | + continue; |
| 376 | + } else { |
| 377 | + if (WARN(err != -ENOENT, "removed non-existant element %d, error %d not %d", |
| 378 | + i, err, -ENOENT)) |
| 379 | + continue; |
| 380 | + } |
| 381 | + |
| 382 | + if (prand & 1) { |
| 383 | + prand >>= 1; |
| 384 | + continue; |
| 385 | + } |
| 386 | + |
| 387 | + err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); |
| 388 | + if (err == 0) { |
| 389 | + if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i)) |
| 390 | + continue; |
| 391 | + } else { |
| 392 | + if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i)) |
| 393 | + continue; |
| 394 | + } |
| 395 | + |
| 396 | + if (prand & 1) { |
| 397 | + prand >>= 1; |
| 398 | + continue; |
| 399 | + } |
| 400 | + |
| 401 | + i = prandom_u32_max(entries); |
| 402 | + if (test_bit(i, obj_in_table)) { |
| 403 | + err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); |
| 404 | + WARN(err, "cannot remove element at slot %d", i); |
| 405 | + if (err == 0) |
| 406 | + clear_bit(i, obj_in_table); |
| 407 | + } else { |
| 408 | + err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); |
| 409 | + WARN(err, "failed to insert object %d", i); |
| 410 | + if (err == 0) |
| 411 | + set_bit(i, obj_in_table); |
| 412 | + } |
| 413 | + } |
| 414 | + |
| 415 | + for (i = 0; i < entries; i++) { |
| 416 | + cond_resched(); |
| 417 | + err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params); |
| 418 | + if (test_bit(i, obj_in_table)) { |
| 419 | + if (WARN(err, "cannot remove element at slot %d", i)) |
| 420 | + continue; |
| 421 | + } else { |
| 422 | + if (WARN(err != -ENOENT, "removed non-existant element, error %d not %d", |
| 423 | + err, -ENOENT)) |
| 424 | + continue; |
| 425 | + } |
| 426 | + } |
| 427 | + |
| 428 | + rhltable_destroy(&rhlt); |
| 429 | +out_free: |
| 430 | + vfree(rhl_test_objects); |
| 431 | + vfree(obj_in_table); |
| 432 | + return ret; |
| 433 | +} |
248 | 434 |
|
249 | 435 | static int __init test_rhashtable_max(struct test_obj *array,
|
250 | 436 | unsigned int entries)
|
@@ -480,11 +666,17 @@ static int __init test_rht_init(void)
|
480 | 666 | failed_threads++;
|
481 | 667 | }
|
482 | 668 | }
|
483 |
| - pr_info("Started %d threads, %d failed\n", |
484 |
| - started_threads, failed_threads); |
485 | 669 | rhashtable_destroy(&ht);
|
486 | 670 | vfree(tdata);
|
487 | 671 | vfree(objs);
|
| 672 | + |
| 673 | + /* |
| 674 | + * rhltable_remove is very expensive, default values can cause test |
| 675 | + * to run for 2 minutes or more, use a smaller number instead. |
| 676 | + */ |
| 677 | + err = test_rhltable(entries / 16); |
| 678 | + pr_info("Started %d threads, %d failed, rhltable test returns %d\n", |
| 679 | + started_threads, failed_threads, err); |
488 | 680 | return 0;
|
489 | 681 | }
|
490 | 682 |
|
|
0 commit comments