1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
|
/*
* Copyright (c) 2014, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CMAP_H
#define CMAP_H 1
#include <stdbool.h>
#include <stdint.h>
#include "ovs-rcu.h"
#include "util.h"
/* Concurrent hash map
* ===================
*
* A single-writer, multiple-reader hash table that efficiently supports
* duplicates.
*
*
* Thread-safety
* =============
*
* The general rules are:
*
* - Only a single thread may safely call into cmap_insert(),
* cmap_remove(), or cmap_replace() at any given time.
*
* - Any number of threads may use functions and macros that search or
* iterate through a given cmap, even in parallel with other threads
* calling cmap_insert(), cmap_remove(), or cmap_replace().
*
* There is one exception: cmap_find_protected() is only safe if no thread
* is currently calling cmap_insert(), cmap_remove(), or cmap_replace().
* (Use ordinary cmap_find() if that is not guaranteed.)
*
* - See "Iteration" below for additional thread safety rules.
*
* Writers must use special care to ensure that any elements that they remove
* do not get freed or reused until readers have finished with them. This
* includes inserting the element back into its original cmap or a different
* one. One correct way to do this is to free them from an RCU callback with
* ovsrcu_postpone().
*/
/* A concurrent hash map node, to be embedded inside the data structure being
* mapped.
*
* All nodes linked together on a chain have exactly the same hash value. */
struct cmap_node {
OVSRCU_TYPE(struct cmap_node *) next; /* Next node with same hash. */
};
static inline struct cmap_node *
cmap_node_next(const struct cmap_node *node)
{
return ovsrcu_get(struct cmap_node *, &node->next);
}
static inline struct cmap_node *
cmap_node_next_protected(const struct cmap_node *node)
{
return ovsrcu_get_protected(struct cmap_node *, &node->next);
}
/* Concurrent hash map. */
struct cmap {
OVSRCU_TYPE(struct cmap_impl *) impl;
};
/* Initializer for an empty cmap. */
#define CMAP_INITIALIZER { \
.impl = OVSRCU_INITIALIZER((struct cmap_impl *) &empty_cmap) \
}
extern OVS_ALIGNED_VAR(CACHE_LINE_SIZE) const struct cmap_impl empty_cmap;
/* Initialization. */
void cmap_init(struct cmap *);
void cmap_destroy(struct cmap *);
/* Count. */
size_t cmap_count(const struct cmap *);
bool cmap_is_empty(const struct cmap *);
/* Insertion and deletion. Return the current count after the operation. */
size_t cmap_insert(struct cmap *, struct cmap_node *, uint32_t hash);
static inline size_t cmap_remove(struct cmap *, struct cmap_node *,
uint32_t hash);
size_t cmap_replace(struct cmap *, struct cmap_node *old_node,
struct cmap_node *new_node, uint32_t hash);
/* Search.
*
* These macros iterate NODE over all of the nodes in CMAP that have hash value
* equal to HASH. MEMBER must be the name of the 'struct cmap_node' member
* within NODE.
*
* CMAP and HASH are evaluated only once. NODE is evaluated many times.
*
* After a normal exit of the loop (not through a "break;" statement) NODE is
* NULL.
*
* Thread-safety
* =============
*
* CMAP_NODE_FOR_EACH will reliably visit each of the nodes starting with
* CMAP_NODE, even with concurrent insertions and deletions. (Of
* course, if nodes are being inserted or deleted, it might or might not visit
* the nodes actually being inserted or deleted.)
*
* CMAP_NODE_FOR_EACH_PROTECTED may only be used if the containing CMAP is
* guaranteed not to change during iteration. It may be only slightly faster.
*
* CMAP_FOR_EACH_WITH_HASH will reliably visit each of the nodes with the
* specified hash in CMAP, even with concurrent insertions and deletions. (Of
* course, if nodes with the given HASH are being inserted or deleted, it might
* or might not visit the nodes actually being inserted or deleted.)
*
* CMAP_FOR_EACH_WITH_HASH_PROTECTED may only be used if CMAP is guaranteed not
* to change during iteration. It may be very slightly faster.
*/
#define CMAP_NODE_FOR_EACH(NODE, MEMBER, CMAP_NODE) \
for (INIT_MULTIVAR(NODE, MEMBER, CMAP_NODE, struct cmap_node); \
CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \
UPDATE_MULTIVAR(NODE, cmap_node_next(ITER_VAR(NODE))))
#define CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, CMAP_NODE) \
for (INIT_MULTIVAR(NODE, MEMBER, CMAP_NODE, struct cmap_node); \
CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL); \
UPDATE_MULTIVAR(NODE, cmap_node_next_protected(ITER_VAR(NODE))))
#define CMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, CMAP) \
CMAP_NODE_FOR_EACH(NODE, MEMBER, cmap_find(CMAP, HASH))
#define CMAP_FOR_EACH_WITH_HASH_PROTECTED(NODE, MEMBER, HASH, CMAP) \
CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, cmap_find_protected(CMAP, HASH))
const struct cmap_node *cmap_find(const struct cmap *, uint32_t hash);
struct cmap_node *cmap_find_protected(const struct cmap *, uint32_t hash);
/* Find node by index or find index by hash. The 'index' of a cmap entry is a
* way to combine the specific bucket and the entry of the bucket into a
* convenient single integer value. In other words, it is the index of the
* entry and each entry has an unique index. It is not used internally by
* cmap.
* Currently the functions assume index will not be larger than uint32_t. In
* OvS table size is usually much smaller than this size.*/
const struct cmap_node * cmap_find_by_index(const struct cmap *,
uint32_t index);
uint32_t cmap_find_index(const struct cmap *, uint32_t hash);
/* Looks up multiple 'hashes', when the corresponding bit in 'map' is 1,
* and sets the corresponding pointer in 'nodes', if the hash value was
* found from the 'cmap'. In other cases the 'nodes' values are not changed,
* i.e., no NULL pointers are stored there.
* Returns a map where a bit is set to 1 if the corresponding 'nodes' pointer
* was stored, 0 otherwise.
* Generally, the caller wants to use CMAP_NODE_FOR_EACH to verify for
* hash collisions. */
unsigned long cmap_find_batch(const struct cmap *cmap, unsigned long map,
uint32_t hashes[],
const struct cmap_node *nodes[]);
/* Iteration.
*
*
* Thread-safety
* =============
*
* Iteration is safe even in a cmap that is changing concurrently. However:
*
* - In the presence of concurrent calls to cmap_insert(), any given
* iteration might skip some nodes and might visit some nodes more than
* once. If this is a problem, then the iterating code should lock the
* data structure (a rwlock can be used to allow multiple threads to
* iterate in parallel).
*
* - Concurrent calls to cmap_remove() don't have the same problem. (A
* node being deleted may be visited once or not at all. Other nodes
* will be visited once.)
*
* - If the cmap is changing, it is not safe to quiesce while iterating.
* Even if the changes are done by the same thread that's performing the
* iteration (Corollary: it is not safe to call cmap_remove() and quiesce
* in the loop body).
*
*
* Example
* =======
*
* struct my_node {
* struct cmap_node cmap_node;
* int extra_data;
* };
*
* struct cmap my_map;
* struct my_node *my_node;
*
* cmap_init(&my_map);
* ...add data...
* CMAP_FOR_EACH (my_node, cmap_node, &my_map) {
* ...operate on my_node...
* }
*
* CMAP_FOR_EACH is "safe" in the sense of HMAP_FOR_EACH_SAFE. That is, it is
* safe to free the current node before going on to the next iteration. Most
* of the time, though, this doesn't matter for a cmap because node
* deallocation has to be postponed until the next grace period. This means
* that this guarantee is useful only in deallocation code already executing at
* postponed time, when it is known that the RCU grace period has already
* expired.
*/
#define CMAP_CURSOR_FOR_EACH__(NODE, CURSOR, MEMBER) \
((CURSOR)->node \
? (INIT_CONTAINER(NODE, (CURSOR)->node, MEMBER), \
cmap_cursor_advance(CURSOR), \
true) \
: (NODE = NULL, false))
#define CMAP_CURSOR_FOR_EACH(NODE, MEMBER, CURSOR, CMAP) \
for (*(CURSOR) = cmap_cursor_start(CMAP); \
CMAP_CURSOR_FOR_EACH__(NODE, CURSOR, MEMBER); \
)
#define CMAP_CURSOR_FOR_EACH_CONTINUE(NODE, MEMBER, CURSOR) \
while (CMAP_CURSOR_FOR_EACH__(NODE, CURSOR, MEMBER))
struct cmap_cursor {
const struct cmap_impl *impl;
uint32_t bucket_idx;
int entry_idx;
struct cmap_node *node;
};
struct cmap_cursor cmap_cursor_start(const struct cmap *);
void cmap_cursor_advance(struct cmap_cursor *);
/* Generate a unique name for the cursor with the __COUNTER__ macro to
* allow nesting of CMAP_FOR_EACH loops. */
#define CMAP_FOR_EACH__(NODE, MEMBER, CMAP, CURSOR_NAME) \
for (struct cmap_cursor CURSOR_NAME = cmap_cursor_start(CMAP); \
CMAP_CURSOR_FOR_EACH__(NODE, &CURSOR_NAME, MEMBER); \
)
#define CMAP_FOR_EACH(NODE, MEMBER, CMAP) \
CMAP_FOR_EACH__(NODE, MEMBER, CMAP, \
OVS_JOIN(cursor_, __COUNTER__))
static inline struct cmap_node *cmap_first(const struct cmap *);
/* Another, less preferred, form of iteration, for use in situations where it
* is difficult to maintain a pointer to a cmap_node. */
struct cmap_position {
unsigned int bucket;
unsigned int entry;
unsigned int offset;
};
struct cmap_node *cmap_next_position(const struct cmap *,
struct cmap_position *);
/* Returns the first node in 'cmap', in arbitrary order, or a null pointer if
* 'cmap' is empty. */
static inline struct cmap_node *
cmap_first(const struct cmap *cmap)
{
struct cmap_position pos = { 0, 0, 0 };
return cmap_next_position(cmap, &pos);
}
/* Removes 'node' from 'cmap'. The caller must ensure that 'cmap' cannot
* change concurrently (from another thread).
*
* 'node' must not be destroyed or modified or inserted back into 'cmap' or
* into any other concurrent hash map while any other thread might be accessing
* it. One correct way to do this is to free it from an RCU callback with
* ovsrcu_postpone().
*
* Returns the current number of nodes in the cmap after the removal. */
static inline size_t
cmap_remove(struct cmap *cmap, struct cmap_node *node, uint32_t hash)
{
return cmap_replace(cmap, node, NULL, hash);
}
#endif /* cmap.h */
|