Skip to content

Commit

Permalink
rhashtable: add function to replace an element
Browse files Browse the repository at this point in the history
Add the rhashtable_replace_fast function. This replaces one object in
the table with another atomically. The hashes of the new and old objects
must be equal.

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Tom Herbert authored and David S. Miller committed Dec 16, 2015
1 parent 33f11d1 commit 3502cad
Showing 1 changed file with 82 additions and 0 deletions.
82 changes: 82 additions & 0 deletions include/linux/rhashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,4 +819,86 @@ static inline int rhashtable_remove_fast(
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 */

0 comments on commit 3502cad

Please sign in to comment.