Documentation/s390: remove outdated dasd documentation
[linux-2.6-microblaze.git] / lib / rhashtable.c
index 97f59ab..bdb7e4c 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Resizable, Scalable, Concurrent Hash Table
  *
@@ -8,10 +9,6 @@
  * Code partially derived from nft_hash
  * Rewritten with rehash code from br_multicast plus single list
  * pointer as suggested by Josh Triplett
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
  */
 
 #include <linux/atomic.h>
 
 #define HASH_DEFAULT_SIZE      64UL
 #define HASH_MIN_SIZE          4U
-#define BUCKET_LOCKS_PER_CPU   32UL
 
 union nested_table {
        union nested_table __rcu *table;
-       struct rhash_head __rcu *bucket;
+       struct rhash_lock_head *bucket;
 };
 
 static u32 head_hashfn(struct rhashtable *ht,
@@ -56,9 +52,11 @@ EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held);
 
 int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash)
 {
-       spinlock_t *lock = rht_bucket_lock(tbl, hash);
-
-       return (debug_locks) ? lockdep_is_held(lock) : 1;
+       if (!debug_locks)
+               return 1;
+       if (unlikely(tbl->nest))
+               return 1;
+       return bit_spin_is_locked(0, (unsigned long *)&tbl->buckets[hash]);
 }
 EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held);
 #else
@@ -104,7 +102,6 @@ static void bucket_table_free(const struct bucket_table *tbl)
        if (tbl->nest)
                nested_bucket_table_free(tbl);
 
-       free_bucket_spinlocks(tbl->locks);
        kvfree(tbl);
 }
 
@@ -131,9 +128,11 @@ static union nested_table *nested_table_alloc(struct rhashtable *ht,
                        INIT_RHT_NULLS_HEAD(ntbl[i].bucket);
        }
 
-       rcu_assign_pointer(*prev, ntbl);
-
-       return ntbl;
+       if (cmpxchg((union nested_table **)prev, NULL, ntbl) == NULL)
+               return ntbl;
+       /* Raced with another thread. */
+       kfree(ntbl);
+       return rcu_dereference(*prev);
 }
 
 static struct bucket_table *nested_bucket_table_alloc(struct rhashtable *ht,
@@ -169,11 +168,11 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
                                               gfp_t gfp)
 {
        struct bucket_table *tbl = NULL;
-       size_t size, max_locks;
+       size_t size;
        int i;
+       static struct lock_class_key __key;
 
-       size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]);
-       tbl = kvzalloc(size, gfp);
+       tbl = kvzalloc(struct_size(tbl, buckets, nbuckets), gfp);
 
        size = nbuckets;
 
@@ -185,18 +184,11 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
        if (tbl == NULL)
                return NULL;
 
-       tbl->size = size;
-
-       max_locks = size >> 1;
-       if (tbl->nest)
-               max_locks = min_t(size_t, max_locks, 1U << tbl->nest);
+       lockdep_init_map(&tbl->dep_map, "rhashtable_bucket", &__key, 0);
 
-       if (alloc_bucket_spinlocks(&tbl->locks, &tbl->locks_mask, max_locks,
-                                  ht->p.locks_mul, gfp) < 0) {
-               bucket_table_free(tbl);
-               return NULL;
-       }
+       tbl->size = size;
 
+       rcu_head_init(&tbl->rcu);
        INIT_LIST_HEAD(&tbl->walkers);
 
        tbl->hash_rnd = get_random_u32();
@@ -220,14 +212,15 @@ static struct bucket_table *rhashtable_last_table(struct rhashtable *ht,
        return new_tbl;
 }
 
-static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash)
+static int rhashtable_rehash_one(struct rhashtable *ht,
+                                struct rhash_lock_head **bkt,
+                                unsigned int old_hash)
 {
        struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
        struct bucket_table *new_tbl = rhashtable_last_table(ht, old_tbl);
-       struct rhash_head __rcu **pprev = rht_bucket_var(old_tbl, old_hash);
        int err = -EAGAIN;
        struct rhash_head *head, *next, *entry;
-       spinlock_t *new_bucket_lock;
+       struct rhash_head __rcu **pprev = NULL;
        unsigned int new_hash;
 
        if (new_tbl->nest)
@@ -235,7 +228,8 @@ static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash)
 
        err = -ENOENT;
 
