diff options
Diffstat (limited to 'lib/rhashtable.c')
-rw-r--r-- | lib/rhashtable.c | 300 |
1 files changed, 224 insertions, 76 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 56054e541a0f..32d0ad058380 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c | |||
@@ -378,22 +378,8 @@ static void rht_deferred_worker(struct work_struct *work) | |||
378 | schedule_work(&ht->run_work); | 378 | schedule_work(&ht->run_work); |
379 | } | 379 | } |
380 | 380 | ||
381 | static bool rhashtable_check_elasticity(struct rhashtable *ht, | 381 | static int rhashtable_insert_rehash(struct rhashtable *ht, |
382 | struct bucket_table *tbl, | 382 | struct bucket_table *tbl) |
383 | unsigned int hash) | ||
384 | { | ||
385 | unsigned int elasticity = ht->elasticity; | ||
386 | struct rhash_head *head; | ||
387 | |||
388 | rht_for_each(head, tbl, hash) | ||
389 | if (!--elasticity) | ||
390 | return true; | ||
391 | |||
392 | return false; | ||
393 | } | ||
394 | |||
395 | int rhashtable_insert_rehash(struct rhashtable *ht, | ||
396 | struct bucket_table *tbl) | ||
397 | { | 383 | { |
398 | struct bucket_table *old_tbl; | 384 | struct bucket_table *old_tbl; |
399 | struct bucket_table *new_tbl; | 385 | struct bucket_table *new_tbl; |
@@ -439,61 +425,172 @@ fail: | |||
439 | 425 | ||
440 | return err; | 426 | return err; |
441 | } | 427 | } |
442 | EXPORT_SYMBOL_GPL(rhashtable_insert_rehash); | ||
443 | 428 | ||
444 | struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht, | 429 | static void *rhashtable_lookup_one(struct rhashtable *ht, |
445 | const void *key, | 430 | struct bucket_table *tbl, unsigned int hash, |
446 | struct rhash_head *obj, | 431 | const void *key, struct rhash_head *obj) |
447 | struct bucket_table *tbl) | ||
448 | { | 432 | { |
433 | struct rhashtable_compare_arg arg = { | ||
434 | .ht = ht, | ||
435 | .key = key, | ||
436 | }; | ||
437 | struct rhash_head __rcu **pprev; | ||
449 | struct rhash_head *head; | 438 | struct rhash_head *head; |
450 | unsigned int hash; | 439 | int elasticity; |
451 | int err; | ||
452 | 440 | ||
453 | tbl = rhashtable_last_table(ht, tbl); | 441 | elasticity = ht->elasticity; |
454 | hash = head_hashfn(ht, tbl, obj); | 442 | pprev = &tbl->buckets[hash]; |
455 | spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); | 443 | rht_for_each(head, tbl, hash) { |
444 | struct rhlist_head *list; | ||
445 | struct rhlist_head *plist; | ||
456 | 446 | ||
457 | err = -EEXIST; | 447 | elasticity--; |
458 | if (key && rhashtable_lookup_fast(ht, key, ht->p)) | 448 | if (!key || |
459 | goto exit; | 449 | (ht->p.obj_cmpfn ? |
450 | ht->p.obj_cmpfn(&arg, rht_obj(ht, head)) : | ||
451 | rhashtable_compare(&arg, rht_obj(ht, head)))) | ||
452 | continue; | ||
460 | 453 | ||
461 | err = -E2BIG; | 454 | if (!ht->rhlist) |
462 | if (unlikely(rht_grow_above_max(ht, tbl))) | 455 | return rht_obj(ht, head); |
463 | goto exit; | 456 | |
457 | list = container_of(obj, struct rhlist_head, rhead); | ||
458 | plist = container_of(head, struct rhlist_head, rhead); | ||
459 | |||
460 | RCU_INIT_POINTER(list->next, plist); | ||
461 | head = rht_dereference_bucket(head->next, tbl, hash); | ||
462 | RCU_INIT_POINTER(list->rhead.next, head); | ||
463 | rcu_assign_pointer(*pprev, obj); | ||
464 | |||
465 | return NULL; | ||
466 | } | ||
467 | |||
468 | if (elasticity <= 0) | ||
469 | return ERR_PTR(-EAGAIN); | ||
470 | |||
471 | return ERR_PTR(-ENOENT); | ||
472 | } | ||
473 | |||
474 | static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht, | ||
475 | struct bucket_table *tbl, | ||
476 | unsigned int hash, | ||
477 | struct rhash_head *obj, | ||
478 | void *data) | ||
479 | { | ||
480 | struct bucket_table *new_tbl; | ||
481 | struct rhash_head *head; | ||
482 | |||
483 | if (!IS_ERR_OR_NULL(data)) | ||
484 | return ERR_PTR(-EEXIST); | ||
485 | |||
486 | if (PTR_ERR(data) != -EAGAIN && PTR_ERR(data) != -ENOENT) | ||
487 | return ERR_CAST(data); | ||
464 | 488 | ||
465 | err = -EAGAIN; | 489 | new_tbl = rcu_dereference(tbl->future_tbl); |
466 | if (rhashtable_check_elasticity(ht, tbl, hash) || | 490 | if (new_tbl) |
467 | rht_grow_above_100(ht, tbl)) | 491 | return new_tbl; |
468 | goto exit; | ||
469 | 492 | ||
470 | err = 0; | 493 | if (PTR_ERR(data) != -ENOENT) |
494 | return ERR_CAST(data); | ||
495 | |||
496 | if (unlikely(rht_grow_above_max(ht, tbl))) | ||
497 | return ERR_PTR(-E2BIG); | ||
498 | |||
499 | if (unlikely(rht_grow_above_100(ht, tbl))) | ||
500 | return ERR_PTR(-EAGAIN); | ||
471 | 501 | ||
472 | head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); | 502 | head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); |
473 | 503 | ||
474 | RCU_INIT_POINTER(obj->next, head); | 504 | RCU_INIT_POINTER(obj->next, head); |
505 | if (ht->rhlist) { | ||
506 | struct rhlist_head *list; | ||
507 | |||
508 | list = container_of(obj, struct rhlist_head, rhead); | ||
509 | RCU_INIT_POINTER(list->next, NULL); | ||
510 | } | ||
475 | 511 | ||
476 | rcu_assign_pointer(tbl->buckets[hash], obj); | 512 | rcu_assign_pointer(tbl->buckets[hash], obj); |
477 | 513 | ||
478 | atomic_inc(&ht->nelems); | 514 | atomic_inc(&ht->nelems); |
515 | if (rht_grow_above_75(ht, tbl)) | ||
516 | schedule_work(&ht->run_work); | ||
479 | 517 | ||
480 | exit: | 518 | return NULL; |
481 | spin_unlock(rht_bucket_lock(tbl, hash)); | 519 | } |
482 | 520 | ||
483 | if (err == 0) | 521 | static void *rhashtable_try_insert(struct rhashtable *ht, const void *key, |
484 | return NULL; | 522 | struct rhash_head *obj) |
485 | else if (err == -EAGAIN) | 523 | { |
486 | return tbl; | 524 | struct bucket_table *new_tbl; |
487 | else | 525 | struct bucket_table *tbl; |
488 | return ERR_PTR(err); | 526 | unsigned int hash; |
527 | spinlock_t *lock; | ||
528 | void *data; | ||
529 | |||
530 | tbl = rcu_dereference(ht->tbl); | ||
531 | |||
532 | /* All insertions must grab the oldest table containing | ||
533 | * the hashed bucket that is yet to be rehashed. | ||
534 | */ | ||
535 | for (;;) { | ||
536 | hash = rht_head_hashfn(ht, tbl, obj, ht->p); | ||
537 | lock = rht_bucket_lock(tbl, hash); | ||
538 | spin_lock_bh(lock); | ||
539 | |||
540 | if (tbl->rehash <= hash) | ||
541 | break; | ||
542 | |||
543 | spin_unlock_bh(lock); | ||
544 | tbl = rcu_dereference(tbl->future_tbl); | ||
545 | } | ||
546 | |||
547 | data = rhashtable_lookup_one(ht, tbl, hash, key, obj); | ||
548 | new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data); | ||
549 | if (PTR_ERR(new_tbl) != -EEXIST) | ||
550 | data = ERR_CAST(new_tbl); | ||
551 | |||
552 | while (!IS_ERR_OR_NULL(new_tbl)) { | ||
553 | tbl = new_tbl; | ||
554 | hash = rht_head_hashfn(ht, tbl, obj, ht->p); | ||
555 | spin_lock_nested(rht_bucket_lock(tbl, hash), | ||
556 | SINGLE_DEPTH_NESTING); | ||
557 | |||
558 | data = rhashtable_lookup_one(ht, tbl, hash, key, obj); | ||
559 | new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data); | ||
560 | if (PTR_ERR(new_tbl) != -EEXIST) | ||
561 | data = ERR_CAST(new_tbl); | ||
562 | |||
563 | spin_unlock(rht_bucket_lock(tbl, hash)); | ||
564 | } | ||
565 | |||
566 | spin_unlock_bh(lock); | ||
567 | |||
568 | if (PTR_ERR(data) == -EAGAIN) | ||
569 | data = ERR_PTR(rhashtable_insert_rehash(ht, tbl) ?: | ||
570 | -EAGAIN); | ||
571 | |||
572 | return data; | ||
573 | } | ||
574 | |||
575 | void *rhashtable_insert_slow(struct rhashtable *ht, const void *key, | ||
576 | struct rhash_head *obj) | ||
577 | { | ||
578 | void *data; | ||
579 | |||
580 | do { | ||
581 | rcu_read_lock(); | ||
582 | data = rhashtable_try_insert(ht, key, obj); | ||
583 | rcu_read_unlock(); | ||
584 | } while (PTR_ERR(data) == -EAGAIN); | ||
585 | |||
586 | return data; | ||
489 | } | 587 | } |
490 | EXPORT_SYMBOL_GPL(rhashtable_insert_slow); | 588 | EXPORT_SYMBOL_GPL(rhashtable_insert_slow); |
491 | 589 | ||
492 | /** | 590 | /** |
493 | * rhashtable_walk_init - Initialise an iterator | 591 | * rhashtable_walk_enter - Initialise an iterator |
494 | * @ht: Table to walk over | 592 | * @ht: Table to walk over |
495 | * @iter: Hash table Iterator | 593 | * @iter: Hash table Iterator |
496 | * @gfp: GFP flags for allocations | ||
497 | * | 594 | * |
498 | * This function prepares a hash table walk. | 595 | * This function prepares a hash table walk. |
499 | * | 596 | * |
@@ -508,30 +605,22 @@ EXPORT_SYMBOL_GPL(rhashtable_insert_slow); | |||
508 | * This function may sleep so you must not call it from interrupt | 605 | * This function may sleep so you must not call it from interrupt |
509 | * context or with spin locks held. | 606 | * context or with spin locks held. |
510 | * | 607 | * |
511 | * You must call rhashtable_walk_exit if this function returns | 608 | * You must call rhashtable_walk_exit after this function returns. |
512 | * successfully. | ||
513 | */ | 609 | */ |
514 | int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter, | 610 | void rhashtable_walk_enter(struct rhashtable *ht, struct rhashtable_iter *iter) |
515 | gfp_t gfp) | ||
516 | { | 611 | { |
517 | iter->ht = ht; | 612 | iter->ht = ht; |
518 | iter->p = NULL; | 613 | iter->p = NULL; |
519 | iter->slot = 0; | 614 | iter->slot = 0; |
520 | iter->skip = 0; | 615 | iter->skip = 0; |
521 | 616 | ||
522 | iter->walker = kmalloc(sizeof(*iter->walker), gfp); | ||
523 | if (!iter->walker) | ||
524 | return -ENOMEM; | ||
525 | |||
526 | spin_lock(&ht->lock); | 617 | spin_lock(&ht->lock); |
527 | iter->walker->tbl = | 618 | iter->walker.tbl = |
528 | rcu_dereference_protected(ht->tbl, lockdep_is_held(&ht->lock)); | 619 | rcu_dereference_protected(ht->tbl, lockdep_is_held(&ht->lock)); |
529 | list_add(&iter->walker->list, &iter->walker->tbl->walkers); | 620 | list_add(&iter->walker.list, &iter->walker.tbl->walkers); |
530 | spin_unlock(&ht->lock); | 621 | spin_unlock(&ht->lock); |
531 | |||
532 | return 0; | ||
533 | } | 622 | } |
534 | EXPORT_SYMBOL_GPL(rhashtable_walk_init); | 623 | EXPORT_SYMBOL_GPL(rhashtable_walk_enter); |
535 | 624 | ||
536 | /** | 625 | /** |
537 | * rhashtable_walk_exit - Free an iterator | 626 | * rhashtable_walk_exit - Free an iterator |
@@ -542,10 +631,9 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_init); | |||
542 | void rhashtable_walk_exit(struct rhashtable_iter *iter) | 631 | void rhashtable_walk_exit(struct rhashtable_iter *iter) |
543 | { | 632 | { |
544 | spin_lock(&iter->ht->lock); | 633 | spin_lock(&iter->ht->lock); |
545 | if (iter->walker->tbl) | 634 | if (iter->walker.tbl) |
546 | list_del(&iter->walker->list); | 635 | list_del(&iter->walker.list); |
547 | spin_unlock(&iter->ht->lock); | 636 | spin_unlock(&iter->ht->lock); |
548 | kfree(iter->walker); | ||
549 | } | 637 | } |
550 | EXPORT_SYMBOL_GPL(rhashtable_walk_exit); | 638 | EXPORT_SYMBOL_GPL(rhashtable_walk_exit); |
551 | 639 | ||
@@ -571,12 +659,12 @@ int rhashtable_walk_start(struct rhashtable_iter *iter) | |||
571 | rcu_read_lock(); | 659 | rcu_read_lock(); |
572 | 660 | ||
573 | spin_lock(&ht->lock); | 661 | spin_lock(&ht->lock); |
574 | if (iter->walker->tbl) | 662 | if (iter->walker.tbl) |
575 | list_del(&iter->walker->list); | 663 | list_del(&iter->walker.list); |
576 | spin_unlock(&ht->lock); | 664 | spin_unlock(&ht->lock); |
577 | 665 | ||
578 | if (!iter->walker->tbl) { | 666 | if (!iter->walker.tbl) { |
579 | iter->walker->tbl = rht_dereference_rcu(ht->tbl, ht); | 667 | iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht); |
580 | return -EAGAIN; | 668 | return -EAGAIN; |
581 | } | 669 | } |
582 | 670 | ||
@@ -598,12 +686,17 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_start); | |||
598 | */ | 686 | */ |
599 | void *rhashtable_walk_next(struct rhashtable_iter *iter) | 687 | void *rhashtable_walk_next(struct rhashtable_iter *iter) |
600 | { | 688 | { |
601 | struct bucket_table *tbl = iter->walker->tbl; | 689 | struct bucket_table *tbl = iter->walker.tbl; |
690 | struct rhlist_head *list = iter->list; | ||
602 | struct rhashtable *ht = iter->ht; | 691 | struct rhashtable *ht = iter->ht; |
603 | struct rhash_head *p = iter->p; | 692 | struct rhash_head *p = iter->p; |
693 | bool rhlist = ht->rhlist; | ||
604 | 694 | ||
605 | if (p) { | 695 | if (p) { |
606 | p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot); | 696 | if (!rhlist || !(list = rcu_dereference(list->next))) { |
697 | p = rcu_dereference(p->next); | ||
698 | list = container_of(p, struct rhlist_head, rhead); | ||
699 | } | ||
607 | goto next; | 700 | goto next; |
608 | } | 701 | } |
609 | 702 | ||
@@ -611,6 +704,18 @@ void *rhashtable_walk_next(struct rhashtable_iter *iter) | |||
611 | int skip = iter->skip; | 704 | int skip = iter->skip; |
612 | 705 | ||
613 | rht_for_each_rcu(p, tbl, iter->slot) { | 706 | rht_for_each_rcu(p, tbl, iter->slot) { |
707 | if (rhlist) { | ||
708 | list = container_of(p, struct rhlist_head, | ||
709 | rhead); | ||
710 | do { | ||
711 | if (!skip) | ||
712 | goto next; | ||
713 | skip--; | ||
714 | list = rcu_dereference(list->next); | ||
715 | } while (list); | ||
716 | |||
717 | continue; | ||
718 | } | ||
614 | if (!skip) | 719 | if (!skip) |
615 | break; | 720 | break; |
616 | skip--; | 721 | skip--; |
@@ -620,7 +725,8 @@ next: | |||
620 | if (!rht_is_a_nulls(p)) { | 725 | if (!rht_is_a_nulls(p)) { |
621 | iter->skip++; | 726 | iter->skip++; |
622 | iter->p = p; | 727 | iter->p = p; |
623 | return rht_obj(ht, p); | 728 | iter->list = list; |
729 | return rht_obj(ht, rhlist ? &list->rhead : p); | ||
624 | } | 730 | } |
625 | 731 | ||
626 | iter->skip = 0; | 732 | iter->skip = 0; |
@@ -631,8 +737,8 @@ next: | |||
631 | /* Ensure we see any new tables. */ | 737 | /* Ensure we see any new tables. */ |
632 | smp_rmb(); | 738 | smp_rmb(); |
633 | 739 | ||
634 | iter->walker->tbl = rht_dereference_rcu(tbl->future_tbl, ht); | 740 | iter->walker.tbl = rht_dereference_rcu(tbl->future_tbl, ht); |
635 | if (iter->walker->tbl) { | 741 | if (iter->walker.tbl) { |
636 | iter->slot = 0; | 742 | iter->slot = 0; |
637 | iter->skip = 0; | 743 | iter->skip = 0; |
638 | return ERR_PTR(-EAGAIN); | 744 | return ERR_PTR(-EAGAIN); |
@@ -652,7 +758,7 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) | |||
652 | __releases(RCU) | 758 | __releases(RCU) |
653 | { | 759 | { |
654 | struct rhashtable *ht; | 760 | struct rhashtable *ht; |
655 | struct bucket_table *tbl = iter->walker->tbl; | 761 | struct bucket_table *tbl = iter->walker.tbl; |
656 | 762 | ||
657 | if (!tbl) | 763 | if (!tbl) |
658 | goto out; | 764 | goto out; |
@@ -661,9 +767,9 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) | |||
661 | 767 | ||
662 | spin_lock(&ht->lock); | 768 | spin_lock(&ht->lock); |
663 | if (tbl->rehash < tbl->size) | 769 | if (tbl->rehash < tbl->size) |
664 | list_add(&iter->walker->list, &tbl->walkers); | 770 | list_add(&iter->walker.list, &tbl->walkers); |
665 | else | 771 | else |
666 | iter->walker->tbl = NULL; | 772 | iter->walker.tbl = NULL; |
667 | spin_unlock(&ht->lock); | 773 | spin_unlock(&ht->lock); |
668 | 774 | ||
669 | iter->p = NULL; | 775 | iter->p = NULL; |
@@ -809,6 +915,48 @@ int rhashtable_init(struct rhashtable *ht, | |||
809 | EXPORT_SYMBOL_GPL(rhashtable_init); | 915 | EXPORT_SYMBOL_GPL(rhashtable_init); |
810 | 916 | ||
811 | /** | 917 | /** |
918 | * rhltable_init - initialize a new hash list table | ||
919 | * @hlt: hash list table to be initialized | ||
920 | * @params: configuration parameters | ||
921 | * | ||
922 | * Initializes a new hash list table. | ||
923 | * | ||
924 | * See documentation for rhashtable_init. | ||
925 | */ | ||
926 | int rhltable_init(struct rhltable *hlt, const struct rhashtable_params *params) | ||
927 | { | ||
928 | int err; | ||
929 | |||
930 | /* No rhlist NULLs marking for now. */ | ||
931 | if (params->nulls_base) | ||
932 | return -EINVAL; | ||
933 | |||
934 | err = rhashtable_init(&hlt->ht, params); | ||
935 | hlt->ht.rhlist = true; | ||
936 | return err; | ||
937 | } | ||
938 | EXPORT_SYMBOL_GPL(rhltable_init); | ||
939 | |||
940 | static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj, | ||
941 | void (*free_fn)(void *ptr, void *arg), | ||
942 | void *arg) | ||
943 | { | ||
944 | struct rhlist_head *list; | ||
945 | |||
946 | if (!ht->rhlist) { | ||
947 | free_fn(rht_obj(ht, obj), arg); | ||
948 | return; | ||
949 | } | ||
950 | |||
951 | list = container_of(obj, struct rhlist_head, rhead); | ||
952 | do { | ||
953 | obj = &list->rhead; | ||
954 | list = rht_dereference(list->next, ht); | ||
955 | free_fn(rht_obj(ht, obj), arg); | ||
956 | } while (list); | ||
957 | } | ||
958 | |||
959 | /** | ||
812 | * rhashtable_free_and_destroy - free elements and destroy hash table | 960 | * rhashtable_free_and_destroy - free elements and destroy hash table |
813 | * @ht: the hash table to destroy | 961 | * @ht: the hash table to destroy |
814 | * @free_fn: callback to release resources of element | 962 | * @free_fn: callback to release resources of element |
@@ -845,7 +993,7 @@ void rhashtable_free_and_destroy(struct rhashtable *ht, | |||
845 | pos = next, | 993 | pos = next, |
846 | next = !rht_is_a_nulls(pos) ? | 994 | next = !rht_is_a_nulls(pos) ? |
847 | rht_dereference(pos->next, ht) : NULL) | 995 | rht_dereference(pos->next, ht) : NULL) |
848 | free_fn(rht_obj(ht, pos), arg); | 996 | rhashtable_free_one(ht, pos, free_fn, arg); |
849 | } | 997 | } |
850 | } | 998 | } |
851 | 999 | ||