blob: 97c4cd08eb18af87790c387ef77e9d04a363514a [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
Dave Barach9466c452018-08-24 17:21:14 -040026 rv = alloc_arena_next (h);
27 alloc_arena_next (h) += nbytes;
Dave Barach97f5af02018-02-22 09:48:45 -050028
Andreas Schultzb4b525e2019-07-19 11:14:50 +020029 if (alloc_arena_next (h) > alloc_arena_size (h))
Dave Barach97f5af02018-02-22 09:48:45 -050030 os_out_of_memory ();
31
Dave Barachffb14b92018-09-11 17:20:23 -040032 return (void *) (uword) (rv + alloc_arena (h));
Dave Barach97f5af02018-02-22 09:48:45 -050033}
34
Dave Barach32dcd3b2019-07-08 12:25:38 -040035void BV (clib_bihash_instantiate) (BVT (clib_bihash) * h)
36{
37 uword bucket_size;
38
39 alloc_arena (h) = (uword) clib_mem_vm_alloc (h->memory_size);
40 alloc_arena_next (h) = 0;
41 alloc_arena_size (h) = h->memory_size;
42
43 bucket_size = h->nbuckets * sizeof (h->buckets[0]);
44 h->buckets = BV (alloc_aligned) (h, bucket_size);
Dave Barach67d09e02019-08-01 08:15:01 -040045 CLIB_MEMORY_BARRIER ();
46 h->instantiated = 1;
Dave Barach32dcd3b2019-07-08 12:25:38 -040047}
Dave Barach97f5af02018-02-22 09:48:45 -050048
Dave Barachbdf9b972019-09-03 10:57:19 -040049void BV (clib_bihash_init2) (BVT (clib_bihash_init2_args) * a)
Ed Warnickecb9cada2015-12-08 15:45:58 -070050{
Dave Barach32dcd3b2019-07-08 12:25:38 -040051 int i;
52 void *oldheap;
Dave Barachbdf9b972019-09-03 10:57:19 -040053 BVT (clib_bihash) * h = a->h;
Ed Warnickecb9cada2015-12-08 15:45:58 -070054
Dave Barachbdf9b972019-09-03 10:57:19 -040055 a->nbuckets = 1 << (max_log2 (a->nbuckets));
56
57 h->name = (u8 *) a->name;
58 h->nbuckets = a->nbuckets;
59 h->log2_nbuckets = max_log2 (a->nbuckets);
60 h->memory_size = a->memory_size;
Dave Barach67d09e02019-08-01 08:15:01 -040061 h->instantiated = 0;
Dave Barachbdf9b972019-09-03 10:57:19 -040062 h->fmt_fn = a->fmt_fn;
63
Dave Barach32dcd3b2019-07-08 12:25:38 -040064 alloc_arena (h) = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -070065
Dave Barach508498f2018-07-19 12:11:16 -040066 /*
67 * Make sure the requested size is rational. The max table
68 * size without playing the alignment card is 64 Gbytes.
69 * If someone starts complaining that's not enough, we can shift
70 * the offset by CLIB_LOG2_CACHE_LINE_BYTES...
71 */
Dave Barachbdf9b972019-09-03 10:57:19 -040072 ASSERT (h->memory_size < (1ULL << BIHASH_BUCKET_OFFSET_BITS));
Dave Barach32dcd3b2019-07-08 12:25:38 -040073
74 /* Add this hash table to the list */
Dave Barachbdf9b972019-09-03 10:57:19 -040075 if (a->dont_add_to_all_bihash_list == 0)
76 {
77 for (i = 0; i < vec_len (clib_all_bihashes); i++)
78 if (clib_all_bihashes[i] == h)
79 goto do_lock;
80 oldheap = clib_all_bihash_set_heap ();
81 vec_add1 (clib_all_bihashes, (void *) h);
82 clib_mem_set_heap (oldheap);
83 }
Dave Barach32dcd3b2019-07-08 12:25:38 -040084
Dave Barachbdf9b972019-09-03 10:57:19 -040085do_lock:
86 if (h->alloc_lock)
87 clib_mem_free ((void *) h->alloc_lock);
Dave Barach67d09e02019-08-01 08:15:01 -040088
89 /*
90 * Set up the lock now, so we can use it to make the first add
91 * thread-safe
92 */
93 h->alloc_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
94 CLIB_CACHE_LINE_BYTES);
95 h->alloc_lock[0] = 0;
96
Dave Barachbdf9b972019-09-03 10:57:19 -040097 if (a->instantiate_immediately)
98 BV (clib_bihash_instantiate) (h);
99}
100
101void BV (clib_bihash_init)
102 (BVT (clib_bihash) * h, char *name, u32 nbuckets, uword memory_size)
103{
104 BVT (clib_bihash_init2_args) _a, *a = &_a;
105
106 memset (a, 0, sizeof (*a));
107
108 a->h = h;
109 a->name = name;
110 a->nbuckets = nbuckets;
111 a->memory_size = memory_size;
112
113 BV (clib_bihash_init2) (a);
Vijayabhaskar Katamreddyfb8e61c2017-12-14 13:20:50 -0800114}
115
Dave Barach9466c452018-08-24 17:21:14 -0400116#if BIHASH_32_64_SVM
117#if !defined (MFD_ALLOW_SEALING)
118#define MFD_ALLOW_SEALING 0x0002U
119#endif
120
121void BV (clib_bihash_master_init_svm)
Dave Barachffb14b92018-09-11 17:20:23 -0400122 (BVT (clib_bihash) * h, char *name, u32 nbuckets, u64 memory_size)
Dave Barach9466c452018-08-24 17:21:14 -0400123{
124 uword bucket_size;
125 u8 *mmap_addr;
126 vec_header_t *freelist_vh;
127 int fd;
128
Dave Barachffb14b92018-09-11 17:20:23 -0400129 ASSERT (memory_size < (1ULL << 32));
Dave Barach9466c452018-08-24 17:21:14 -0400130 /* Set up for memfd sharing */
131 if ((fd = memfd_create (name, MFD_ALLOW_SEALING)) == -1)
132 {
133 clib_unix_warning ("memfd_create");
134 return;
135 }
136
137 if (ftruncate (fd, memory_size) < 0)
138 {
139 clib_unix_warning ("ftruncate");
140 return;
141 }
142
143 /* Not mission-critical, complain and continue */
144 if ((fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK)) == -1)
145 clib_unix_warning ("fcntl (F_ADD_SEALS)");
146
Dave Barachffb14b92018-09-11 17:20:23 -0400147 mmap_addr = mmap (0, memory_size,
148 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 /* offset */ );
Dave Barach9466c452018-08-24 17:21:14 -0400149
150 if (mmap_addr == MAP_FAILED)
151 {
152 clib_unix_warning ("mmap failed");
153 ASSERT (0);
154 }
155
156 h->sh = (void *) mmap_addr;
157 h->memfd = fd;
158 nbuckets = 1 << (max_log2 (nbuckets));
159
160 h->name = (u8 *) name;
161 h->sh->nbuckets = h->nbuckets = nbuckets;
162 h->log2_nbuckets = max_log2 (nbuckets);
163
164 alloc_arena (h) = (u64) (uword) mmap_addr;
Dave Barachffb14b92018-09-11 17:20:23 -0400165 alloc_arena_next (h) = CLIB_CACHE_LINE_BYTES;
Dave Barach9466c452018-08-24 17:21:14 -0400166 alloc_arena_size (h) = memory_size;
167
168 bucket_size = nbuckets * sizeof (h->buckets[0]);
169 h->buckets = BV (alloc_aligned) (h, bucket_size);
Dave Barachffb14b92018-09-11 17:20:23 -0400170 h->sh->buckets_as_u64 = (u64) BV (clib_bihash_get_offset) (h, h->buckets);
Dave Barach9466c452018-08-24 17:21:14 -0400171
172 h->alloc_lock = BV (alloc_aligned) (h, CLIB_CACHE_LINE_BYTES);
173 h->alloc_lock[0] = 0;
174
Dave Barachffb14b92018-09-11 17:20:23 -0400175 h->sh->alloc_lock_as_u64 =
176 (u64) BV (clib_bihash_get_offset) (h, (void *) h->alloc_lock);
177 freelist_vh =
178 BV (alloc_aligned) (h,
179 sizeof (vec_header_t) +
180 BIHASH_FREELIST_LENGTH * sizeof (u64));
Dave Barach9466c452018-08-24 17:21:14 -0400181 freelist_vh->len = BIHASH_FREELIST_LENGTH;
182 freelist_vh->dlmalloc_header_offset = 0xDEADBEEF;
Dave Barachffb14b92018-09-11 17:20:23 -0400183 h->sh->freelists_as_u64 =
184 (u64) BV (clib_bihash_get_offset) (h, freelist_vh->vector_data);
185 h->freelists = (void *) (freelist_vh->vector_data);
Dave Barach9466c452018-08-24 17:21:14 -0400186
187 h->fmt_fn = NULL;
188}
189
190void BV (clib_bihash_slave_init_svm)
191 (BVT (clib_bihash) * h, char *name, int fd)
192{
193 u8 *mmap_addr;
Dave Barachffb14b92018-09-11 17:20:23 -0400194 u64 memory_size;
Dave Barach9466c452018-08-24 17:21:14 -0400195 BVT (clib_bihash_shared_header) * sh;
196
Dave Barachffb14b92018-09-11 17:20:23 -0400197 /* Trial mapping, to learn the segment size */
Dave Barach9466c452018-08-24 17:21:14 -0400198 mmap_addr = mmap (0, 4096, PROT_READ, MAP_SHARED, fd, 0 /* offset */ );
199 if (mmap_addr == MAP_FAILED)
200 {
201 clib_unix_warning ("trial mmap failed");
202 ASSERT (0);
203 }
204
205 sh = (BVT (clib_bihash_shared_header) *) mmap_addr;
206
Dave Barach9466c452018-08-24 17:21:14 -0400207 memory_size = sh->alloc_arena_size;
208
209 munmap (mmap_addr, 4096);
210
Dave Barachffb14b92018-09-11 17:20:23 -0400211 /* Actual mapping, at the required size */
212 mmap_addr = mmap (0, memory_size,
213 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 /* offset */ );
Dave Barach9466c452018-08-24 17:21:14 -0400214
215 if (mmap_addr == MAP_FAILED)
216 {
217 clib_unix_warning ("mmap failed");
218 ASSERT (0);
219 }
220
221 (void) close (fd);
222
223 h->sh = (void *) mmap_addr;
Dave Barachffb14b92018-09-11 17:20:23 -0400224 alloc_arena (h) = (u64) (uword) mmap_addr;
Dave Barach9466c452018-08-24 17:21:14 -0400225 h->memfd = -1;
226
227 h->name = (u8 *) name;
Dave Barachffb14b92018-09-11 17:20:23 -0400228 h->buckets = BV (clib_bihash_get_value) (h, h->sh->buckets_as_u64);
Dave Barach9466c452018-08-24 17:21:14 -0400229 h->nbuckets = h->sh->nbuckets;
230 h->log2_nbuckets = max_log2 (h->nbuckets);
231
Dave Barachffb14b92018-09-11 17:20:23 -0400232 h->alloc_lock = BV (clib_bihash_get_value) (h, h->sh->alloc_lock_as_u64);
233 h->freelists = BV (clib_bihash_get_value) (h, h->sh->freelists_as_u64);
Dave Barach9466c452018-08-24 17:21:14 -0400234 h->fmt_fn = NULL;
235}
236#endif /* BIHASH_32_64_SVM */
237
Vijayabhaskar Katamreddyfb8e61c2017-12-14 13:20:50 -0800238void BV (clib_bihash_set_kvp_format_fn) (BVT (clib_bihash) * h,
239 format_function_t * fmt_fn)
240{
241 h->fmt_fn = fmt_fn;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700242}
243
Dave Barachc3799992016-08-15 11:12:27 -0400244void BV (clib_bihash_free) (BVT (clib_bihash) * h)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700245{
Dave Barach32dcd3b2019-07-08 12:25:38 -0400246 int i;
247
Dave Barach67d09e02019-08-01 08:15:01 -0400248 if (PREDICT_FALSE (h->instantiated == 0))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400249 goto never_initialized;
250
Dave Barach67d09e02019-08-01 08:15:01 -0400251 h->instantiated = 0;
Dave Barach97f5af02018-02-22 09:48:45 -0500252 vec_free (h->working_copies);
Vijayabhaskar Katamreddy72739a62019-05-07 13:27:32 -0700253 vec_free (h->working_copy_lengths);
Dave Barach9466c452018-08-24 17:21:14 -0400254#if BIHASH_32_64_SVM == 0
Dave Barach97f5af02018-02-22 09:48:45 -0500255 vec_free (h->freelists);
Dave Barach9466c452018-08-24 17:21:14 -0400256#else
257 if (h->memfd > 0)
258 (void) close (h->memfd);
259#endif
260 clib_mem_vm_free ((void *) (uword) (alloc_arena (h)), alloc_arena_size (h));
Dave Barach32dcd3b2019-07-08 12:25:38 -0400261never_initialized:
Dave Barachb7b92992018-10-17 10:38:51 -0400262 clib_memset (h, 0, sizeof (*h));
Dave Barach32dcd3b2019-07-08 12:25:38 -0400263 for (i = 0; i < vec_len (clib_all_bihashes); i++)
264 {
265 if ((void *) h == clib_all_bihashes[i])
266 {
267 vec_delete (clib_all_bihashes, 1, i);
268 return;
269 }
270 }
271 clib_warning ("Couldn't find hash table %llx on clib_all_bihashes...",
272 (u64) h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700273}
274
Dave Barachc3799992016-08-15 11:12:27 -0400275static
276BVT (clib_bihash_value) *
277BV (value_alloc) (BVT (clib_bihash) * h, u32 log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700278{
Dave Barachc3799992016-08-15 11:12:27 -0400279 BVT (clib_bihash_value) * rv = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700280
Dave Barach508498f2018-07-19 12:11:16 -0400281 ASSERT (h->alloc_lock[0]);
Dave Barach9466c452018-08-24 17:21:14 -0400282
283#if BIHASH_32_64_SVM
284 ASSERT (log2_pages < vec_len (h->freelists));
285#endif
286
Dave Barachc3799992016-08-15 11:12:27 -0400287 if (log2_pages >= vec_len (h->freelists) || h->freelists[log2_pages] == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700288 {
Dave Barach97f5af02018-02-22 09:48:45 -0500289 vec_validate_init_empty (h->freelists, log2_pages, 0);
290 rv = BV (alloc_aligned) (h, (sizeof (*rv) * (1 << log2_pages)));
Dave Barachc3799992016-08-15 11:12:27 -0400291 goto initialize;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700292 }
Dave Barachffb14b92018-09-11 17:20:23 -0400293 rv = BV (clib_bihash_get_value) (h, (uword) h->freelists[log2_pages]);
Dave Barach9466c452018-08-24 17:21:14 -0400294 h->freelists[log2_pages] = rv->next_free_as_u64;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700295
Dave Barachc3799992016-08-15 11:12:27 -0400296initialize:
297 ASSERT (rv);
Dave Barachc3799992016-08-15 11:12:27 -0400298 /*
299 * Latest gcc complains that the length arg is zero
300 * if we replace (1<<log2_pages) with vec_len(rv).
301 * No clue.
302 */
Dave Barachb7b92992018-10-17 10:38:51 -0400303 clib_memset (rv, 0xff, sizeof (*rv) * (1 << log2_pages));
Dave Barachc3799992016-08-15 11:12:27 -0400304 return rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700305}
306
307static void
Dave Barachba7ddfe2017-05-17 20:20:50 -0400308BV (value_free) (BVT (clib_bihash) * h, BVT (clib_bihash_value) * v,
309 u32 log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700310{
Dave Barach508498f2018-07-19 12:11:16 -0400311 ASSERT (h->alloc_lock[0]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700312
Dave Barachc3799992016-08-15 11:12:27 -0400313 ASSERT (vec_len (h->freelists) > log2_pages);
314
Dave Barach508498f2018-07-19 12:11:16 -0400315 if (CLIB_DEBUG > 0)
Dave Barachb7b92992018-10-17 10:38:51 -0400316 clib_memset (v, 0xFE, sizeof (*v) * (1 << log2_pages));
Dave Barach508498f2018-07-19 12:11:16 -0400317
Dave Barach9466c452018-08-24 17:21:14 -0400318 v->next_free_as_u64 = (u64) h->freelists[log2_pages];
Dave Barachffb14b92018-09-11 17:20:23 -0400319 h->freelists[log2_pages] = (u64) BV (clib_bihash_get_offset) (h, v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700320}
321
322static inline void
Dave Barach908a5ea2017-07-14 12:42:21 -0400323BV (make_working_copy) (BVT (clib_bihash) * h, BVT (clib_bihash_bucket) * b)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700324{
Dave Barachc3799992016-08-15 11:12:27 -0400325 BVT (clib_bihash_value) * v;
Dave Barach908a5ea2017-07-14 12:42:21 -0400326 BVT (clib_bihash_bucket) working_bucket __attribute__ ((aligned (8)));
Dave Barachc3799992016-08-15 11:12:27 -0400327 BVT (clib_bihash_value) * working_copy;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200328 u32 thread_index = os_get_thread_index ();
Dave Barachba7ddfe2017-05-17 20:20:50 -0400329 int log2_working_copy_length;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700330
Dave Barach508498f2018-07-19 12:11:16 -0400331 ASSERT (h->alloc_lock[0]);
332
Damjan Marionf55f9b82017-05-10 21:06:28 +0200333 if (thread_index >= vec_len (h->working_copies))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700334 {
Damjan Marionf55f9b82017-05-10 21:06:28 +0200335 vec_validate (h->working_copies, thread_index);
Steve Shin871cdec2017-06-02 10:09:02 -0700336 vec_validate_init_empty (h->working_copy_lengths, thread_index, ~0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700337 }
338
Dave Barachc3799992016-08-15 11:12:27 -0400339 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700340 * working_copies are per-cpu so that near-simultaneous
341 * updates from multiple threads will not result in sporadic, spurious
Dave Barachc3799992016-08-15 11:12:27 -0400342 * lookup failures.
Ed Warnickecb9cada2015-12-08 15:45:58 -0700343 */
Damjan Marionf55f9b82017-05-10 21:06:28 +0200344 working_copy = h->working_copies[thread_index];
Dave Barachba7ddfe2017-05-17 20:20:50 -0400345 log2_working_copy_length = h->working_copy_lengths[thread_index];
Ed Warnickecb9cada2015-12-08 15:45:58 -0700346
347 h->saved_bucket.as_u64 = b->as_u64;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700348
Dave Barachba7ddfe2017-05-17 20:20:50 -0400349 if (b->log2_pages > log2_working_copy_length)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700350 {
Dave Barach97f5af02018-02-22 09:48:45 -0500351 /*
352 * It's not worth the bookkeeping to free working copies
353 * if (working_copy)
354 * clib_mem_free (working_copy);
355 */
356 working_copy = BV (alloc_aligned)
357 (h, sizeof (working_copy[0]) * (1 << b->log2_pages));
Dave Barachba7ddfe2017-05-17 20:20:50 -0400358 h->working_copy_lengths[thread_index] = b->log2_pages;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200359 h->working_copies[thread_index] = working_copy;
Dave Barach2ce28d62019-05-03 12:58:01 -0400360
361 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_working_copy_lost,
362 1ULL << b->log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700363 }
364
Dave Barachc3799992016-08-15 11:12:27 -0400365 v = BV (clib_bihash_get_value) (h, b->offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700366
Dave Barach178cf492018-11-13 16:34:13 -0500367 clib_memcpy_fast (working_copy, v, sizeof (*v) * (1 << b->log2_pages));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700368 working_bucket.as_u64 = b->as_u64;
Dave Barachc3799992016-08-15 11:12:27 -0400369 working_bucket.offset = BV (clib_bihash_get_offset) (h, working_copy);
370 CLIB_MEMORY_BARRIER ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700371 b->as_u64 = working_bucket.as_u64;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200372 h->working_copies[thread_index] = working_copy;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700373}
374
Dave Barachc3799992016-08-15 11:12:27 -0400375static
376BVT (clib_bihash_value) *
377BV (split_and_rehash)
378 (BVT (clib_bihash) * h,
Dave Barachba7ddfe2017-05-17 20:20:50 -0400379 BVT (clib_bihash_value) * old_values, u32 old_log2_pages,
380 u32 new_log2_pages)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700381{
Dave Barach5e6b9582016-12-12 15:37:29 -0500382 BVT (clib_bihash_value) * new_values, *new_v;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400383 int i, j, length_in_kvs;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700384
Dave Barach508498f2018-07-19 12:11:16 -0400385 ASSERT (h->alloc_lock[0]);
386
Dave Barachc3799992016-08-15 11:12:27 -0400387 new_values = BV (value_alloc) (h, new_log2_pages);
Dave Barachba7ddfe2017-05-17 20:20:50 -0400388 length_in_kvs = (1 << old_log2_pages) * BIHASH_KVP_PER_PAGE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700389
Dave Barachba7ddfe2017-05-17 20:20:50 -0400390 for (i = 0; i < length_in_kvs; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700391 {
392 u64 new_hash;
Dave Barachc3799992016-08-15 11:12:27 -0400393
Dave Barach5e6b9582016-12-12 15:37:29 -0500394 /* Entry not in use? Forget it */
395 if (BV (clib_bihash_is_free) (&(old_values->kvp[i])))
396 continue;
397
398 /* rehash the item onto its new home-page */
399 new_hash = BV (clib_bihash_hash) (&(old_values->kvp[i]));
400 new_hash >>= h->log2_nbuckets;
401 new_hash &= (1 << new_log2_pages) - 1;
402 new_v = &new_values[new_hash];
403
404 /* Across the new home-page */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700405 for (j = 0; j < BIHASH_KVP_PER_PAGE; j++)
Dave Barachc3799992016-08-15 11:12:27 -0400406 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500407 /* Empty slot */
408 if (BV (clib_bihash_is_free) (&(new_v->kvp[j])))
Dave Barachc3799992016-08-15 11:12:27 -0400409 {
Dave Barach178cf492018-11-13 16:34:13 -0500410 clib_memcpy_fast (&(new_v->kvp[j]), &(old_values->kvp[i]),
411 sizeof (new_v->kvp[j]));
Dave Barach5e6b9582016-12-12 15:37:29 -0500412 goto doublebreak;
Dave Barachc3799992016-08-15 11:12:27 -0400413 }
Dave Barachc3799992016-08-15 11:12:27 -0400414 }
Dave Barach5e6b9582016-12-12 15:37:29 -0500415 /* Crap. Tell caller to try again */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400416 BV (value_free) (h, new_values, new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500417 return 0;
418 doublebreak:;
419 }
Dave Barachba7ddfe2017-05-17 20:20:50 -0400420
Dave Barach5e6b9582016-12-12 15:37:29 -0500421 return new_values;
422}
423
424static
425BVT (clib_bihash_value) *
426BV (split_and_rehash_linear)
427 (BVT (clib_bihash) * h,
Dave Barachba7ddfe2017-05-17 20:20:50 -0400428 BVT (clib_bihash_value) * old_values, u32 old_log2_pages,
429 u32 new_log2_pages)
Dave Barach5e6b9582016-12-12 15:37:29 -0500430{
431 BVT (clib_bihash_value) * new_values;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400432 int i, j, new_length, old_length;
Dave Barach5e6b9582016-12-12 15:37:29 -0500433
Dave Barach508498f2018-07-19 12:11:16 -0400434 ASSERT (h->alloc_lock[0]);
435
Dave Barach5e6b9582016-12-12 15:37:29 -0500436 new_values = BV (value_alloc) (h, new_log2_pages);
437 new_length = (1 << new_log2_pages) * BIHASH_KVP_PER_PAGE;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400438 old_length = (1 << old_log2_pages) * BIHASH_KVP_PER_PAGE;
Dave Barach5e6b9582016-12-12 15:37:29 -0500439
440 j = 0;
441 /* Across the old value array */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400442 for (i = 0; i < old_length; i++)
Dave Barach5e6b9582016-12-12 15:37:29 -0500443 {
444 /* Find a free slot in the new linear scan bucket */
445 for (; j < new_length; j++)
446 {
Dave Barach8f544962017-01-18 10:23:22 -0500447 /* Old value not in use? Forget it. */
Dave Barach5e6b9582016-12-12 15:37:29 -0500448 if (BV (clib_bihash_is_free) (&(old_values->kvp[i])))
449 goto doublebreak;
450
451 /* New value should never be in use */
452 if (BV (clib_bihash_is_free) (&(new_values->kvp[j])))
453 {
454 /* Copy the old value and move along */
Dave Barach178cf492018-11-13 16:34:13 -0500455 clib_memcpy_fast (&(new_values->kvp[j]), &(old_values->kvp[i]),
456 sizeof (new_values->kvp[j]));
Dave Barach5e6b9582016-12-12 15:37:29 -0500457 j++;
458 goto doublebreak;
459 }
Dave Barach5e6b9582016-12-12 15:37:29 -0500460 }
Dave Barach8f544962017-01-18 10:23:22 -0500461 /* This should never happen... */
462 clib_warning ("BUG: linear rehash failed!");
Dave Barachba7ddfe2017-05-17 20:20:50 -0400463 BV (value_free) (h, new_values, new_log2_pages);
Dave Barach8f544962017-01-18 10:23:22 -0500464 return 0;
465
Dave Barach5e6b9582016-12-12 15:37:29 -0500466 doublebreak:;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700467 }
468 return new_values;
469}
470
Matus Fabian828d27e2018-08-21 03:15:50 -0700471static inline int BV (clib_bihash_add_del_inline)
472 (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * add_v, int is_add,
473 int (*is_stale_cb) (BVT (clib_bihash_kv) *, void *), void *arg)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700474{
475 u32 bucket_index;
Dave Barach908a5ea2017-07-14 12:42:21 -0400476 BVT (clib_bihash_bucket) * b, tmp_b;
Dave Barachc3799992016-08-15 11:12:27 -0400477 BVT (clib_bihash_value) * v, *new_v, *save_new_v, *working_copy;
Dave Barach5e6b9582016-12-12 15:37:29 -0500478 int i, limit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700479 u64 hash, new_hash;
Dave Barachba7ddfe2017-05-17 20:20:50 -0400480 u32 new_log2_pages, old_log2_pages;
Damjan Marionf55f9b82017-05-10 21:06:28 +0200481 u32 thread_index = os_get_thread_index ();
Dave Barach5e6b9582016-12-12 15:37:29 -0500482 int mark_bucket_linear;
483 int resplit_once;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700484
Dave Barach67d09e02019-08-01 08:15:01 -0400485 /*
486 * Create the table (is_add=1,2), or flunk the request now (is_add=0)
487 * Use the alloc_lock to protect the instantiate operation.
488 */
489 if (PREDICT_FALSE (h->instantiated == 0))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400490 {
491 if (is_add == 0)
492 return (-1);
Dave Barach67d09e02019-08-01 08:15:01 -0400493
494 BV (clib_bihash_alloc_lock) (h);
495 if (h->instantiated == 0)
496 BV (clib_bihash_instantiate) (h);
497 BV (clib_bihash_alloc_unlock) (h);
Dave Barach32dcd3b2019-07-08 12:25:38 -0400498 }
499
Dave Barachc3799992016-08-15 11:12:27 -0400500 hash = BV (clib_bihash_hash) (add_v);
501
502 bucket_index = hash & (h->nbuckets - 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700503 b = &h->buckets[bucket_index];
504
505 hash >>= h->log2_nbuckets;
506
Dave Barach508498f2018-07-19 12:11:16 -0400507 BV (clib_bihash_lock_bucket) (b);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700508
509 /* First elt in the bucket? */
Damjan Marion882fcfe2018-07-17 23:01:49 +0200510 if (BV (clib_bihash_bucket_is_empty) (b))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700511 {
512 if (is_add == 0)
Dave Barachc3799992016-08-15 11:12:27 -0400513 {
Dave Barach508498f2018-07-19 12:11:16 -0400514 BV (clib_bihash_unlock_bucket) (b);
515 return (-1);
Dave Barachc3799992016-08-15 11:12:27 -0400516 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700517
Dave Barach508498f2018-07-19 12:11:16 -0400518 BV (clib_bihash_alloc_lock) (h);
Dave Barachc3799992016-08-15 11:12:27 -0400519 v = BV (value_alloc) (h, 0);
Dave Barach508498f2018-07-19 12:11:16 -0400520 BV (clib_bihash_alloc_unlock) (h);
Dave Barachba7ddfe2017-05-17 20:20:50 -0400521
Dave Barachc3799992016-08-15 11:12:27 -0400522 *v->kvp = *add_v;
Dave Barach508498f2018-07-19 12:11:16 -0400523 tmp_b.as_u64 = 0; /* clears bucket lock */
Dave Barachc3799992016-08-15 11:12:27 -0400524 tmp_b.offset = BV (clib_bihash_get_offset) (h, v);
Dave Barache7d212f2018-02-07 13:14:06 -0500525 tmp_b.refcnt = 1;
Dave Barach508498f2018-07-19 12:11:16 -0400526 CLIB_MEMORY_BARRIER ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700527
Tom Seidenberg97f8ae92019-03-15 10:15:26 -0400528 b->as_u64 = tmp_b.as_u64; /* unlocks the bucket */
Dave Barach2ce28d62019-05-03 12:58:01 -0400529 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_alloc_add, 1);
530
Dave Barach508498f2018-07-19 12:11:16 -0400531 return (0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700532 }
533
Dave Barach508498f2018-07-19 12:11:16 -0400534 /* WARNING: we're still looking at the live copy... */
Dave Barach5e6b9582016-12-12 15:37:29 -0500535 limit = BIHASH_KVP_PER_PAGE;
Dave Barach508498f2018-07-19 12:11:16 -0400536 v = BV (clib_bihash_get_value) (h, b->offset);
537
Dave Barach5e6b9582016-12-12 15:37:29 -0500538 v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
539 if (b->linear_search)
540 limit <<= b->log2_pages;
Dave Barachc3799992016-08-15 11:12:27 -0400541
Ed Warnickecb9cada2015-12-08 15:45:58 -0700542 if (is_add)
543 {
Dave Barachc3799992016-08-15 11:12:27 -0400544 /*
Dave Barach508498f2018-07-19 12:11:16 -0400545 * Because reader threads are looking at live data,
546 * we have to be extra careful. Readers do NOT hold the
547 * bucket lock. We need to be SLOWER than a search, past the
548 * point where readers CHECK the bucket lock.
549 */
550
551 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700552 * For obvious (in hindsight) reasons, see if we're supposed to
553 * replace an existing key, then look for an empty slot.
554 */
Dave Barach5e6b9582016-12-12 15:37:29 -0500555 for (i = 0; i < limit; i++)
Dave Barachc3799992016-08-15 11:12:27 -0400556 {
Dave Baracha11bf452019-04-17 17:27:31 -0400557 if (BV (clib_bihash_key_compare) (v->kvp[i].key, add_v->key))
Dave Barachc3799992016-08-15 11:12:27 -0400558 {
Dave Barach9e4946b2019-07-08 14:47:44 -0400559 /* Add but do not overwrite? */
560 if (is_add == 2)
561 {
562 BV (clib_bihash_unlock_bucket) (b);
563 return (-2);
564 }
565
Dave Barach508498f2018-07-19 12:11:16 -0400566 CLIB_MEMORY_BARRIER (); /* Add a delay */
Dave Barach178cf492018-11-13 16:34:13 -0500567 clib_memcpy_fast (&(v->kvp[i]), add_v, sizeof (*add_v));
Dave Barach508498f2018-07-19 12:11:16 -0400568 BV (clib_bihash_unlock_bucket) (b);
Dave Barach2ce28d62019-05-03 12:58:01 -0400569 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_replace, 1);
Dave Barach508498f2018-07-19 12:11:16 -0400570 return (0);
Dave Barachc3799992016-08-15 11:12:27 -0400571 }
572 }
Dave Barach508498f2018-07-19 12:11:16 -0400573 /*
574 * Look for an empty slot. If found, use it
575 */
Dave Barach5e6b9582016-12-12 15:37:29 -0500576 for (i = 0; i < limit; i++)
Dave Barachc3799992016-08-15 11:12:27 -0400577 {
578 if (BV (clib_bihash_is_free) (&(v->kvp[i])))
579 {
Dave Barach508498f2018-07-19 12:11:16 -0400580 /*
581 * Copy the value first, so that if a reader manages
582 * to match the new key, the value will be right...
583 */
Dave Barach178cf492018-11-13 16:34:13 -0500584 clib_memcpy_fast (&(v->kvp[i].value),
585 &add_v->value, sizeof (add_v->value));
Dave Barach508498f2018-07-19 12:11:16 -0400586 CLIB_MEMORY_BARRIER (); /* Make sure the value has settled */
Dave Barach178cf492018-11-13 16:34:13 -0500587 clib_memcpy_fast (&(v->kvp[i]), &add_v->key,
588 sizeof (add_v->key));
Dave Barache7d212f2018-02-07 13:14:06 -0500589 b->refcnt++;
Dave Barach9466c452018-08-24 17:21:14 -0400590 ASSERT (b->refcnt > 0);
Dave Barach508498f2018-07-19 12:11:16 -0400591 BV (clib_bihash_unlock_bucket) (b);
Dave Barach2ce28d62019-05-03 12:58:01 -0400592 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_add, 1);
Dave Barach508498f2018-07-19 12:11:16 -0400593 return (0);
Dave Barachc3799992016-08-15 11:12:27 -0400594 }
595 }
Matus Fabian828d27e2018-08-21 03:15:50 -0700596 /* look for stale data to overwrite */
597 if (is_stale_cb)
598 {
599 for (i = 0; i < limit; i++)
600 {
601 if (is_stale_cb (&(v->kvp[i]), arg))
602 {
603 CLIB_MEMORY_BARRIER ();
Dave Barach178cf492018-11-13 16:34:13 -0500604 clib_memcpy_fast (&(v->kvp[i]), add_v, sizeof (*add_v));
Matus Fabian828d27e2018-08-21 03:15:50 -0700605 BV (clib_bihash_unlock_bucket) (b);
Dave Barach2ce28d62019-05-03 12:58:01 -0400606 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_replace, 1);
Matus Fabian828d27e2018-08-21 03:15:50 -0700607 return (0);
608 }
609 }
610 }
Dave Barach508498f2018-07-19 12:11:16 -0400611 /* Out of space in this bucket, split the bucket... */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700612 }
Dave Barach508498f2018-07-19 12:11:16 -0400613 else /* delete case */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700614 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500615 for (i = 0; i < limit; i++)
Dave Barachc3799992016-08-15 11:12:27 -0400616 {
Dave Barach508498f2018-07-19 12:11:16 -0400617 /* Found the key? Kill it... */
Dave Baracha11bf452019-04-17 17:27:31 -0400618 if (BV (clib_bihash_key_compare) (v->kvp[i].key, add_v->key))
Dave Barachc3799992016-08-15 11:12:27 -0400619 {
Dave Barachb7b92992018-10-17 10:38:51 -0400620 clib_memset (&(v->kvp[i]), 0xff, sizeof (*(add_v)));
Dave Barach508498f2018-07-19 12:11:16 -0400621 /* Is the bucket empty? */
622 if (PREDICT_TRUE (b->refcnt > 1))
Dave Barache7d212f2018-02-07 13:14:06 -0500623 {
Dave Barach508498f2018-07-19 12:11:16 -0400624 b->refcnt--;
625 BV (clib_bihash_unlock_bucket) (b);
Dave Barach2ce28d62019-05-03 12:58:01 -0400626 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_del, 1);
Dave Barach508498f2018-07-19 12:11:16 -0400627 return (0);
Dave Barache7d212f2018-02-07 13:14:06 -0500628 }
Dave Barach508498f2018-07-19 12:11:16 -0400629 else /* yes, free it */
Dave Barache7d212f2018-02-07 13:14:06 -0500630 {
Dave Barach508498f2018-07-19 12:11:16 -0400631 /* Save old bucket value, need log2_pages to free it */
632 tmp_b.as_u64 = b->as_u64;
633 CLIB_MEMORY_BARRIER ();
634
635 /* Kill and unlock the bucket */
636 b->as_u64 = 0;
637
638 /* And free the backing storage */
639 BV (clib_bihash_alloc_lock) (h);
640 /* Note: v currently points into the middle of the bucket */
641 v = BV (clib_bihash_get_value) (h, tmp_b.offset);
642 BV (value_free) (h, v, tmp_b.log2_pages);
643 BV (clib_bihash_alloc_unlock) (h);
Dave Barach2ce28d62019-05-03 12:58:01 -0400644 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_del_free,
645 1);
Dave Barach508498f2018-07-19 12:11:16 -0400646 return (0);
Dave Barache7d212f2018-02-07 13:14:06 -0500647 }
Dave Barachc3799992016-08-15 11:12:27 -0400648 }
649 }
Dave Barach508498f2018-07-19 12:11:16 -0400650 /* Not found... */
651 BV (clib_bihash_unlock_bucket) (b);
652 return (-3);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700653 }
654
Dave Barach508498f2018-07-19 12:11:16 -0400655 /* Move readers to a (locked) temp copy of the bucket */
656 BV (clib_bihash_alloc_lock) (h);
657 BV (make_working_copy) (h, b);
658
659 v = BV (clib_bihash_get_value) (h, h->saved_bucket.offset);
660
Dave Barachba7ddfe2017-05-17 20:20:50 -0400661 old_log2_pages = h->saved_bucket.log2_pages;
662 new_log2_pages = old_log2_pages + 1;
Dave Barach5e6b9582016-12-12 15:37:29 -0500663 mark_bucket_linear = 0;
Dave Barach2ce28d62019-05-03 12:58:01 -0400664 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_split_add, 1);
665 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_splits, old_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700666
Damjan Marionf55f9b82017-05-10 21:06:28 +0200667 working_copy = h->working_copies[thread_index];
Dave Barach5e6b9582016-12-12 15:37:29 -0500668 resplit_once = 0;
Dave Barach2ce28d62019-05-03 12:58:01 -0400669 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_splits, 1);
Dave Barach5e6b9582016-12-12 15:37:29 -0500670
Dave Barachba7ddfe2017-05-17 20:20:50 -0400671 new_v = BV (split_and_rehash) (h, working_copy, old_log2_pages,
672 new_log2_pages);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700673 if (new_v == 0)
674 {
Dave Barach5e6b9582016-12-12 15:37:29 -0500675 try_resplit:
676 resplit_once = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700677 new_log2_pages++;
Dave Barach5e6b9582016-12-12 15:37:29 -0500678 /* Try re-splitting. If that fails, fall back to linear search */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400679 new_v = BV (split_and_rehash) (h, working_copy, old_log2_pages,
680 new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500681 if (new_v == 0)
682 {
683 mark_linear:
684 new_log2_pages--;
685 /* pinned collisions, use linear search */
686 new_v =
Dave Barachba7ddfe2017-05-17 20:20:50 -0400687 BV (split_and_rehash_linear) (h, working_copy, old_log2_pages,
688 new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500689 mark_bucket_linear = 1;
Dave Barach2ce28d62019-05-03 12:58:01 -0400690 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_linear, 1);
Dave Barach5e6b9582016-12-12 15:37:29 -0500691 }
Dave Barach2ce28d62019-05-03 12:58:01 -0400692 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_resplit, 1);
693 BV (clib_bihash_increment_stat) (h, BIHASH_STAT_splits,
694 old_log2_pages + 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700695 }
696
697 /* Try to add the new entry */
698 save_new_v = new_v;
Dave Barachc3799992016-08-15 11:12:27 -0400699 new_hash = BV (clib_bihash_hash) (add_v);
Dave Barach5e6b9582016-12-12 15:37:29 -0500700 limit = BIHASH_KVP_PER_PAGE;
701 if (mark_bucket_linear)
702 limit <<= new_log2_pages;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700703 new_hash >>= h->log2_nbuckets;
Dave Barach5e6b9582016-12-12 15:37:29 -0500704 new_hash &= (1 << new_log2_pages) - 1;
705 new_v += mark_bucket_linear ? 0 : new_hash;
Dave Barachc3799992016-08-15 11:12:27 -0400706
Dave Barach5e6b9582016-12-12 15:37:29 -0500707 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700708 {
Dave Barachc3799992016-08-15 11:12:27 -0400709 if (BV (clib_bihash_is_free) (&(new_v->kvp[i])))
710 {
Dave Barach178cf492018-11-13 16:34:13 -0500711 clib_memcpy_fast (&(new_v->kvp[i]), add_v, sizeof (*add_v));
Dave Barachc3799992016-08-15 11:12:27 -0400712 goto expand_ok;
713 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700714 }
Dave Barachba7ddfe2017-05-17 20:20:50 -0400715
Ed Warnickecb9cada2015-12-08 15:45:58 -0700716 /* Crap. Try again */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400717 BV (value_free) (h, save_new_v, new_log2_pages);
Dave Barach5e6b9582016-12-12 15:37:29 -0500718 /*
719 * If we've already doubled the size of the bucket once,
720 * fall back to linear search now.
721 */
722 if (resplit_once)
723 goto mark_linear;
724 else
725 goto try_resplit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700726
Dave Barachc3799992016-08-15 11:12:27 -0400727expand_ok:
Dave Barach5e6b9582016-12-12 15:37:29 -0500728 tmp_b.log2_pages = new_log2_pages;
Dave Barachc3799992016-08-15 11:12:27 -0400729 tmp_b.offset = BV (clib_bihash_get_offset) (h, save_new_v);
Dave Barach5e6b9582016-12-12 15:37:29 -0500730 tmp_b.linear_search = mark_bucket_linear;
Dave Barache7d212f2018-02-07 13:14:06 -0500731 tmp_b.refcnt = h->saved_bucket.refcnt + 1;
Dave Barach9466c452018-08-24 17:21:14 -0400732 ASSERT (tmp_b.refcnt > 0);
Dave Barach508498f2018-07-19 12:11:16 -0400733 tmp_b.lock = 0;
Dave Barachc3799992016-08-15 11:12:27 -0400734 CLIB_MEMORY_BARRIER ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700735 b->as_u64 = tmp_b.as_u64;
Andrew Yourtchenkodf32bc42018-09-20 15:36:51 +0200736 /* free the old bucket */
737 v = BV (clib_bihash_get_value) (h, h->saved_bucket.offset);
738 BV (value_free) (h, v, h->saved_bucket.log2_pages);
Dave Barach508498f2018-07-19 12:11:16 -0400739 BV (clib_bihash_alloc_unlock) (h);
740 return (0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700741}
742
Matus Fabian828d27e2018-08-21 03:15:50 -0700743int BV (clib_bihash_add_del)
744 (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * add_v, int is_add)
745{
746 return BV (clib_bihash_add_del_inline) (h, add_v, is_add, 0, 0);
747}
748
749int BV (clib_bihash_add_or_overwrite_stale)
750 (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * add_v,
751 int (*stale_callback) (BVT (clib_bihash_kv) *, void *), void *arg)
752{
753 return BV (clib_bihash_add_del_inline) (h, add_v, 1, stale_callback, arg);
754}
755
Dave Barachc3799992016-08-15 11:12:27 -0400756int BV (clib_bihash_search)
Dave Barach908a5ea2017-07-14 12:42:21 -0400757 (BVT (clib_bihash) * h,
Dave Barachc3799992016-08-15 11:12:27 -0400758 BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700759{
760 u64 hash;
761 u32 bucket_index;
Dave Barachc3799992016-08-15 11:12:27 -0400762 BVT (clib_bihash_value) * v;
Dave Barach908a5ea2017-07-14 12:42:21 -0400763 BVT (clib_bihash_bucket) * b;
Dave Barach5e6b9582016-12-12 15:37:29 -0500764 int i, limit;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700765
Dave Barachc3799992016-08-15 11:12:27 -0400766 ASSERT (valuep);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700767
Dave Barach32dcd3b2019-07-08 12:25:38 -0400768 if (PREDICT_FALSE (alloc_arena (h) == 0))
769 return -1;
770
Dave Barachc3799992016-08-15 11:12:27 -0400771 hash = BV (clib_bihash_hash) (search_key);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700772
Dave Barachc3799992016-08-15 11:12:27 -0400773 bucket_index = hash & (h->nbuckets - 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700774 b = &h->buckets[bucket_index];
775
Damjan Marion882fcfe2018-07-17 23:01:49 +0200776 if (BV (clib_bihash_bucket_is_empty) (b))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700777 return -1;
778
Dave Barach508498f2018-07-19 12:11:16 -0400779 if (PREDICT_FALSE (b->lock))
Dave Barach908a5ea2017-07-14 12:42:21 -0400780 {
Dave Barach508498f2018-07-19 12:11:16 -0400781 volatile BVT (clib_bihash_bucket) * bv = b;
782 while (bv->lock)
Damjan Marion2a03efe2018-07-20 21:48:59 +0200783 CLIB_PAUSE ();
Dave Barach908a5ea2017-07-14 12:42:21 -0400784 }
785
Ed Warnickecb9cada2015-12-08 15:45:58 -0700786 hash >>= h->log2_nbuckets;
787
Dave Barachc3799992016-08-15 11:12:27 -0400788 v = BV (clib_bihash_get_value) (h, b->offset);
Dave Barach5e6b9582016-12-12 15:37:29 -0500789 limit = BIHASH_KVP_PER_PAGE;
790 v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
791 if (PREDICT_FALSE (b->linear_search))
792 limit <<= b->log2_pages;
Dave Barachc3799992016-08-15 11:12:27 -0400793
Dave Barach5e6b9582016-12-12 15:37:29 -0500794 for (i = 0; i < limit; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700795 {
Dave Barachc3799992016-08-15 11:12:27 -0400796 if (BV (clib_bihash_key_compare) (v->kvp[i].key, search_key->key))
797 {
798 *valuep = v->kvp[i];
799 return 0;
800 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700801 }
802 return -1;
803}
804
Dave Barachc3799992016-08-15 11:12:27 -0400805u8 *BV (format_bihash) (u8 * s, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700806{
Dave Barachc3799992016-08-15 11:12:27 -0400807 BVT (clib_bihash) * h = va_arg (*args, BVT (clib_bihash) *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700808 int verbose = va_arg (*args, int);
Dave Barach908a5ea2017-07-14 12:42:21 -0400809 BVT (clib_bihash_bucket) * b;
Dave Barachc3799992016-08-15 11:12:27 -0400810 BVT (clib_bihash_value) * v;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700811 int i, j, k;
812 u64 active_elements = 0;
Dave Barache7d212f2018-02-07 13:14:06 -0500813 u64 active_buckets = 0;
814 u64 linear_buckets = 0;
Dave Barach97f5af02018-02-22 09:48:45 -0500815 u64 used_bytes;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700816
817 s = format (s, "Hash table %s\n", h->name ? h->name : (u8 *) "(unnamed)");
Dave Barachc3799992016-08-15 11:12:27 -0400818
Dave Barach32dcd3b2019-07-08 12:25:38 -0400819 if (PREDICT_FALSE (alloc_arena (h) == 0))
820 return format (s, "[empty, uninitialized]");
821
Ed Warnickecb9cada2015-12-08 15:45:58 -0700822 for (i = 0; i < h->nbuckets; i++)
823 {
Dave Barachc3799992016-08-15 11:12:27 -0400824 b = &h->buckets[i];
Damjan Marion882fcfe2018-07-17 23:01:49 +0200825 if (BV (clib_bihash_bucket_is_empty) (b))
Dave Barachc3799992016-08-15 11:12:27 -0400826 {
827 if (verbose > 1)
828 s = format (s, "[%d]: empty\n", i);
829 continue;
830 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700831
Dave Barache7d212f2018-02-07 13:14:06 -0500832 active_buckets++;
833
834 if (b->linear_search)
835 linear_buckets++;
836
Ed Warnickecb9cada2015-12-08 15:45:58 -0700837 if (verbose)
Dave Barachc3799992016-08-15 11:12:27 -0400838 {
Dave Barach9466c452018-08-24 17:21:14 -0400839 s = format (s, "[%d]: heap offset %lld, len %d, linear %d\n", i,
Dave Barach5e6b9582016-12-12 15:37:29 -0500840 b->offset, (1 << b->log2_pages), b->linear_search);
Dave Barachc3799992016-08-15 11:12:27 -0400841 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700842
Dave Barachc3799992016-08-15 11:12:27 -0400843 v = BV (clib_bihash_get_value) (h, b->offset);
844 for (j = 0; j < (1 << b->log2_pages); j++)
845 {
846 for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
847 {
848 if (BV (clib_bihash_is_free) (&v->kvp[k]))
849 {
850 if (verbose > 1)
851 s = format (s, " %d: empty\n",
852 j * BIHASH_KVP_PER_PAGE + k);
853 continue;
854 }
855 if (verbose)
856 {
Vijayabhaskar Katamreddyfb8e61c2017-12-14 13:20:50 -0800857 if (h->fmt_fn)
858 {
859 s = format (s, " %d: %U\n",
860 j * BIHASH_KVP_PER_PAGE + k,
Vijayabhaskar Katamreddy72739a62019-05-07 13:27:32 -0700861 h->fmt_fn, &(v->kvp[k]), verbose);
Vijayabhaskar Katamreddyfb8e61c2017-12-14 13:20:50 -0800862 }
863 else
864 {
865 s = format (s, " %d: %U\n",
866 j * BIHASH_KVP_PER_PAGE + k,
867 BV (format_bihash_kvp), &(v->kvp[k]));
868 }
Dave Barachc3799992016-08-15 11:12:27 -0400869 }
870 active_elements++;
871 }
872 v++;
873 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700874 }
875
Dave Barache7d212f2018-02-07 13:14:06 -0500876 s = format (s, " %lld active elements %lld active buckets\n",
877 active_elements, active_buckets);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700878 s = format (s, " %d free lists\n", vec_len (h->freelists));
Dave Barache7d212f2018-02-07 13:14:06 -0500879
880 for (i = 0; i < vec_len (h->freelists); i++)
881 {
882 u32 nfree = 0;
883 BVT (clib_bihash_value) * free_elt;
Dave Barachffb14b92018-09-11 17:20:23 -0400884 u64 free_elt_as_u64 = h->freelists[i];
Dave Barache7d212f2018-02-07 13:14:06 -0500885
Dave Barachffb14b92018-09-11 17:20:23 -0400886 while (free_elt_as_u64)
Dave Barache7d212f2018-02-07 13:14:06 -0500887 {
Dave Barachffb14b92018-09-11 17:20:23 -0400888 free_elt = BV (clib_bihash_get_value) (h, free_elt_as_u64);
Dave Barache7d212f2018-02-07 13:14:06 -0500889 nfree++;
Dave Barachffb14b92018-09-11 17:20:23 -0400890 free_elt_as_u64 = free_elt->next_free_as_u64;
Dave Barache7d212f2018-02-07 13:14:06 -0500891 }
892
Dave Barach9466c452018-08-24 17:21:14 -0400893 if (nfree || verbose)
894 s = format (s, " [len %d] %u free elts\n", 1 << i, nfree);
Dave Barache7d212f2018-02-07 13:14:06 -0500895 }
896
897 s = format (s, " %lld linear search buckets\n", linear_buckets);
Dave Barachffb14b92018-09-11 17:20:23 -0400898 used_bytes = alloc_arena_next (h);
Dave Barach97f5af02018-02-22 09:48:45 -0500899 s = format (s,
900 " arena: base %llx, next %llx\n"
901 " used %lld b (%lld Mbytes) of %lld b (%lld Mbytes)\n",
Dave Barach9466c452018-08-24 17:21:14 -0400902 alloc_arena (h), alloc_arena_next (h),
Dave Barach97f5af02018-02-22 09:48:45 -0500903 used_bytes, used_bytes >> 20,
Dave Barach9466c452018-08-24 17:21:14 -0400904 alloc_arena_size (h), alloc_arena_size (h) >> 20);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700905 return s;
906}
907
Dave Barachc3799992016-08-15 11:12:27 -0400908void BV (clib_bihash_foreach_key_value_pair)
909 (BVT (clib_bihash) * h, void *callback, void *arg)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700910{
911 int i, j, k;
Dave Barach908a5ea2017-07-14 12:42:21 -0400912 BVT (clib_bihash_bucket) * b;
Dave Barachc3799992016-08-15 11:12:27 -0400913 BVT (clib_bihash_value) * v;
914 void (*fp) (BVT (clib_bihash_kv) *, void *) = callback;
915
Dave Barach32dcd3b2019-07-08 12:25:38 -0400916 if (PREDICT_FALSE (alloc_arena (h) == 0))
917 return;
918
Ed Warnickecb9cada2015-12-08 15:45:58 -0700919 for (i = 0; i < h->nbuckets; i++)
920 {
Dave Barachc3799992016-08-15 11:12:27 -0400921 b = &h->buckets[i];
Damjan Marion882fcfe2018-07-17 23:01:49 +0200922 if (BV (clib_bihash_bucket_is_empty) (b))
Dave Barachc3799992016-08-15 11:12:27 -0400923 continue;
924
925 v = BV (clib_bihash_get_value) (h, b->offset);
926 for (j = 0; j < (1 << b->log2_pages); j++)
927 {
928 for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
929 {
930 if (BV (clib_bihash_is_free) (&v->kvp[k]))
931 continue;
932
933 (*fp) (&v->kvp[k], arg);
Dave Barachca45ee72018-08-06 08:43:47 -0400934 /*
935 * In case the callback deletes the last entry in the bucket...
936 */
937 if (BV (clib_bihash_bucket_is_empty) (b))
938 goto doublebreak;
Dave Barachc3799992016-08-15 11:12:27 -0400939 }
940 v++;
941 }
Dave Barachca45ee72018-08-06 08:43:47 -0400942 doublebreak:
943 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700944 }
945}
Dave Barachdd3a57f2016-07-27 16:58:51 -0400946
Chris Luke16bcf7d2016-09-01 14:31:46 -0400947/** @endcond */
Dave Barachc3799992016-08-15 11:12:27 -0400948
949/*
950 * fd.io coding-style-patch-verification: ON
951 *
952 * Local Variables:
953 * eval: (c-set-style "gnu")
954 * End:
955 */