diff options
author | David S. Miller <davem@davemloft.net> | 2016-01-13 00:21:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-01-13 00:21:27 -0500 |
commit | ddb5388ffd0ad75d07e7b78181a0b579824ba6f0 (patch) | |
tree | be1e2bd103c69d7bbace3fffd97bc3d714bbc3d7 /include/linux/rhashtable.h | |
parent | ccdf6ce6a8dba374668ae9b4d763e19903611c38 (diff) | |
parent | 67990608c8b95d2b8ccc29932376ae73d5818727 (diff) | |
download | linux-ddb5388ffd0ad75d07e7b78181a0b579824ba6f0.tar.gz linux-ddb5388ffd0ad75d07e7b78181a0b579824ba6f0.tar.bz2 linux-ddb5388ffd0ad75d07e7b78181a0b579824ba6f0.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
Diffstat (limited to 'include/linux/rhashtable.h')
-rw-r--r-- | include/linux/rhashtable.h | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index e50b31d18462..63bd7601b6de 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -823,4 +823,86 @@ out: return err; } +/* Internal function, please use rhashtable_replace_fast() instead */ +static inline int __rhashtable_replace_fast( + struct rhashtable *ht, struct bucket_table *tbl, + struct rhash_head *obj_old, struct rhash_head *obj_new, + const struct rhashtable_params params) +{ + struct rhash_head __rcu **pprev; + struct rhash_head *he; + spinlock_t *lock; + unsigned int hash; + int err = -ENOENT; + + /* Minimally, the old and new objects must have same hash + * (which should mean identifiers are the same). + */ + hash = rht_head_hashfn(ht, tbl, obj_old, params); + if (hash != rht_head_hashfn(ht, tbl, obj_new, params)) + return -EINVAL; + + lock = rht_bucket_lock(tbl, hash); + + spin_lock_bh(lock); + + pprev = &tbl->buckets[hash]; + rht_for_each(he, tbl, hash) { + if (he != obj_old) { + pprev = &he->next; + continue; + } + + rcu_assign_pointer(obj_new->next, obj_old->next); + rcu_assign_pointer(*pprev, obj_new); + err = 0; + break; + } + + spin_unlock_bh(lock); + + return err; +} + +/** + * rhashtable_replace_fast - replace an object in hash table + * @ht: hash table + * @obj_old: pointer to hash head inside object being replaced + * @obj_new: pointer to hash head inside object which is new + * @params: hash table parameters + * + * Replacing an object doesn't affect the number of elements in the hash table + * or bucket, so we don't need to worry about shrinking or expanding the + * table here. + * + * Returns zero on success, -ENOENT if the entry could not be found, + * -EINVAL if hash is not the same for the old and new objects. + */ +static inline int rhashtable_replace_fast( + struct rhashtable *ht, struct rhash_head *obj_old, + struct rhash_head *obj_new, + const struct rhashtable_params params) +{ + struct bucket_table *tbl; + int err; + + rcu_read_lock(); + + tbl = rht_dereference_rcu(ht->tbl, ht); + + /* Because we have already taken (and released) the bucket + * lock in old_tbl, if we find that future_tbl is not yet + * visible then that guarantees the entry to still be in + * the old tbl if it exists. + */ + while ((err = __rhashtable_replace_fast(ht, tbl, obj_old, + obj_new, params)) && + (tbl = rht_dereference_rcu(tbl->future_tbl, ht))) + ; + + rcu_read_unlock(); + + return err; +} + #endif /* _LINUX_RHASHTABLE_H */ |