aboutsummaryrefslogtreecommitdiff
path: root/util/qht.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/qht.c')
-rw-r--r--util/qht.c138
1 files changed, 104 insertions, 34 deletions
diff --git a/util/qht.c b/util/qht.c
index 1e3a072e25..aa51be3c52 100644
--- a/util/qht.c
+++ b/util/qht.c
@@ -89,6 +89,19 @@
#define QHT_BUCKET_ENTRIES 4
#endif
+enum qht_iter_type {
+ QHT_ITER_VOID, /* do nothing; use retvoid */
+ QHT_ITER_RM, /* remove element if retbool returns true */
+};
+
+struct qht_iter {
+ union {
+ qht_iter_func_t retvoid;
+ qht_iter_bool_func_t retbool;
+ } f;
+ enum qht_iter_type type;
+};
+
/*
* Do _not_ use qemu_mutex_[try]lock directly! Use these macros, otherwise
* the profiler (QSP) will deadlock.
@@ -223,7 +236,7 @@ static inline void qht_head_init(struct qht_bucket *b)
}
static inline
-struct qht_bucket *qht_map_to_bucket(struct qht_map *map, uint32_t hash)
+struct qht_bucket *qht_map_to_bucket(const struct qht_map *map, uint32_t hash)
{
return &map->buckets[hash & (map->n_buckets - 1)];
}
@@ -255,7 +268,8 @@ static void qht_map_unlock_buckets(struct qht_map *map)
* Call with at least a bucket lock held.
* @map should be the value read before acquiring the lock (or locks).
*/
-static inline bool qht_map_is_stale__locked(struct qht *ht, struct qht_map *map)
+static inline bool qht_map_is_stale__locked(const struct qht *ht,
+ const struct qht_map *map)
{
return map != ht->map;
}
@@ -324,12 +338,12 @@ struct qht_bucket *qht_bucket_lock__no_stale(struct qht *ht, uint32_t hash,
return b;
}
-static inline bool qht_map_needs_resize(struct qht_map *map)
+static inline bool qht_map_needs_resize(const struct qht_map *map)
{
return atomic_read(&map->n_added_buckets) > map->n_added_buckets_threshold;
}
-static inline void qht_chain_destroy(struct qht_bucket *head)
+static inline void qht_chain_destroy(const struct qht_bucket *head)
{
struct qht_bucket *curr = head->next;
struct qht_bucket *prev;
@@ -469,10 +483,10 @@ bool qht_reset_size(struct qht *ht, size_t n_elems)
}
static inline
-void *qht_do_lookup(struct qht_bucket *head, qht_lookup_func_t func,
+void *qht_do_lookup(const struct qht_bucket *head, qht_lookup_func_t func,
const void *userp, uint32_t hash)
{
- struct qht_bucket *b = head;
+ const struct qht_bucket *b = head;
int i;
do {
@@ -496,7 +510,7 @@ void *qht_do_lookup(struct qht_bucket *head, qht_lookup_func_t func,
}
static __attribute__((noinline))
-void *qht_lookup__slowpath(struct qht_bucket *b, qht_lookup_func_t func,
+void *qht_lookup__slowpath(const struct qht_bucket *b, qht_lookup_func_t func,
const void *userp, uint32_t hash)
{
unsigned int version;
@@ -509,11 +523,11 @@ void *qht_lookup__slowpath(struct qht_bucket *b, qht_lookup_func_t func,
return ret;
}
-void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash,
+void *qht_lookup_custom(const struct qht *ht, const void *userp, uint32_t hash,
qht_lookup_func_t func)
{
- struct qht_bucket *b;
- struct qht_map *map;
+ const struct qht_bucket *b;
+ const struct qht_map *map;
unsigned int version;
void *ret;
@@ -532,13 +546,16 @@ void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash,
return qht_lookup__slowpath(b, func, userp, hash);
}
-void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash)
+void *qht_lookup(const struct qht *ht, const void *userp, uint32_t hash)
{
return qht_lookup_custom(ht, userp, hash, ht->cmp);
}
-/* call with head->lock held */
-static void *qht_insert__locked(struct qht *ht, struct qht_map *map,
+/*
+ * call with head->lock held
+ * @ht is const since it is only used for ht->cmp()
+ */
+static void *qht_insert__locked(const struct qht *ht, struct qht_map *map,
struct qht_bucket *head, void *p, uint32_t hash,
bool *needs_resize)
{
@@ -632,7 +649,7 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing)
return false;
}
-static inline bool qht_entry_is_last(struct qht_bucket *b, int pos)
+static inline bool qht_entry_is_last(const struct qht_bucket *b, int pos)
{
if (pos == QHT_BUCKET_ENTRIES - 1) {
if (b->next == NULL) {
@@ -658,7 +675,7 @@ qht_entry_move(struct qht_bucket *to, int i, struct qht_bucket *from, int j)
}
/*
- * Find the last valid entry in @head, and swap it with @orig[pos], which has
+ * Find the last valid entry in @orig, and swap it with @orig[pos], which has
* just been invalidated.
*/
static inline void qht_bucket_remove_entry(struct qht_bucket *orig, int pos)
@@ -692,8 +709,7 @@ static inline void qht_bucket_remove_entry(struct qht_bucket *orig, int pos)
/* call with b->lock held */
static inline
-bool qht_remove__locked(struct qht_map *map, struct qht_bucket *head,
- const void *p, uint32_t hash)
+bool qht_remove__locked(struct qht_bucket *head, const void *p, uint32_t hash)
{
struct qht_bucket *b = head;
int i;
@@ -728,15 +744,16 @@ bool qht_remove(struct qht *ht, const void *p, uint32_t hash)
qht_debug_assert(p);
b = qht_bucket_lock__no_stale(ht, hash, &map);
- ret = qht_remove__locked(map, b, p, hash);
+ ret = qht_remove__locked(b, p, hash);
qht_bucket_debug__locked(b);
qemu_spin_unlock(&b->lock);
return ret;
}
-static inline void qht_bucket_iter(struct qht *ht, struct qht_bucket *b,
- qht_iter_func_t func, void *userp)
+static inline void qht_bucket_iter(struct qht_bucket *head,
+ const struct qht_iter *iter, void *userp)
{
+ struct qht_bucket *b = head;
int i;
do {
@@ -744,37 +761,83 @@ static inline void qht_bucket_iter(struct qht *ht, struct qht_bucket *b,
if (b->pointers[i] == NULL) {
return;
}
- func(ht, b->pointers[i], b->hashes[i], userp);
+ switch (iter->type) {
+ case QHT_ITER_VOID:
+ iter->f.retvoid(b->pointers[i], b->hashes[i], userp);
+ break;
+ case QHT_ITER_RM:
+ if (iter->f.retbool(b->pointers[i], b->hashes[i], userp)) {
+ /* replace i with the last valid element in the bucket */
+ seqlock_write_begin(&head->sequence);
+ qht_bucket_remove_entry(b, i);
+ seqlock_write_end(&head->sequence);
+ qht_bucket_debug__locked(b);
+ /* reevaluate i, since it just got replaced */
+ i--;
+ continue;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
b = b->next;
} while (b);
}
/* call with all of the map's locks held */
-static inline void qht_map_iter__all_locked(struct qht *ht, struct qht_map *map,
- qht_iter_func_t func, void *userp)
+static inline void qht_map_iter__all_locked(struct qht_map *map,
+ const struct qht_iter *iter,
+ void *userp)
{
size_t i;
for (i = 0; i < map->n_buckets; i++) {
- qht_bucket_iter(ht, &map->buckets[i], func, userp);
+ qht_bucket_iter(&map->buckets[i], iter, userp);
}
}
-void qht_iter(struct qht *ht, qht_iter_func_t func, void *userp)
+static inline void
+do_qht_iter(struct qht *ht, const struct qht_iter *iter, void *userp)
{
struct qht_map *map;
map = atomic_rcu_read(&ht->map);
qht_map_lock_buckets(map);
- /* Note: ht here is merely for carrying ht->mode; ht->map won't be read */
- qht_map_iter__all_locked(ht, map, func, userp);
+ qht_map_iter__all_locked(map, iter, userp);
qht_map_unlock_buckets(map);
}
-static void qht_map_copy(struct qht *ht, void *p, uint32_t hash, void *userp)
+void qht_iter(struct qht *ht, qht_iter_func_t func, void *userp)
+{
+ const struct qht_iter iter = {
+ .f.retvoid = func,
+ .type = QHT_ITER_VOID,
+ };
+
+ do_qht_iter(ht, &iter, userp);
+}
+
+void qht_iter_remove(struct qht *ht, qht_iter_bool_func_t func, void *userp)
+{
+ const struct qht_iter iter = {
+ .f.retbool = func,
+ .type = QHT_ITER_RM,
+ };
+
+ do_qht_iter(ht, &iter, userp);
+}
+
+struct qht_map_copy_data {
+ struct qht *ht;
+ struct qht_map *new;
+};
+
+static void qht_map_copy(void *p, uint32_t hash, void *userp)
{
- struct qht_map *new = userp;
+ struct qht_map_copy_data *data = userp;
+ struct qht *ht = data->ht;
+ struct qht_map *new = data->new;
struct qht_bucket *b = qht_map_to_bucket(new, hash);
/* no need to acquire b->lock because no thread has seen this map yet */
@@ -788,6 +851,11 @@ static void qht_map_copy(struct qht *ht, void *p, uint32_t hash, void *userp)
static void qht_do_resize_reset(struct qht *ht, struct qht_map *new, bool reset)
{
struct qht_map *old;
+ const struct qht_iter iter = {
+ .f.retvoid = qht_map_copy,
+ .type = QHT_ITER_VOID,
+ };
+ struct qht_map_copy_data data;
old = ht->map;
qht_map_lock_buckets(old);
@@ -802,7 +870,9 @@ static void qht_do_resize_reset(struct qht *ht, struct qht_map *new, bool reset)
}
g_assert(new->n_buckets != old->n_buckets);
- qht_map_iter__all_locked(ht, old, qht_map_copy, new);
+ data.ht = ht;
+ data.new = new;
+ qht_map_iter__all_locked(old, &iter, &data);
qht_map_debug__all_locked(new);
atomic_rcu_set(&ht->map, new);
@@ -829,9 +899,9 @@ bool qht_resize(struct qht *ht, size_t n_elems)
}
/* pass @stats to qht_statistics_destroy() when done */
-void qht_statistics_init(struct qht *ht, struct qht_stats *stats)
+void qht_statistics_init(const struct qht *ht, struct qht_stats *stats)
{
- struct qht_map *map;
+ const struct qht_map *map;
int i;
map = atomic_rcu_read(&ht->map);
@@ -848,8 +918,8 @@ void qht_statistics_init(struct qht *ht, struct qht_stats *stats)
stats->head_buckets = map->n_buckets;
for (i = 0; i < map->n_buckets; i++) {
- struct qht_bucket *head = &map->buckets[i];
- struct qht_bucket *b;
+ const struct qht_bucket *head = &map->buckets[i];
+ const struct qht_bucket *b;
unsigned int version;
size_t buckets;
size_t entries;