blob: 53ffbf156e69a64c0ad54b8032a5219a7150ab16 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
Chris Luke16bcf7d2016-09-01 14:31:46 -040016/** @cond DOCUMENTATION_IS_IN_BIHASH_DOC_H */
Dave Barachdd3a57f2016-07-27 16:58:51 -040017
Dave Barach97f5af02018-02-22 09:48:45 -050018static inline void *BV (alloc_aligned) (BVT (clib_bihash) * h, uword nbytes)
19{
20 uword rv;
21
22 /* Round to an even number of cache lines */
23 nbytes += CLIB_CACHE_LINE_BYTES - 1;
24 nbytes &= ~(CLIB_CACHE_LINE_BYTES - 1);
25
26 rv = h->alloc_arena_next;
27 h->alloc_arena_next += nbytes;
28
29 if (rv >= (h->alloc_arena + h->alloc_arena_size))
30 os_out_of_memory ();
31
32 return (void *) rv;
33}
34
35
Dave Barachc3799992016-08-15 11:12:27 -040036void BV (clib_bihash_init)
37 (BVT (clib_bihash) * h, char *name, u32 nbuckets, uword memory_size)
Ed Warnickecb9cada2015-12-08 15:45:58 -070038{
Dave Barach97f5af02018-02-22 09:48:45 -050039 uword bucket_size;
Dave Barach908a5ea2017-07-14 12:42:21 -040040 int i;
Ed Warnickecb9cada2015-12-08 15:45:58 -070041
42 nbuckets = 1 << (max_log2 (nbuckets));
43
Dave Barachc3799992016-08-15 11:12:27 -040044 h->name = (u8 *) name;
Ed Warnickecb9cada2015-12-08 15:45:58 -070045 h->nbuckets = nbuckets;
46 h->log2_nbuckets = max_log2 (nbuckets);
Dave Barach908a5ea2017-07-14 12:42:21 -040047 h->cache_hits = 0;
48 h->cache_misses = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -070049
Dave Barach97f5af02018-02-22 09:48:45 -050050 h->alloc_arena = (uword) clib_mem_vm_alloc (memory_size);
51 h->alloc_arena_next = h->alloc_arena;
52 h->alloc_arena_size = memory_size;
Ed Warnickecb9cada2015-12-08 15:45:58 -070053
Dave Barach97f5af02018-02-22 09:48:45 -050054 bucket_size = nbuckets * sizeof (h->buckets[0]);
55 h->buckets = BV (alloc_aligned) (h, bucket_size);
56
57 h->writer_lock = BV (alloc_aligned) (h, CLIB_CACHE_LINE_BYTES);
JingLiuZTE4c9f2a82017-11-08 15:35:01 +080058 h->writer_lock[0] = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -070059
Dave Barach908a5ea2017-07-14 12:42:21 -040060 for (i = 0; i < nbuckets; i++)
61 BV (clib_bihash_reset_cache) (h->buckets + i);
62
Vijayabhaskar Katamreddyfb8e61c2017-12-14 13:20:50 -080063 h->fmt_fn = NULL;
64}
65
66void BV (clib_bihash_set_kvp_format_fn) (BVT (clib_bihash) * h,
67 format_function_t * fmt_fn)
68{
69 h->fmt_fn = fmt_fn;
Ed Warnickecb9cada2015-12-08 15:45:58 -070070}
71
Dave Barachc3799992016-08-15 11:12:27 -040072void BV (clib_bihash_free) (BVT (clib_bihash) * h)
Ed Warnickecb9cada2015-12-08 15:45:58 -070073{
Dave Barach97f5af02018-02-22 09:48:45 -050074 vec_free (h->working_copies);
75 vec_free (h->freelists);
76 clib_mem_vm_free ((void *) (h->alloc_arena), h->alloc_arena_size);
Dave Barachc3799992016-08-15 11:12:27 -040077 memset (h, 0, sizeof (*h));
Ed Warnickecb9cada2015-12-08 15:45:58 -070078}
79
Dave Barachc3799992016-08-15 11:12:27 -040080static
81BVT (clib_bihash_value) *
82BV (value_alloc) (BVT (clib_bihash) * h, u32 log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -070083{
Dave Barachc3799992016-08-15 11:12:27 -040084 BVT (clib_bihash_value) * rv = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -070085
Dave Barachc3799992016-08-15 11:12:27 -040086 ASSERT (h->writer_lock[0]);
87 if (log2_pages >= vec_len (h->freelists) || h->freelists[log2_pages] == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -070088 {
Dave Barach97f5af02018-02-22 09:48:45 -050089 vec_validate_init_empty (h->freelists, log2_pages, 0);
90 rv = BV (alloc_aligned) (h, (sizeof (*rv) * (1 << log2_pages)));
Dave Barachc3799992016-08-15 11:12:27 -040091 goto initialize;
Ed Warnickecb9cada2015-12-08 15:45:58 -070092 }
Dave Barachc3799992016-08-15 11:12:27 -040093 rv = h->freelists[log2_pages];
94 h->freelists[log2_pages] = rv->next_free;
Ed Warnickecb9cada2015-12-08 15:45:58 -070095
Dave Barachc3799992016-08-15 11:12:27 -040096initialize:
97 ASSERT (rv);
Dave Barachc3799992016-08-15 11:12:27 -040098 /*
99 * Latest gcc complains that the length arg is zero
100 * if we replace (1<<log2_pages) with vec_len(rv).
101 * No clue.
102 */
103 memset (rv, 0xff, sizeof (*rv) * (1 << log2_pages));
104 return rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700105}
106
107static void
Dave Barachba7ddfe2017-05-17 20:20:50 -0400108BV (value_free) (BVT (clib_bihash) * h, BVT (clib_bihash_value) * v,
109 u32 log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110{
Dave Barachc3799992016-08-15 11:12:27 -0400111 ASSERT (h->writer_lock[0]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700112
Dave Barachc3799992016-08-15 11:12:27 -0400113 ASSERT (vec_len (h->freelists) > log2_pages);
114
115 v->next_free = h->freelists[log2_pages];
116 h->freelists[log2_pages] = v;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700117}
118
119static inline void
Dave Barach908a5ea2017-07-14 12:42:21 -0400120BV (make_working_copy) (BVT (clib_bihash) * h, BVT (clib_bihash_bucket) * b)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700121{
Dave Barachc3799992016-08-15 11:12:27 -0400122 BVT (clib_bihash_value) * v;
Dave Barach908a5ea2017-07-14 12:42:21 -0400123 BVT (clib_bihash_bucket) working_bucket __attribute__ ((aligned (8)));
Dave Barachc3799992016-08-15 11:12:27 -0400124 BVT (clib_bihash_value) * working_copy;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200125 u32 thread_index = os_get_thread_index ();
Dave Barachba7ddfe2017-05-17 20:20:50 -0400126 int log2_working_copy_length;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700127
Damjan Marionf55f9b82017-05-10 21:06:28 +0200128 if (thread_index >= vec_len (h->working_copies))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700129 {
Damjan Marionf55f9b82017-05-10 21:06:28 +0200130 vec_validate (h->working_copies, thread_index);
Steve Shin871cdec2017-06-02 10:09:02 -0700131 vec_validate_init_empty (h->working_copy_lengths, thread_index, ~0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700132 }
133
Dave Barachc3799992016-08-15 11:12:27 -0400134 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700135 * working_copies are per-cpu so that near-simultaneous
136 * updates from multiple threads will not result in sporadic, spurious
Dave Barachc3799992016-08-15 11:12:27 -0400137 * lookup failures.
Ed Warnickecb9cada2015-12-08 15:45:58 -0700138 */
Damjan Marionf55f9b82017-05-10 21:06:28 +0200139 working_copy = h->working_copies[thread_index];
Dave Barachba7ddfe2017-05-17 20:20:50 -0400140 log2_working_copy_length = h->working_copy_lengths[thread_index];
Ed Warnickecb9cada2015-12-08 15:45:58 -0700141
142 h->saved_bucket.as_u64 = b->as_u64;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700143
Dave Barachba7ddfe2017-05-17 20:20:50 -0400144 if (b->log2_pages > log2_working_copy_length)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700145 {
Dave Barach97f5af02018-02-22 09:48:45 -0500146 /*
147 * It's not worth the bookkeeping to free working copies
148 * if (working_copy)
149 * clib_mem_free (working_copy);
150 */
151 working_copy = BV (alloc_aligned)
152 (h, sizeof (working_copy[0]) * (1 << b->log2_pages));
Dave Barachba7ddfe2017-05-17 20:20:50 -0400153 h->working_copy_lengths[thread_index] = b->log2_pages;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200154 h->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700155 }
156
Dave Barach858c06f2017-07-21 10:44:27 -0400157 /* Lock the bucket... */
158 while (BV (clib_bihash_lock_bucket) (b) == 0)
159 ;
Dave Barach908a5ea2017-07-14 12:42:21 -0400160
Dave Barachc3799992016-08-15 11:12:27 -0400161 v = BV (clib_bihash_get_value) (h, b->offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700162
Dave Barachc3799992016-08-15 11:12:27 -0400163 clib_memcpy (working_copy, v, sizeof (*v) * (1 << b->log2_pages));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700164 working_bucket.as_u64 = b->as_u64;
Dave Barachc3799992016-08-15 11:12:27 -0400165 working_bucket.offset = BV (clib_bihash_get_offset) (h, working_copy);
166 CLIB_MEMORY_BARRIER ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700167 b->as_u64 = working_bucket.as_u64;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200168 h->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700169}
170
Dave Barachc3799992016-08-15 11:12:27 -0400171static
172BVT (clib_bihash_value) *
173BV (split_and_rehash)
174 (BVT (clib_bihash) * h,
Dave Barachba7ddfe2017-05-17 20:20:50 -0400175 BVT (clib_bihash_value) * old_values, u32 old_log2_pages,
176 u32 new_log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700177{
Dave Barach5e6b9582016-12-12 15:37:29 -0500178 BVT (clib_bihash_value) * new_values, *new_v;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400179 int i, j, length_in_kvs;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700180
Dave Barachc3799992016-08-15 11:12:27 -0400181 new_values = BV (value_alloc) (h, new_log2_pages);
Dave Barachba7ddfe2017-05-17 20:20:50 -0400182 length_in_kvs = (1 << old_log2_pages) * BIHASH_KVP_PER_PAGE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700183
Dave Barachba7ddfe2017-05-17 20:20:50 -0400184 for (i = 0; i < length_in_kvs; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700185 {
186 u64 new_hash;
Dave Barachc3799992016-08-15 11:12:27 -0400187
Dave Barach5e6b9582016-12-12 15:37:29 -0500188 /* Entry not in use? Forget it */
189 if (BV (clib_bihash_is_free) (&(old_values->kvp[i])))
190 continue;
191
192 /* rehash the item onto its new home-page */
193 new_hash = BV (clib_bihash_hash) (&(old_values->kvp[i]));
194 new_hash >>= h->log2_nbuckets;
195 new_hash &= (1 << new_log2_pages) - 1;
196 new_v = &new_values[new_hash];
197
198 /* Across the new home-page */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700199 for (j = 0; j < BIHASH_KVP_PER_PAGE; j++)
Dave Barachc3799992016-08-15 11:12:27 -0400200 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500201 /* Empty slot */
202 if (BV (clib_bihash_is_free) (&(new_v->kvp[j])))
Dave Barachc3799992016-08-15 11:12:27 -0400203 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500204 clib_memcpy (&(new_v->kvp[j]), &(old_values->kvp[i]),
205 sizeof (new_v->kvp[j]));
206 goto doublebreak;
Dave Barachc3799992016-08-15 11:12:27 -0400207 }
Dave Barachc3799992016-08-15 11:12:27 -0400208 }
Dave Barach5e6b9582016-12-12 15:37:29 -0500209 /* Crap. Tell caller to try again */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400210 BV (value_free) (h, new_values, new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500211 return 0;
212 doublebreak:;
213 }
Dave Barachba7ddfe2017-05-17 20:20:50 -0400214
Dave Barach5e6b9582016-12-12 15:37:29 -0500215 return new_values;
216}
217
218static
219BVT (clib_bihash_value) *
220BV (split_and_rehash_linear)
221 (BVT (clib_bihash) * h,
Dave Barachba7ddfe2017-05-17 20:20:50 -0400222 BVT (clib_bihash_value) * old_values, u32 old_log2_pages,
223 u32 new_log2_pages)
Dave Barach5e6b9582016-12-12 15:37:29 -0500224{
225 BVT (clib_bihash_value) * new_values;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400226 int i, j, new_length, old_length;
Dave Barach5e6b9582016-12-12 15:37:29 -0500227
228 new_values = BV (value_alloc) (h, new_log2_pages);
229 new_length = (1 << new_log2_pages) * BIHASH_KVP_PER_PAGE;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400230 old_length = (1 << old_log2_pages) * BIHASH_KVP_PER_PAGE;
Dave Barach5e6b9582016-12-12 15:37:29 -0500231
232 j = 0;
233 /* Across the old value array */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400234 for (i = 0; i < old_length; i++)
Dave Barach5e6b9582016-12-12 15:37:29 -0500235 {
236 /* Find a free slot in the new linear scan bucket */
237 for (; j < new_length; j++)
238 {
Dave Barach8f544962017-01-18 10:23:22 -0500239 /* Old value not in use? Forget it. */
Dave Barach5e6b9582016-12-12 15:37:29 -0500240 if (BV (clib_bihash_is_free) (&(old_values->kvp[i])))
241 goto doublebreak;
242
243 /* New value should never be in use */
244 if (BV (clib_bihash_is_free) (&(new_values->kvp[j])))
245 {
246 /* Copy the old value and move along */
247 clib_memcpy (&(new_values->kvp[j]), &(old_values->kvp[i]),
248 sizeof (new_values->kvp[j]));
249 j++;
250 goto doublebreak;
251 }
Dave Barach5e6b9582016-12-12 15:37:29 -0500252 }
Dave Barach8f544962017-01-18 10:23:22 -0500253 /* This should never happen... */
254 clib_warning ("BUG: linear rehash failed!");
Dave Barachba7ddfe2017-05-17 20:20:50 -0400255 BV (value_free) (h, new_values, new_log2_pages);
Dave Barach8f544962017-01-18 10:23:22 -0500256 return 0;
257
Dave Barach5e6b9582016-12-12 15:37:29 -0500258 doublebreak:;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700259 }
260 return new_values;
261}
262
Dave Barachc3799992016-08-15 11:12:27 -0400263int BV (clib_bihash_add_del)
264 (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * add_v, int is_add)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700265{
266 u32 bucket_index;
Dave Barach908a5ea2017-07-14 12:42:21 -0400267 BVT (clib_bihash_bucket) * b, tmp_b;
Dave Barachc3799992016-08-15 11:12:27 -0400268 BVT (clib_bihash_value) * v, *new_v, *save_new_v, *working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700269 int rv = 0;
Dave Barach5e6b9582016-12-12 15:37:29 -0500270 int i, limit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700271 u64 hash, new_hash;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400272 u32 new_log2_pages, old_log2_pages;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200273 u32 thread_index = os_get_thread_index ();
Dave Barach5e6b9582016-12-12 15:37:29 -0500274 int mark_bucket_linear;
275 int resplit_once;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700276
Dave Barachc3799992016-08-15 11:12:27 -0400277 hash = BV (clib_bihash_hash) (add_v);
278
279 bucket_index = hash & (h->nbuckets - 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700280 b = &h->buckets[bucket_index];
281
282 hash >>= h->log2_nbuckets;
283
Marco Varlese47366b12017-06-05 17:59:24 +0200284 tmp_b.linear_search = 0;
285
Ed Warnickecb9cada2015-12-08 15:45:58 -0700286 while (__sync_lock_test_and_set (h->writer_lock, 1))
Dave Barachc3799992016-08-15 11:12:27 -0400287 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700288
289 /* First elt in the bucket? */
Damjan Marion882fcfe2018-07-17 23:01:49 +0200290 if (BV (clib_bihash_bucket_is_empty) (b))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700291 {
292 if (is_add == 0)
Dave Barachc3799992016-08-15 11:12:27 -0400293 {
294 rv = -1;
295 goto unlock;
296 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700297
Dave Barachc3799992016-08-15 11:12:27 -0400298 v = BV (value_alloc) (h, 0);
Dave Barachba7ddfe2017-05-17 20:20:50 -0400299
Dave Barachc3799992016-08-15 11:12:27 -0400300 *v->kvp = *add_v;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700301 tmp_b.as_u64 = 0;
Dave Barachc3799992016-08-15 11:12:27 -0400302 tmp_b.offset = BV (clib_bihash_get_offset) (h, v);
Dave Barache7d212f2018-02-07 13:14:06 -0500303 tmp_b.refcnt = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700304
305 b->as_u64 = tmp_b.as_u64;
306 goto unlock;
307 }
308
Dave Barach908a5ea2017-07-14 12:42:21 -0400309 /* Note: this leaves the cache disabled */
Dave Barachc3799992016-08-15 11:12:27 -0400310 BV (make_working_copy) (h, b);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700311
Dave Barachc3799992016-08-15 11:12:27 -0400312 v = BV (clib_bihash_get_value) (h, h->saved_bucket.offset);
Dave Barach5e6b9582016-12-12 15:37:29 -0500313
314 limit = BIHASH_KVP_PER_PAGE;
315 v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
316 if (b->linear_search)
317 limit <<= b->log2_pages;
Dave Barachc3799992016-08-15 11:12:27 -0400318
Ed Warnickecb9cada2015-12-08 15:45:58 -0700319 if (is_add)
320 {
Dave Barachc3799992016-08-15 11:12:27 -0400321 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700322 * For obvious (in hindsight) reasons, see if we're supposed to
323 * replace an existing key, then look for an empty slot.
324 */
Dave Barach5e6b9582016-12-12 15:37:29 -0500325 for (i = 0; i < limit; i++)
Dave Barachc3799992016-08-15 11:12:27 -0400326 {
327 if (!memcmp (&(v->kvp[i]), &add_v->key, sizeof (add_v->key)))
328 {
329 clib_memcpy (&(v->kvp[i]), add_v, sizeof (*add_v));
330 CLIB_MEMORY_BARRIER ();
331 /* Restore the previous (k,v) pairs */
332 b->as_u64 = h->saved_bucket.as_u64;
333 goto unlock;
334 }
335 }
Dave Barach5e6b9582016-12-12 15:37:29 -0500336 for (i = 0; i < limit; i++)
Dave Barachc3799992016-08-15 11:12:27 -0400337 {
338 if (BV (clib_bihash_is_free) (&(v->kvp[i])))
339 {
340 clib_memcpy (&(v->kvp[i]), add_v, sizeof (*add_v));
341 CLIB_MEMORY_BARRIER ();
342 b->as_u64 = h->saved_bucket.as_u64;
Dave Barache7d212f2018-02-07 13:14:06 -0500343 b->refcnt++;
Dave Barachc3799992016-08-15 11:12:27 -0400344 goto unlock;
345 }
346 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700347 /* no room at the inn... split case... */
348 }
349 else
350 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500351 for (i = 0; i < limit; i++)
Dave Barachc3799992016-08-15 11:12:27 -0400352 {
353 if (!memcmp (&(v->kvp[i]), &add_v->key, sizeof (add_v->key)))
354 {
355 memset (&(v->kvp[i]), 0xff, sizeof (*(add_v)));
356 CLIB_MEMORY_BARRIER ();
Dave Barache7d212f2018-02-07 13:14:06 -0500357 if (PREDICT_TRUE (h->saved_bucket.refcnt > 1))
358 {
359 h->saved_bucket.refcnt -= 1;
360 b->as_u64 = h->saved_bucket.as_u64;
361 goto unlock;
362 }
363 else
364 {
365 tmp_b.as_u64 = 0;
366 goto free_old_bucket;
367 }
Dave Barachc3799992016-08-15 11:12:27 -0400368 }
369 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700370 rv = -3;
371 b->as_u64 = h->saved_bucket.as_u64;
372 goto unlock;
373 }
374
Dave Barachba7ddfe2017-05-17 20:20:50 -0400375 old_log2_pages = h->saved_bucket.log2_pages;
376 new_log2_pages = old_log2_pages + 1;
Dave Barach5e6b9582016-12-12 15:37:29 -0500377 mark_bucket_linear = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700378
Damjan Marionf55f9b82017-05-10 21:06:28 +0200379 working_copy = h->working_copies[thread_index];
Dave Barach5e6b9582016-12-12 15:37:29 -0500380 resplit_once = 0;
381
Dave Barachba7ddfe2017-05-17 20:20:50 -0400382 new_v = BV (split_and_rehash) (h, working_copy, old_log2_pages,
383 new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700384 if (new_v == 0)
385 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500386 try_resplit:
387 resplit_once = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700388 new_log2_pages++;
Dave Barach5e6b9582016-12-12 15:37:29 -0500389 /* Try re-splitting. If that fails, fall back to linear search */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400390 new_v = BV (split_and_rehash) (h, working_copy, old_log2_pages,
391 new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500392 if (new_v == 0)
393 {
394 mark_linear:
395 new_log2_pages--;
396 /* pinned collisions, use linear search */
397 new_v =
Dave Barachba7ddfe2017-05-17 20:20:50 -0400398 BV (split_and_rehash_linear) (h, working_copy, old_log2_pages,
399 new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500400 mark_bucket_linear = 1;
401 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700402 }
403
404 /* Try to add the new entry */
405 save_new_v = new_v;
Dave Barachc3799992016-08-15 11:12:27 -0400406 new_hash = BV (clib_bihash_hash) (add_v);
Dave Barach5e6b9582016-12-12 15:37:29 -0500407 limit = BIHASH_KVP_PER_PAGE;
408 if (mark_bucket_linear)
409 limit <<= new_log2_pages;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700410 new_hash >>= h->log2_nbuckets;
Dave Barach5e6b9582016-12-12 15:37:29 -0500411 new_hash &= (1 << new_log2_pages) - 1;
412 new_v += mark_bucket_linear ? 0 : new_hash;
Dave Barachc3799992016-08-15 11:12:27 -0400413
Dave Barach5e6b9582016-12-12 15:37:29 -0500414 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700415 {
Dave Barachc3799992016-08-15 11:12:27 -0400416 if (BV (clib_bihash_is_free) (&(new_v->kvp[i])))
417 {
418 clib_memcpy (&(new_v->kvp[i]), add_v, sizeof (*add_v));
419 goto expand_ok;
420 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700421 }
Dave Barachba7ddfe2017-05-17 20:20:50 -0400422
Ed Warnickecb9cada2015-12-08 15:45:58 -0700423 /* Crap. Try again */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400424 BV (value_free) (h, save_new_v, new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500425 /*
426 * If we've already doubled the size of the bucket once,
427 * fall back to linear search now.
428 */
429 if (resplit_once)
430 goto mark_linear;
431 else
432 goto try_resplit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700433
Dave Barachc3799992016-08-15 11:12:27 -0400434expand_ok:
Dave Barach5e6b9582016-12-12 15:37:29 -0500435 tmp_b.log2_pages = new_log2_pages;
Dave Barachc3799992016-08-15 11:12:27 -0400436 tmp_b.offset = BV (clib_bihash_get_offset) (h, save_new_v);
Dave Barach5e6b9582016-12-12 15:37:29 -0500437 tmp_b.linear_search = mark_bucket_linear;
Dave Barache7d212f2018-02-07 13:14:06 -0500438 tmp_b.refcnt = h->saved_bucket.refcnt + 1;
439
440free_old_bucket:
Dave Barachba7ddfe2017-05-17 20:20:50 -0400441
Dave Barachc3799992016-08-15 11:12:27 -0400442 CLIB_MEMORY_BARRIER ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700443 b->as_u64 = tmp_b.as_u64;
Dave Barachc3799992016-08-15 11:12:27 -0400444 v = BV (clib_bihash_get_value) (h, h->saved_bucket.offset);
Dave Barache7d212f2018-02-07 13:14:06 -0500445
446 BV (value_free) (h, v, h->saved_bucket.log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700447
Dave Barachc3799992016-08-15 11:12:27 -0400448unlock:
Dave Barach908a5ea2017-07-14 12:42:21 -0400449 BV (clib_bihash_reset_cache) (b);
Dave Barach858c06f2017-07-21 10:44:27 -0400450 BV (clib_bihash_unlock_bucket) (b);
Dave Barachc3799992016-08-15 11:12:27 -0400451 CLIB_MEMORY_BARRIER ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700452 h->writer_lock[0] = 0;
453 return rv;
454}
455
Dave Barachc3799992016-08-15 11:12:27 -0400456int BV (clib_bihash_search)
Dave Barach908a5ea2017-07-14 12:42:21 -0400457 (BVT (clib_bihash) * h,
Dave Barachc3799992016-08-15 11:12:27 -0400458 BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700459{
460 u64 hash;
461 u32 bucket_index;
Dave Barachc3799992016-08-15 11:12:27 -0400462 BVT (clib_bihash_value) * v;
Dave Barachc8ef08a2017-08-31 05:58:22 -0400463#if BIHASH_KVP_CACHE_SIZE > 0
Dave Barach908a5ea2017-07-14 12:42:21 -0400464 BVT (clib_bihash_kv) * kvp;
Dave Barachc8ef08a2017-08-31 05:58:22 -0400465#endif
Dave Barach908a5ea2017-07-14 12:42:21 -0400466 BVT (clib_bihash_bucket) * b;
Dave Barach5e6b9582016-12-12 15:37:29 -0500467 int i, limit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700468
Dave Barachc3799992016-08-15 11:12:27 -0400469 ASSERT (valuep);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700470
Dave Barachc3799992016-08-15 11:12:27 -0400471 hash = BV (clib_bihash_hash) (search_key);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700472
Dave Barachc3799992016-08-15 11:12:27 -0400473 bucket_index = hash & (h->nbuckets - 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700474 b = &h->buckets[bucket_index];
475
Damjan Marion882fcfe2018-07-17 23:01:49 +0200476 if (BV (clib_bihash_bucket_is_empty) (b))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700477 return -1;
478
Dave Barachc8ef08a2017-08-31 05:58:22 -0400479#if BIHASH_KVP_CACHE_SIZE > 0
Dave Barach908a5ea2017-07-14 12:42:21 -0400480 /* Check the cache, if currently enabled */
Dave Barach858c06f2017-07-21 10:44:27 -0400481 if (PREDICT_TRUE ((b->cache_lru & (1 << 15)) == 0))
Dave Barach908a5ea2017-07-14 12:42:21 -0400482 {
483 limit = BIHASH_KVP_CACHE_SIZE;
484 kvp = b->cache;
485 for (i = 0; i < limit; i++)
486 {
487 if (BV (clib_bihash_key_compare) (kvp[i].key, search_key->key))
488 {
489 *valuep = kvp[i];
490 h->cache_hits++;
491 return 0;
492 }
493 }
494 }
Dave Barachc8ef08a2017-08-31 05:58:22 -0400495#endif
Dave Barach908a5ea2017-07-14 12:42:21 -0400496
Ed Warnickecb9cada2015-12-08 15:45:58 -0700497 hash >>= h->log2_nbuckets;
498
Dave Barachc3799992016-08-15 11:12:27 -0400499 v = BV (clib_bihash_get_value) (h, b->offset);
Dave Barach5e6b9582016-12-12 15:37:29 -0500500 limit = BIHASH_KVP_PER_PAGE;
501 v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
502 if (PREDICT_FALSE (b->linear_search))
503 limit <<= b->log2_pages;
Dave Barachc3799992016-08-15 11:12:27 -0400504
Dave Barach5e6b9582016-12-12 15:37:29 -0500505 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700506 {
Dave Barachc3799992016-08-15 11:12:27 -0400507 if (BV (clib_bihash_key_compare) (v->kvp[i].key, search_key->key))
508 {
509 *valuep = v->kvp[i];
Dave Barach908a5ea2017-07-14 12:42:21 -0400510
Dave Barachc8ef08a2017-08-31 05:58:22 -0400511#if BIHASH_KVP_CACHE_SIZE > 0
512 u8 cache_slot;
Dave Barach908a5ea2017-07-14 12:42:21 -0400513 /* Shut off the cache */
Dave Barach858c06f2017-07-21 10:44:27 -0400514 if (BV (clib_bihash_lock_bucket) (b))
515 {
516 cache_slot = BV (clib_bihash_get_lru) (b);
517 b->cache[cache_slot] = v->kvp[i];
518 BV (clib_bihash_update_lru) (b, cache_slot);
Dave Barach908a5ea2017-07-14 12:42:21 -0400519
Dave Barach858c06f2017-07-21 10:44:27 -0400520 /* Reenable the cache */
521 BV (clib_bihash_unlock_bucket) (b);
522 h->cache_misses++;
523 }
Dave Barachc8ef08a2017-08-31 05:58:22 -0400524#endif
Dave Barachc3799992016-08-15 11:12:27 -0400525 return 0;
526 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700527 }
528 return -1;
529}
530
Dave Barach908a5ea2017-07-14 12:42:21 -0400531u8 *BV (format_bihash_lru) (u8 * s, va_list * args)
532{
Dave Barachc8ef08a2017-08-31 05:58:22 -0400533#if BIHASH_KVP_SIZE > 0
Dave Barach908a5ea2017-07-14 12:42:21 -0400534 int i;
535 BVT (clib_bihash_bucket) * b = va_arg (*args, BVT (clib_bihash_bucket) *);
536 u16 cache_lru = b->cache_lru;
537
538 s = format (s, "cache %s, order ", cache_lru & (1 << 15) ? "on" : "off");
539
540 for (i = 0; i < BIHASH_KVP_CACHE_SIZE; i++)
541 s = format (s, "[%d] ", ((cache_lru >> (3 * i)) & 7));
Chris Luke2951d902017-09-05 11:59:55 -0400542
543 return (s);
Dave Barachc8ef08a2017-08-31 05:58:22 -0400544#else
545 return format (s, "cache not configured");
546#endif
Dave Barach908a5ea2017-07-14 12:42:21 -0400547}
548
549void
550BV (clib_bihash_update_lru_not_inline) (BVT (clib_bihash_bucket) * b, u8 slot)
551{
Dave Barachc8ef08a2017-08-31 05:58:22 -0400552#if BIHASH_KVP_SIZE > 0
Dave Barach908a5ea2017-07-14 12:42:21 -0400553 BV (clib_bihash_update_lru) (b, slot);
Dave Barachc8ef08a2017-08-31 05:58:22 -0400554#endif
Dave Barach908a5ea2017-07-14 12:42:21 -0400555}
556
Dave Barachc3799992016-08-15 11:12:27 -0400557u8 *BV (format_bihash) (u8 * s, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700558{
Dave Barachc3799992016-08-15 11:12:27 -0400559 BVT (clib_bihash) * h = va_arg (*args, BVT (clib_bihash) *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700560 int verbose = va_arg (*args, int);
Dave Barach908a5ea2017-07-14 12:42:21 -0400561 BVT (clib_bihash_bucket) * b;
Dave Barachc3799992016-08-15 11:12:27 -0400562 BVT (clib_bihash_value) * v;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700563 int i, j, k;
564 u64 active_elements = 0;
Dave Barache7d212f2018-02-07 13:14:06 -0500565 u64 active_buckets = 0;
566 u64 linear_buckets = 0;
Dave Barach97f5af02018-02-22 09:48:45 -0500567 u64 used_bytes;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700568
569 s = format (s, "Hash table %s\n", h->name ? h->name : (u8 *) "(unnamed)");
Dave Barachc3799992016-08-15 11:12:27 -0400570
Ed Warnickecb9cada2015-12-08 15:45:58 -0700571 for (i = 0; i < h->nbuckets; i++)
572 {
Dave Barachc3799992016-08-15 11:12:27 -0400573 b = &h->buckets[i];
Damjan Marion882fcfe2018-07-17 23:01:49 +0200574 if (BV (clib_bihash_bucket_is_empty) (b))
Dave Barachc3799992016-08-15 11:12:27 -0400575 {
576 if (verbose > 1)
577 s = format (s, "[%d]: empty\n", i);
578 continue;
579 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700580
Dave Barache7d212f2018-02-07 13:14:06 -0500581 active_buckets++;
582
583 if (b->linear_search)
584 linear_buckets++;
585
Ed Warnickecb9cada2015-12-08 15:45:58 -0700586 if (verbose)
Dave Barachc3799992016-08-15 11:12:27 -0400587 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500588 s = format (s, "[%d]: heap offset %d, len %d, linear %d\n", i,
589 b->offset, (1 << b->log2_pages), b->linear_search);
Dave Barachc3799992016-08-15 11:12:27 -0400590 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700591
Dave Barachc3799992016-08-15 11:12:27 -0400592 v = BV (clib_bihash_get_value) (h, b->offset);
593 for (j = 0; j < (1 << b->log2_pages); j++)
594 {
595 for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
596 {
597 if (BV (clib_bihash_is_free) (&v->kvp[k]))
598 {
599 if (verbose > 1)
600 s = format (s, " %d: empty\n",
601 j * BIHASH_KVP_PER_PAGE + k);
602 continue;
603 }
604 if (verbose)
605 {
Vijayabhaskar Katamreddyfb8e61c2017-12-14 13:20:50 -0800606 if (h->fmt_fn)
607 {
608 s = format (s, " %d: %U\n",
609 j * BIHASH_KVP_PER_PAGE + k,
610 h->fmt_fn, &(v->kvp[k]));
611 }
612 else
613 {
614 s = format (s, " %d: %U\n",
615 j * BIHASH_KVP_PER_PAGE + k,
616 BV (format_bihash_kvp), &(v->kvp[k]));
617 }
Dave Barachc3799992016-08-15 11:12:27 -0400618 }
619 active_elements++;
620 }
621 v++;
622 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700623 }
624
Dave Barache7d212f2018-02-07 13:14:06 -0500625 s = format (s, " %lld active elements %lld active buckets\n",
626 active_elements, active_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700627 s = format (s, " %d free lists\n", vec_len (h->freelists));
Dave Barache7d212f2018-02-07 13:14:06 -0500628
629 for (i = 0; i < vec_len (h->freelists); i++)
630 {
631 u32 nfree = 0;
632 BVT (clib_bihash_value) * free_elt;
633
634 free_elt = h->freelists[i];
635 while (free_elt)
636 {
637 nfree++;
638 free_elt = free_elt->next_free;
639 }
640
641 s = format (s, " [len %d] %u free elts\n", 1 << i, nfree);
642 }
643
644 s = format (s, " %lld linear search buckets\n", linear_buckets);
Dave Barach908a5ea2017-07-14 12:42:21 -0400645 s = format (s, " %lld cache hits, %lld cache misses\n",
646 h->cache_hits, h->cache_misses);
Dave Barach97f5af02018-02-22 09:48:45 -0500647 used_bytes = h->alloc_arena_next - h->alloc_arena;
648 s = format (s,
649 " arena: base %llx, next %llx\n"
650 " used %lld b (%lld Mbytes) of %lld b (%lld Mbytes)\n",
651 h->alloc_arena, h->alloc_arena_next,
652 used_bytes, used_bytes >> 20,
653 h->alloc_arena_size, h->alloc_arena_size >> 20);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700654 return s;
655}
656
Dave Barachc3799992016-08-15 11:12:27 -0400657void BV (clib_bihash_foreach_key_value_pair)
658 (BVT (clib_bihash) * h, void *callback, void *arg)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700659{
660 int i, j, k;
Dave Barach908a5ea2017-07-14 12:42:21 -0400661 BVT (clib_bihash_bucket) * b;
Dave Barachc3799992016-08-15 11:12:27 -0400662 BVT (clib_bihash_value) * v;
663 void (*fp) (BVT (clib_bihash_kv) *, void *) = callback;
664
Ed Warnickecb9cada2015-12-08 15:45:58 -0700665 for (i = 0; i < h->nbuckets; i++)
666 {
Dave Barachc3799992016-08-15 11:12:27 -0400667 b = &h->buckets[i];
Damjan Marion882fcfe2018-07-17 23:01:49 +0200668 if (BV (clib_bihash_bucket_is_empty) (b))
Dave Barachc3799992016-08-15 11:12:27 -0400669 continue;
670
671 v = BV (clib_bihash_get_value) (h, b->offset);
672 for (j = 0; j < (1 << b->log2_pages); j++)
673 {
674 for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
675 {
676 if (BV (clib_bihash_is_free) (&v->kvp[k]))
677 continue;
678
679 (*fp) (&v->kvp[k], arg);
680 }
681 v++;
682 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700683 }
684}
Dave Barachdd3a57f2016-07-27 16:58:51 -0400685
Chris Luke16bcf7d2016-09-01 14:31:46 -0400686/** @endcond */
Dave Barachc3799992016-08-15 11:12:27 -0400687
688/*
689 * fd.io coding-style-patch-verification: ON
690 *
691 * Local Variables:
692 * eval: (c-set-style "gnu")
693 * End:
694 */