-       rht_for_each(entry, old_tbl, old_hash) {
+       rht_for_each_from(entry, rht_ptr(bkt, old_tbl, old_hash),
+                         old_tbl, old_hash) {
                err = 0;
                next = rht_dereference_bucket(entry->next, old_tbl, old_hash);
 
@@ -250,18 +244,19 @@ static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash)
 
        new_hash = head_hashfn(ht, new_tbl, entry);
 
-       new_bucket_lock = rht_bucket_lock(new_tbl, new_hash);
+       rht_lock_nested(new_tbl, &new_tbl->buckets[new_hash], SINGLE_DEPTH_NESTING);
 
-       spin_lock_nested(new_bucket_lock, SINGLE_DEPTH_NESTING);
-       head = rht_dereference_bucket(new_tbl->buckets[new_hash],
-                                     new_tbl, new_hash);
+       head = rht_ptr(new_tbl->buckets + new_hash, new_tbl, new_hash);
 
        RCU_INIT_POINTER(entry->next, head);
 
-       rcu_assign_pointer(new_tbl->buckets[new_hash], entry);
-       spin_unlock(new_bucket_lock);
+       rht_assign_unlock(new_tbl, &new_tbl->buckets[new_hash], entry);
 
-       rcu_assign_pointer(*pprev, next);
+       if (pprev)
+               rcu_assign_pointer(*pprev, next);
+       else
+               /* Need to preserved the bit lock. */
+               rht_assign_locked(bkt, next);
 
 out:
        return err;
@@ -271,20 +266,19 @@ static int rhashtable_rehash_chain(struct rhashtable *ht,
                                    unsigned int old_hash)
 {
        struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
-       spinlock_t *old_bucket_lock;
+       struct rhash_lock_head **bkt = rht_bucket_var(old_tbl, old_hash);
        int err;
 
-       old_bucket_lock = rht_bucket_lock(old_tbl, old_hash);
+       if (!bkt)
+               return 0;
+       rht_lock(old_tbl, bkt);
 
-       spin_lock_bh(old_bucket_lock);
-       while (!(err = rhashtable_rehash_one(ht, old_hash)))
+       while (!(err = rhashtable_rehash_one(ht, bkt, old_hash)))
                ;
 
-       if (err == -ENOENT) {
-               old_tbl->rehash++;
+       if (err == -ENOENT)
                err = 0;
-       }
-       spin_unlock_bh(old_bucket_lock);
+       rht_unlock(old_tbl, bkt);
 
        return err;
 }
@@ -299,7 +293,8 @@ static int rhashtable_rehash_attach(struct rhashtable *ht,
         * rcu_assign_pointer().
         */
 
-       if (cmpxchg(&old_tbl->future_tbl, NULL, new_tbl) != NULL)
+       if (cmpxchg((struct bucket_table **)&old_tbl->future_tbl, NULL,
+                   new_tbl) != NULL)
                return -EEXIST;
 
        return 0;
@@ -330,13 +325,16 @@ static int rhashtable_rehash_table(struct rhashtable *ht)
        spin_lock(&ht->lock);
        list_for_each_entry(walker, &old_tbl->walkers, list)
                walker->tbl = NULL;
-       spin_unlock(&ht->lock);
 
        /* Wait for readers. All new readers will see the new
         * table, and thus no references to the old table will
         * remain.
+        * We do this inside the locked region so that
+        * rhashtable_walk_stop() can use rcu_head_after_call_rcu()
+        * to check if it should not re-link the table.
         */
        call_rcu(&old_tbl->rcu, bucket_table_free_rcu);
+       spin_unlock(&ht->lock);
 
        return rht_dereference(new_tbl->future_tbl, ht) ? -EAGAIN : 0;
 }
@@ -478,6 +476,7 @@ fail:
 }
 
 static void *rhashtable_lookup_one(struct rhashtable *ht,
+                                  struct rhash_lock_head **bkt,
                                   struct bucket_table *tbl, unsigned int hash,
                                   const void *key, struct rhash_head *obj)
 {
@@ -485,13 +484,12 @@ static void *rhashtable_lookup_one(struct rhashtable *ht,
                .ht = ht,
                .key = key,
        };
-       struct rhash_head __rcu **pprev;
+       struct rhash_head __rcu **pprev = NULL;
        struct rhash_head *head;
        int elasticity;
 
        elasticity = RHT_ELASTICITY;
-       pprev = rht_bucket_var(tbl, hash);
-       rht_for_each_continue(head, *pprev, tbl, hash) {
+       rht_for_each_from(head, rht_ptr(bkt, tbl, hash), tbl, hash) {
                struct rhlist_head *list;
                struct rhlist_head *plist;
 
@@ -513,7 +511,11 @@ static void *rhashtable_lookup_one(struct rhashtable *ht,
                RCU_INIT_POINTER(list->next, plist);
                head = rht_dereference_bucket(head->next, tbl, hash);
                RCU_INIT_POINTER(list->rhead.next, head);
-               rcu_assign_pointer(*pprev, obj);
+               if (pprev)
+                       rcu_assign_pointer(*pprev, obj);
+               else
+                       /* Need to preserve the bit lock */
+                       rht_assign_locked(bkt, obj);
 
                return NULL;
        }
@@ -525,12 +527,12 @@ static void *rhashtable_lookup_one(struct rhashtable *ht,
 }
 
 static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht,
+                                                 struct rhash_lock_head **bkt,
                                                  struct bucket_table *tbl,
                                                  unsigned int hash,
                                                  struct rhash_head *obj,
                                                  void *data)
 {
-       struct rhash_head __rcu **pprev;
        struct bucket_table *new_tbl;
        struct rhash_head *head;
 
@@ -553,11 +555,7 @@ static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht,
        if (unlikely(rht_grow_above_100(ht, tbl)))
                return ERR_PTR(-EAGAIN);
 
-       pprev = rht_bucket_insert(ht, tbl, hash);
-       if (!pprev)
-               return ERR_PTR(-ENOMEM);
-
-       head = rht_dereference_bucket(*pprev, tbl, hash);
+       head = rht_ptr(bkt, tbl, hash);
 
        RCU_INIT_POINTER(obj->next, head);
        if (ht->rhlist) {
@@ -567,7 +565,10 @@ static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht,
                RCU_INIT_POINTER(list->next, NULL);
        }
 
-       rcu_assign_pointer(*pprev, obj);
+       /* bkt is always the head of the list, so it holds
+        * the lock, which we need to preserve
+        */
+       rht_assign_locked(bkt, obj);
 
        atomic_inc(&ht->nelems);
        if (rht_grow_above_75(ht, tbl))
@@ -581,47 +582,35 @@ static void *rhashtable_try_insert(struct rhashtable *ht, const void *key,
 {
        struct bucket_table *new_tbl;
        struct bucket_table *tbl;
+       struct rhash_lock_head **bkt;
        unsigned int hash;
-       spinlock_t *lock;
        void *data;
 
-       tbl = rcu_dereference(ht->tbl);
-
-       /* All insertions must grab the oldest table containing
-        * the hashed bucket that is yet to be rehashed.
-        */
-       for (;;) {
-               hash = rht_head_hashfn(ht, tbl, obj, ht->p);
-               lock = rht_bucket_lock(tbl, hash);
-               spin_lock_bh(lock);
-
-               if (tbl->rehash <= hash)
-                       break;
-
-               spin_unlock_bh(lock);
-               tbl = rht_dereference_rcu(tbl->future_tbl, ht);
-       }
-
-       data = rhashtable_lookup_one(ht, tbl, hash, key, obj);
-       new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data);
-       if (PTR_ERR(new_tbl) != -EEXIST)
-               data = ERR_CAST(new_tbl);
+       new_tbl = rcu_dereference(ht->tbl);
 
-       while (!IS_ERR_OR_NULL(new_tbl)) {
+       do {
                tbl = new_tbl;
                hash = rht_head_hashfn(ht, tbl, obj, ht->p);
-               spin_lock_nested(rht_bucket_lock(tbl, hash),
-                                SINGLE_DEPTH_NESTING);
-
-               data = rhashtable_lookup_one(ht, tbl, hash, key, obj);
-               new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data);
-               if (PTR_ERR(new_tbl) != -EEXIST)
-                       data = ERR_CAST(new_tbl);
-
-               spin_unlock(rht_bucket_lock(tbl, hash));
-       }
-
-       spin_unlock_bh(lock);
+               if (rcu_access_pointer(tbl->future_tbl))
+                       /* Failure is OK */
+                       bkt = rht_bucket_var(tbl, hash);
+               else
+                       bkt = rht_bucket_insert(ht, tbl, hash);
+               if (bkt == NULL) {
+                       new_tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+                       data = ERR_PTR(-EAGAIN);
+               } else {
+                       rht_lock(tbl, bkt);
+                       data = rhashtable_lookup_one(ht, bkt, tbl,
+                                                    hash, key, obj);
+                       new_tbl = rhashtable_insert_one(ht, bkt, tbl,
+                                                       hash, obj, data);
+                       if (PTR_ERR(new_tbl) != -EEXIST)
+                               data = ERR_CAST(new_tbl);
+
+                       rht_unlock(tbl, bkt);
+               }
+       } while (!IS_ERR_OR_NULL(new_tbl));
 
        if (PTR_ERR(data) == -EAGAIN)
                data = ERR_PTR(rhashtable_insert_rehash(ht, tbl) ?:
@@ -943,10 +932,11 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter)
        ht = iter->ht;
 
        spin_lock(&ht->lock);
-       if (tbl->rehash < tbl->size)
-               list_add(&iter->walker.list, &tbl->walkers);
-       else
+       if (rcu_head_after_call_rcu(&tbl->rcu, bucket_table_free_rcu))
+               /* This bucket table is being freed, don't re-link it. */
                iter->walker.tbl = NULL;
+       else
+               list_add(&iter->walker.list, &tbl->walkers);
        spin_unlock(&ht->lock);
 
 out:
@@ -1046,11 +1036,6 @@ int rhashtable_init(struct rhashtable *ht,
 
        size = rounded_hashtable_size(&ht->p);
 
-       if (params->locks_mul)
-               ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
-       else
-               ht->p.locks_mul = BUCKET_LOCKS_PER_CPU;
-
        ht->key_len = ht->p.key_len;
        if (!params->hashfn) {
                ht->p.hashfn = jhash;
@@ -1152,7 +1137,7 @@ restart:
                        struct rhash_head *pos, *next;
 
                        cond_resched();
-                       for (pos = rht_dereference(*rht_bucket(tbl, i), ht),
+                       for (pos = rht_ptr_exclusive(rht_bucket(tbl, i)),
                             next = !rht_is_a_nulls(pos) ?
                                        rht_dereference(pos->next, ht) : NULL;
                             !rht_is_a_nulls(pos);
@@ -1179,11 +1164,10 @@ void rhashtable_destroy(struct rhashtable *ht)
 }
 EXPORT_SYMBOL_GPL(rhashtable_destroy);
 
-struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
-                                           unsigned int hash)
+struct rhash_lock_head **__rht_bucket_nested(const struct bucket_table *tbl,
+                                            unsigned int hash)
 {
        const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
-       static struct rhash_head __rcu *rhnull;
        unsigned int index = hash & ((1 << tbl->nest) - 1);
        unsigned int size = tbl->size >> tbl->nest;
        unsigned int subhash = hash;
@@ -1201,20 +1185,28 @@ struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
                subhash >>= shift;
        }
 
-       if (!ntbl) {
-               if (!rhnull)
-                       INIT_RHT_NULLS_HEAD(rhnull);
-               return &rhnull;
-       }
+       if (!ntbl)
+               return NULL;
 
        return &ntbl[subhash].bucket;
 
 }
+EXPORT_SYMBOL_GPL(__rht_bucket_nested);
+
+struct rhash_lock_head **rht_bucket_nested(const struct bucket_table *tbl,
+                                          unsigned int hash)
+{
+       static struct rhash_lock_head *rhnull;
+
+       if (!rhnull)
+               INIT_RHT_NULLS_HEAD(rhnull);
+       return __rht_bucket_nested(tbl, hash) ?: &rhnull;
+}
 EXPORT_SYMBOL_GPL(rht_bucket_nested);
 
-struct rhash_head __rcu **rht_bucket_nested_insert(struct rhashtable *ht,
-                                                  struct bucket_table *tbl,
-                                                  unsigned int hash)
+struct rhash_lock_head **rht_bucket_nested_insert(struct rhashtable *ht,
+                                                 struct bucket_table *tbl,
+                                                 unsigned int hash)
 {
        const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
        unsigned int index = hash & ((1 << tbl->nest) - 1);