api: fix coverity warnings
[vpp.git] / src / vppinfra / bihash_template.h
1 /*
2   Copyright (c) 2014 Cisco and/or its affiliates.
3
4   * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15 */
16
17 /** @cond DOCUMENTATION_IS_IN_BIHASH_DOC_H */
18
19 /*
20  * Note: to instantiate the template multiple times in a single file,
21  * #undef __included_bihash_template_h__...
22  */
23 #ifndef __included_bihash_template_h__
24 #define __included_bihash_template_h__
25
26 #include <vppinfra/heap.h>
27 #include <vppinfra/format.h>
28 #include <vppinfra/pool.h>
29 #include <vppinfra/cache.h>
30 #include <vppinfra/lock.h>
31
32 #ifndef BIHASH_TYPE
33 #error BIHASH_TYPE not defined
34 #endif
35
36 #ifdef BIHASH_32_64_SVM
37 #undef HAVE_MEMFD_CREATE
38 #include <vppinfra/linux/syscall.h>
39 #include <fcntl.h>
40 #define F_LINUX_SPECIFIC_BASE 1024
41 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
42 #define F_SEAL_SHRINK (2)
43 /* Max page size 2**16 due to refcount width  */
44 #define BIHASH_FREELIST_LENGTH 17
45 #endif
46
47 #define _bv(a,b) a##b
48 #define __bv(a,b) _bv(a,b)
49 #define BV(a) __bv(a,BIHASH_TYPE)
50
51 #define _bvt(a,b) a##b##_t
52 #define __bvt(a,b) _bvt(a,b)
53 #define BVT(a) __bvt(a,BIHASH_TYPE)
54
55 #define _bvs(a,b) struct a##b
56 #define __bvs(a,b) _bvs(a,b)
57 #define BVS(a) __bvs(a,BIHASH_TYPE)
58
59 #if _LP64 == 0
60 #define OVERFLOW_ASSERT(x) ASSERT(((x) & 0xFFFFFFFF00000000ULL) == 0)
61 #define u64_to_pointer(x) (void *)(u32)((x))
62 #define pointer_to_u64(x) (u64)(u32)((x))
63 #else
64 #define OVERFLOW_ASSERT(x)
65 #define u64_to_pointer(x) (void *)((x))
66 #define pointer_to_u64(x) (u64)((x))
67 #endif
68
69 typedef struct BV (clib_bihash_value)
70 {
71   union
72   {
73     BVT (clib_bihash_kv) kvp[BIHASH_KVP_PER_PAGE];
74     u64 next_free_as_u64;
75   };
76 } BVT (clib_bihash_value);
77
78 #define BIHASH_BUCKET_OFFSET_BITS 36
79
80 typedef struct
81 {
82   union
83   {
84     struct
85     {
86       u64 offset:BIHASH_BUCKET_OFFSET_BITS;
87       u64 lock:1;
88       u64 linear_search:1;
89       u64 log2_pages:8;
90       u64 refcnt:16;
91     };
92     u64 as_u64;
93   };
94 } BVT (clib_bihash_bucket);
95
96 STATIC_ASSERT_SIZEOF (BVT (clib_bihash_bucket), sizeof (u64));
97
98 /* *INDENT-OFF* */
99 typedef CLIB_PACKED (struct {
100   /*
101    * Backing store allocation. Since bihash manages its own
102    * freelists, we simple dole out memory starting from alloc_arena[alloc_arena_next].
103    */
104   u64 alloc_arena_next; /* Next offset from alloc_arena to allocate, definitely NOT a constant */
105   u64 alloc_arena_size; /* Size of the arena */
106   /* Two SVM pointers stored as 8-byte integers */
107   u64 alloc_lock_as_u64;
108   u64 buckets_as_u64;
109   /* freelist list-head arrays/vectors */
110   u64 freelists_as_u64;
111   u32 nbuckets; /* Number of buckets */
112   /* Set when header valid */
113   volatile u32 ready;
114   u64 pad[2];
115 }) BVT (clib_bihash_shared_header);
116 /* *INDENT-ON* */
117
118 STATIC_ASSERT_SIZEOF (BVT (clib_bihash_shared_header), 8 * sizeof (u64));
119
120 typedef
121 BVS (clib_bihash)
122 {
123   BVT (clib_bihash_bucket) * buckets;
124   volatile u32 *alloc_lock;
125
126   BVT (clib_bihash_value) ** working_copies;
127   int *working_copy_lengths;
128   BVT (clib_bihash_bucket) saved_bucket;
129
130   u32 nbuckets;
131   u32 log2_nbuckets;
132   u8 *name;
133
134   u64 *freelists;
135
136 #if BIHASH_32_64_SVM
137   BVT (clib_bihash_shared_header) * sh;
138   int memfd;
139 #else
140   BVT (clib_bihash_shared_header) sh;
141 #endif
142
143   u64 alloc_arena;              /* Base of the allocation arena */
144
145   /**
146     * A custom format function to print the Key and Value of bihash_key instead of default hexdump
147     */
148   format_function_t *fmt_fn;
149
150   /** Optional statistics-gathering callback */
151 #if BIHASH_ENABLE_STATS
152   void (*inc_stats_callback) (BVS (clib_bihash) *, int stat_id, u64 count);
153
154   /** Statistics callback context (e.g. address of stats data structure) */
155   void *inc_stats_context;
156 #endif
157
158 } BVT (clib_bihash);
159
160 #if BIHASH_32_64_SVM
161 #undef alloc_arena_next
162 #undef alloc_arena_size
163 #undef alloc_arena
164 #undef CLIB_BIHASH_READY_MAGIC
165 #define alloc_arena_next(h) (((h)->sh)->alloc_arena_next)
166 #define alloc_arena_size(h) (((h)->sh)->alloc_arena_size)
167 #define alloc_arena(h) ((h)->alloc_arena)
168 #define CLIB_BIHASH_READY_MAGIC 0xFEEDFACE
169 #else
170 #undef alloc_arena_next
171 #undef alloc_arena_size
172 #undef alloc_arena
173 #undef CLIB_BIHASH_READY_MAGIC
174 #define alloc_arena_next(h) ((h)->sh.alloc_arena_next)
175 #define alloc_arena_size(h) ((h)->sh.alloc_arena_size)
176 #define alloc_arena(h) ((h)->alloc_arena)
177 #define CLIB_BIHASH_READY_MAGIC 0
178 #endif
179
180 #ifndef BIHASH_STAT_IDS
181 #define BIHASH_STAT_IDS 1
182
183 #define foreach_bihash_stat                     \
184 _(alloc_add)                                    \
185 _(add)                                          \
186 _(split_add)                                    \
187 _(replace)                                      \
188 _(update)                                       \
189 _(del)                                          \
190 _(del_free)                                     \
191 _(linear)                                       \
192 _(resplit)                                      \
193 _(working_copy_lost)                            \
194 _(splits)                       /* must be last */
195
196 typedef enum
197 {
198 #define _(a) BIHASH_STAT_##a,
199   foreach_bihash_stat
200 #undef _
201     BIHASH_STAT_N_STATS,
202 } BVT (clib_bihash_stat_id);
203 #endif /* BIHASH_STAT_IDS */
204
205 static inline void BV (clib_bihash_increment_stat) (BVT (clib_bihash) * h,
206                                                     int stat_id, u64 count)
207 {
208 #if BIHASH_ENABLE_STATS
209   if (PREDICT_FALSE (h->inc_stats_callback != 0))
210     h->inc_stats_callback (h, stat_id, count);
211 #endif
212 }
213
214 #if BIHASH_ENABLE_STATS
215 static inline void BV (clib_bihash_set_stats_callback)
216   (BVT (clib_bihash) * h, void (*cb) (BVT (clib_bihash) *, int, u64),
217    void *ctx)
218 {
219   h->inc_stats_callback = cb;
220   h->inc_stats_context = ctx;
221 }
222 #endif
223
224
225 static inline void BV (clib_bihash_alloc_lock) (BVT (clib_bihash) * h)
226 {
227   while (__atomic_test_and_set (h->alloc_lock, __ATOMIC_ACQUIRE))
228     CLIB_PAUSE ();
229 }
230
231 static inline void BV (clib_bihash_alloc_unlock) (BVT (clib_bihash) * h)
232 {
233   __atomic_clear (h->alloc_lock, __ATOMIC_RELEASE);
234 }
235
236 static inline void BV (clib_bihash_lock_bucket) (BVT (clib_bihash_bucket) * b)
237 {
238   BVT (clib_bihash_bucket) unlocked_bucket, locked_bucket;
239
240   do
241     {
242       locked_bucket.as_u64 = unlocked_bucket.as_u64 = b->as_u64;
243       unlocked_bucket.lock = 0;
244       locked_bucket.lock = 1;
245       CLIB_PAUSE ();
246     }
247   while (__atomic_compare_exchange_n (&b->as_u64, &unlocked_bucket.as_u64,
248                                       locked_bucket.as_u64, 1 /* weak */ ,
249                                       __ATOMIC_ACQUIRE,
250                                       __ATOMIC_ACQUIRE) == 0);
251 }
252
253 static inline void BV (clib_bihash_unlock_bucket)
254   (BVT (clib_bihash_bucket) * b)
255 {
256   CLIB_MEMORY_BARRIER ();
257   b->lock = 0;
258 }
259
260 static inline void *BV (clib_bihash_get_value) (BVT (clib_bihash) * h,
261                                                 uword offset)
262 {
263   u8 *hp = (u8 *) (uword) alloc_arena (h);
264   u8 *vp = hp + offset;
265
266   return (void *) vp;
267 }
268
269 static inline int BV (clib_bihash_bucket_is_empty)
270   (BVT (clib_bihash_bucket) * b)
271 {
272   /* Note: applied to locked buckets, test offset */
273   return b->offset == 0;
274 }
275
276 static inline uword BV (clib_bihash_get_offset) (BVT (clib_bihash) * h,
277                                                  void *v)
278 {
279   u8 *hp, *vp;
280
281   hp = (u8 *) (uword) alloc_arena (h);
282   vp = (u8 *) v;
283
284   return vp - hp;
285 }
286
287 void BV (clib_bihash_init)
288   (BVT (clib_bihash) * h, char *name, u32 nbuckets, uword memory_size);
289
290 #if BIHASH_32_64_SVM
291 void BV (clib_bihash_master_init_svm)
292   (BVT (clib_bihash) * h, char *name, u32 nbuckets, u64 memory_size);
293 void BV (clib_bihash_slave_init_svm)
294   (BVT (clib_bihash) * h, char *name, int fd);
295 #endif
296
297 void BV (clib_bihash_set_kvp_format_fn) (BVT (clib_bihash) * h,
298                                          format_function_t * fmt_fn);
299
300 void BV (clib_bihash_free) (BVT (clib_bihash) * h);
301
302 int BV (clib_bihash_add_del) (BVT (clib_bihash) * h,
303                               BVT (clib_bihash_kv) * add_v, int is_add);
304 int BV (clib_bihash_add_or_overwrite_stale) (BVT (clib_bihash) * h,
305                                              BVT (clib_bihash_kv) * add_v,
306                                              int (*is_stale_cb) (BVT
307                                                                  (clib_bihash_kv)
308                                                                  *, void *),
309                                              void *arg);
310 int BV (clib_bihash_search) (BVT (clib_bihash) * h,
311                              BVT (clib_bihash_kv) * search_v,
312                              BVT (clib_bihash_kv) * return_v);
313
314 void BV (clib_bihash_foreach_key_value_pair) (BVT (clib_bihash) * h,
315                                               void *callback, void *arg);
316
317 format_function_t BV (format_bihash);
318 format_function_t BV (format_bihash_kvp);
319 format_function_t BV (format_bihash_lru);
320
321 static inline int BV (clib_bihash_search_inline_with_hash)
322   (BVT (clib_bihash) * h, u64 hash, BVT (clib_bihash_kv) * key_result)
323 {
324   u32 bucket_index;
325   BVT (clib_bihash_value) * v;
326   BVT (clib_bihash_bucket) * b;
327   int i, limit;
328
329   bucket_index = hash & (h->nbuckets - 1);
330   b = &h->buckets[bucket_index];
331
332   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
333     return -1;
334
335   if (PREDICT_FALSE (b->lock))
336     {
337       volatile BVT (clib_bihash_bucket) * bv = b;
338       while (bv->lock)
339         CLIB_PAUSE ();
340     }
341
342   hash >>= h->log2_nbuckets;
343
344   v = BV (clib_bihash_get_value) (h, b->offset);
345
346   /* If the bucket has unresolvable collisions, use linear search */
347   limit = BIHASH_KVP_PER_PAGE;
348   v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
349   if (PREDICT_FALSE (b->linear_search))
350     limit <<= b->log2_pages;
351
352   for (i = 0; i < limit; i++)
353     {
354       if (BV (clib_bihash_key_compare) (v->kvp[i].key, key_result->key))
355         {
356           *key_result = v->kvp[i];
357           return 0;
358         }
359     }
360   return -1;
361 }
362
363 static inline int BV (clib_bihash_search_inline)
364   (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * key_result)
365 {
366   u64 hash;
367
368   hash = BV (clib_bihash_hash) (key_result);
369
370   return BV (clib_bihash_search_inline_with_hash) (h, hash, key_result);
371 }
372
373 static inline void BV (clib_bihash_prefetch_bucket)
374   (BVT (clib_bihash) * h, u64 hash)
375 {
376   u32 bucket_index;
377   BVT (clib_bihash_bucket) * b;
378
379   bucket_index = hash & (h->nbuckets - 1);
380   b = &h->buckets[bucket_index];
381
382   CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, READ);
383 }
384
385 static inline void BV (clib_bihash_prefetch_data)
386   (BVT (clib_bihash) * h, u64 hash)
387 {
388   u32 bucket_index;
389   BVT (clib_bihash_value) * v;
390   BVT (clib_bihash_bucket) * b;
391
392   bucket_index = hash & (h->nbuckets - 1);
393   b = &h->buckets[bucket_index];
394
395   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
396     return;
397
398   hash >>= h->log2_nbuckets;
399   v = BV (clib_bihash_get_value) (h, b->offset);
400
401   v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
402
403   CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, READ);
404 }
405
406 static inline int BV (clib_bihash_search_inline_2_with_hash)
407   (BVT (clib_bihash) * h,
408    u64 hash, BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
409 {
410   u32 bucket_index;
411   BVT (clib_bihash_value) * v;
412   BVT (clib_bihash_bucket) * b;
413   int i, limit;
414
415   ASSERT (valuep);
416
417   bucket_index = hash & (h->nbuckets - 1);
418   b = &h->buckets[bucket_index];
419
420   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
421     return -1;
422
423   if (PREDICT_FALSE (b->lock))
424     {
425       volatile BVT (clib_bihash_bucket) * bv = b;
426       while (bv->lock)
427         CLIB_PAUSE ();
428     }
429
430   hash >>= h->log2_nbuckets;
431   v = BV (clib_bihash_get_value) (h, b->offset);
432
433   /* If the bucket has unresolvable collisions, use linear search */
434   limit = BIHASH_KVP_PER_PAGE;
435   v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
436   if (PREDICT_FALSE (b->linear_search))
437     limit <<= b->log2_pages;
438
439   for (i = 0; i < limit; i++)
440     {
441       if (BV (clib_bihash_key_compare) (v->kvp[i].key, search_key->key))
442         {
443           *valuep = v->kvp[i];
444           return 0;
445         }
446     }
447   return -1;
448 }
449
450 static inline int BV (clib_bihash_search_inline_2)
451   (BVT (clib_bihash) * h,
452    BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
453 {
454   u64 hash;
455
456   hash = BV (clib_bihash_hash) (search_key);
457
458   return BV (clib_bihash_search_inline_2_with_hash) (h, hash, search_key,
459                                                      valuep);
460 }
461
462
463 #endif /* __included_bihash_template_h__ */
464
465 /** @endcond */
466
467 /*
468  * fd.io coding-style-patch-verification: ON
469  *
470  * Local Variables:
471  * eval: (c-set-style "gnu")
472  * End:
473  */