|
|
@@ -170,6 +170,8 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+ INIT_LIST_HEAD(&tbl->walkers);
|
|
|
+
|
|
|
for (i = 0; i < nbuckets; i++)
|
|
|
INIT_RHT_NULLS_HEAD(tbl->buckets[i], ht, i);
|
|
|
|
|
|
@@ -264,6 +266,7 @@ static void rhashtable_rehash(struct rhashtable *ht,
|
|
|
struct bucket_table *new_tbl)
|
|
|
{
|
|
|
struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
|
|
|
+ struct rhashtable_walker *walker;
|
|
|
unsigned old_hash;
|
|
|
|
|
|
get_random_bytes(&new_tbl->hash_rnd, sizeof(new_tbl->hash_rnd));
|
|
|
@@ -284,6 +287,9 @@ static void rhashtable_rehash(struct rhashtable *ht,
|
|
|
/* Publish the new table pointer. */
|
|
|
rcu_assign_pointer(ht->tbl, new_tbl);
|
|
|
|
|
|
+ list_for_each_entry(walker, &old_tbl->walkers, list)
|
|
|
+ walker->tbl = NULL;
|
|
|
+
|
|
|
/* Wait for readers. All new readers will see the new
|
|
|
* table, and thus no references to the old table will
|
|
|
* remain.
|
|
|
@@ -358,7 +364,6 @@ static void rht_deferred_worker(struct work_struct *work)
|
|
|
{
|
|
|
struct rhashtable *ht;
|
|
|
struct bucket_table *tbl;
|
|
|
- struct rhashtable_walker *walker;
|
|
|
|
|
|
ht = container_of(work, struct rhashtable, run_work);
|
|
|
mutex_lock(&ht->mutex);
|
|
|
@@ -367,9 +372,6 @@ static void rht_deferred_worker(struct work_struct *work)
|
|
|
|
|
|
tbl = rht_dereference(ht->tbl, ht);
|
|
|
|
|
|
- list_for_each_entry(walker, &ht->walkers, list)
|
|
|
- walker->resize = true;
|
|
|
-
|
|
|
if (rht_grow_above_75(ht, tbl))
|
|
|
rhashtable_expand(ht);
|
|
|
else if (rht_shrink_below_30(ht, tbl))
|
|
|
@@ -725,11 +727,9 @@ int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
|
|
|
if (!iter->walker)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- INIT_LIST_HEAD(&iter->walker->list);
|
|
|
- iter->walker->resize = false;
|
|
|
-
|
|
|
mutex_lock(&ht->mutex);
|
|
|
- list_add(&iter->walker->list, &ht->walkers);
|
|
|
+ iter->walker->tbl = rht_dereference(ht->tbl, ht);
|
|
|
+ list_add(&iter->walker->list, &iter->walker->tbl->walkers);
|
|
|
mutex_unlock(&ht->mutex);
|
|
|
|
|
|
return 0;
|
|
|
@@ -745,7 +745,8 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_init);
|
|
|
void rhashtable_walk_exit(struct rhashtable_iter *iter)
|
|
|
{
|
|
|
mutex_lock(&iter->ht->mutex);
|
|
|
- list_del(&iter->walker->list);
|
|
|
+ if (iter->walker->tbl)
|
|
|
+ list_del(&iter->walker->list);
|
|
|
mutex_unlock(&iter->ht->mutex);
|
|
|
kfree(iter->walker);
|
|
|
}
|
|
|
@@ -767,12 +768,19 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
|
|
|
*/
|
|
|
int rhashtable_walk_start(struct rhashtable_iter *iter)
|
|
|
{
|
|
|
+ struct rhashtable *ht = iter->ht;
|
|
|
+
|
|
|
+ mutex_lock(&ht->mutex);
|
|
|
+
|
|
|
+ if (iter->walker->tbl)
|
|
|
+ list_del(&iter->walker->list);
|
|
|
+
|
|
|
rcu_read_lock();
|
|
|
|
|
|
- if (iter->walker->resize) {
|
|
|
- iter->slot = 0;
|
|
|
- iter->skip = 0;
|
|
|
- iter->walker->resize = false;
|
|
|
+ mutex_unlock(&ht->mutex);
|
|
|
+
|
|
|
+ if (!iter->walker->tbl) {
|
|
|
+ iter->walker->tbl = rht_dereference_rcu(ht->tbl, ht);
|
|
|
return -EAGAIN;
|
|
|
}
|
|
|
|
|
|
@@ -794,13 +802,11 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_start);
|
|
|
*/
|
|
|
void *rhashtable_walk_next(struct rhashtable_iter *iter)
|
|
|
{
|
|
|
- const struct bucket_table *tbl;
|
|
|
+ struct bucket_table *tbl = iter->walker->tbl;
|
|
|
struct rhashtable *ht = iter->ht;
|
|
|
struct rhash_head *p = iter->p;
|
|
|
void *obj = NULL;
|
|
|
|
|
|
- tbl = rht_dereference_rcu(ht->tbl, ht);
|
|
|
-
|
|
|
if (p) {
|
|
|
p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot);
|
|
|
goto next;
|
|
|
@@ -826,17 +832,18 @@ next:
|
|
|
iter->skip = 0;
|
|
|
}
|
|
|
|
|
|
- iter->p = NULL;
|
|
|
-
|
|
|
-out:
|
|
|
- if (iter->walker->resize) {
|
|
|
- iter->p = NULL;
|
|
|
+ iter->walker->tbl = rht_dereference_rcu(ht->future_tbl, ht);
|
|
|
+ if (iter->walker->tbl != tbl) {
|
|
|
iter->slot = 0;
|
|
|
iter->skip = 0;
|
|
|
- iter->walker->resize = false;
|
|
|
return ERR_PTR(-EAGAIN);
|
|
|
}
|
|
|
|
|
|
+ iter->walker->tbl = NULL;
|
|
|
+ iter->p = NULL;
|
|
|
+
|
|
|
+out:
|
|
|
+
|
|
|
return obj;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(rhashtable_walk_next);
|
|
|
@@ -849,7 +856,24 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_next);
|
|
|
*/
|
|
|
void rhashtable_walk_stop(struct rhashtable_iter *iter)
|
|
|
{
|
|
|
+ struct rhashtable *ht;
|
|
|
+ struct bucket_table *tbl = iter->walker->tbl;
|
|
|
+
|
|
|
rcu_read_unlock();
|
|
|
+
|
|
|
+ if (!tbl)
|
|
|
+ return;
|
|
|
+
|
|
|
+ ht = iter->ht;
|
|
|
+
|
|
|
+ mutex_lock(&ht->mutex);
|
|
|
+ if (rht_dereference(ht->tbl, ht) == tbl ||
|
|
|
+ rht_dereference(ht->future_tbl, ht) == tbl)
|
|
|
+ list_add(&iter->walker->list, &tbl->walkers);
|
|
|
+ else
|
|
|
+ iter->walker->tbl = NULL;
|
|
|
+ mutex_unlock(&ht->mutex);
|
|
|
+
|
|
|
iter->p = NULL;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
|
|
|
@@ -927,7 +951,6 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params)
|
|
|
memset(ht, 0, sizeof(*ht));
|
|
|
mutex_init(&ht->mutex);
|
|
|
memcpy(&ht->p, params, sizeof(*params));
|
|
|
- INIT_LIST_HEAD(&ht->walkers);
|
|
|
|
|
|
if (params->locks_mul)
|
|
|
ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
|