6303992bc577ec9e4c9d29ed5bdc48c651d67b3d
[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
31 #ifndef BIHASH_TYPE
32 #error BIHASH_TYPE not defined
33 #endif
34
35 #define _bv(a,b) a##b
36 #define __bv(a,b) _bv(a,b)
37 #define BV(a) __bv(a,BIHASH_TYPE)
38
39 #define _bvt(a,b) a##b##_t
40 #define __bvt(a,b) _bvt(a,b)
41 #define BVT(a) __bvt(a,BIHASH_TYPE)
42
43 typedef struct BV (clib_bihash_value)
44 {
45   union
46   {
47     BVT (clib_bihash_kv) kvp[BIHASH_KVP_PER_PAGE];
48     struct BV (clib_bihash_value) * next_free;
49   };
50 } BVT (clib_bihash_value);
51
52 #define BIHASH_BUCKET_OFFSET_BITS 36
53
54 typedef struct
55 {
56   union
57   {
58     struct
59     {
60       u64 offset:BIHASH_BUCKET_OFFSET_BITS;
61       u64 lock:1;
62       u64 linear_search:1;
63       u64 log2_pages:8;
64       i64 refcnt:16;
65     };
66     u64 as_u64;
67   };
68 } BVT (clib_bihash_bucket);
69
70 STATIC_ASSERT_SIZEOF (BVT (clib_bihash_bucket), sizeof (u64));
71
72 typedef struct
73 {
74   BVT (clib_bihash_value) * values;
75   BVT (clib_bihash_bucket) * buckets;
76   volatile u32 *alloc_lock;
77
78     BVT (clib_bihash_value) ** working_copies;
79   int *working_copy_lengths;
80     BVT (clib_bihash_bucket) saved_bucket;
81
82   u32 nbuckets;
83   u32 log2_nbuckets;
84   u8 *name;
85
86   u64 cache_hits;
87   u64 cache_misses;
88
89     BVT (clib_bihash_value) ** freelists;
90
91   /*
92    * Backing store allocation. Since bihash manages its own
93    * freelists, we simple dole out memory at alloc_arena_next.
94    */
95   uword alloc_arena;
96   uword alloc_arena_next;
97   uword alloc_arena_size;
98
99   /**
100     * A custom format function to print the Key and Value of bihash_key instead of default hexdump
101     */
102   format_function_t *fmt_fn;
103
104 } BVT (clib_bihash);
105
106 static inline void BV (clib_bihash_alloc_lock) (BVT (clib_bihash) * h)
107 {
108   while (__atomic_test_and_set (h->alloc_lock, __ATOMIC_ACQUIRE))
109     ;
110 }
111
112 static inline void BV (clib_bihash_alloc_unlock) (BVT (clib_bihash) * h)
113 {
114   __atomic_clear (h->alloc_lock, __ATOMIC_RELEASE);
115 }
116
117 static inline void BV (clib_bihash_lock_bucket) (BVT (clib_bihash_bucket) * b)
118 {
119   BVT (clib_bihash_bucket) unlocked_bucket, locked_bucket;
120
121   do
122     {
123       locked_bucket.as_u64 = unlocked_bucket.as_u64 = b->as_u64;
124       unlocked_bucket.lock = 0;
125       locked_bucket.lock = 1;
126     }
127   while (__atomic_compare_exchange_n (&b->as_u64, &unlocked_bucket.as_u64,
128                                       locked_bucket.as_u64, 1 /* weak */ ,
129                                       __ATOMIC_ACQUIRE,
130                                       __ATOMIC_ACQUIRE) == 0);
131 }
132
133 static inline void BV (clib_bihash_unlock_bucket)
134   (BVT (clib_bihash_bucket) * b)
135 {
136   CLIB_MEMORY_BARRIER ();
137   b->lock = 0;
138 }
139
140 static inline void *BV (clib_bihash_get_value) (BVT (clib_bihash) * h,
141                                                 uword offset)
142 {
143   u8 *hp = (u8 *) h->alloc_arena;
144   u8 *vp = hp + offset;
145
146   return (void *) vp;
147 }
148
149 static inline int BV (clib_bihash_bucket_is_empty)
150   (BVT (clib_bihash_bucket) * b)
151 {
152   /* Note: applied to locked buckets, test offset */
153   return b->offset == 0;
154 }
155
156 static inline uword BV (clib_bihash_get_offset) (BVT (clib_bihash) * h,
157                                                  void *v)
158 {
159   u8 *hp, *vp;
160
161   hp = (u8 *) h->alloc_arena;
162   vp = (u8 *) v;
163
164   return vp - hp;
165 }
166
167 void BV (clib_bihash_init)
168   (BVT (clib_bihash) * h, char *name, u32 nbuckets, uword memory_size);
169
170 void BV (clib_bihash_set_kvp_format_fn) (BVT (clib_bihash) * h,
171                                          format_function_t * fmt_fn);
172
173 void BV (clib_bihash_free) (BVT (clib_bihash) * h);
174
175 int BV (clib_bihash_add_del) (BVT (clib_bihash) * h,
176                               BVT (clib_bihash_kv) * add_v, int is_add);
177 int BV (clib_bihash_search) (BVT (clib_bihash) * h,
178                              BVT (clib_bihash_kv) * search_v,
179                              BVT (clib_bihash_kv) * return_v);
180
181 void BV (clib_bihash_foreach_key_value_pair) (BVT (clib_bihash) * h,
182                                               void *callback, void *arg);
183
184 format_function_t BV (format_bihash);
185 format_function_t BV (format_bihash_kvp);
186 format_function_t BV (format_bihash_lru);
187
188 static inline int BV (clib_bihash_search_inline_with_hash)
189   (BVT (clib_bihash) * h, u64 hash, BVT (clib_bihash_kv) * key_result)
190 {
191   u32 bucket_index;
192   BVT (clib_bihash_value) * v;
193   BVT (clib_bihash_bucket) * b;
194   int i, limit;
195
196   bucket_index = hash & (h->nbuckets - 1);
197   b = &h->buckets[bucket_index];
198
199   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
200     return -1;
201
202   if (PREDICT_FALSE (b->lock))
203     {
204       volatile BVT (clib_bihash_bucket) * bv = b;
205       while (bv->lock)
206         ;
207     }
208
209   hash >>= h->log2_nbuckets;
210
211   v = BV (clib_bihash_get_value) (h, b->offset);
212
213   /* If the bucket has unresolvable collisions, use linear search */
214   limit = BIHASH_KVP_PER_PAGE;
215   v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
216   if (PREDICT_FALSE (b->linear_search))
217     limit <<= b->log2_pages;
218
219   for (i = 0; i < limit; i++)
220     {
221       if (BV (clib_bihash_key_compare) (v->kvp[i].key, key_result->key))
222         {
223           *key_result = v->kvp[i];
224           return 0;
225         }
226     }
227   return -1;
228 }
229
230 static inline int BV (clib_bihash_search_inline)
231   (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * key_result)
232 {
233   u64 hash;
234
235   hash = BV (clib_bihash_hash) (key_result);
236
237   return BV (clib_bihash_search_inline_with_hash) (h, hash, key_result);
238 }
239
240 static inline void BV (clib_bihash_prefetch_bucket)
241   (BVT (clib_bihash) * h, u64 hash)
242 {
243   u32 bucket_index;
244   BVT (clib_bihash_bucket) * b;
245
246   bucket_index = hash & (h->nbuckets - 1);
247   b = &h->buckets[bucket_index];
248
249   CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, READ);
250 }
251
252 static inline void BV (clib_bihash_prefetch_data)
253   (BVT (clib_bihash) * h, u64 hash)
254 {
255   u32 bucket_index;
256   BVT (clib_bihash_value) * v;
257   BVT (clib_bihash_bucket) * b;
258
259   bucket_index = hash & (h->nbuckets - 1);
260   b = &h->buckets[bucket_index];
261
262   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
263     return;
264
265   hash >>= h->log2_nbuckets;
266   v = BV (clib_bihash_get_value) (h, b->offset);
267
268   v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
269
270   CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, READ);
271 }
272
273 static inline int BV (clib_bihash_search_inline_2_with_hash)
274   (BVT (clib_bihash) * h,
275    u64 hash, BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
276 {
277   u32 bucket_index;
278   BVT (clib_bihash_value) * v;
279   BVT (clib_bihash_bucket) * b;
280   int i, limit;
281
282   ASSERT (valuep);
283
284   bucket_index = hash & (h->nbuckets - 1);
285   b = &h->buckets[bucket_index];
286
287   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
288     return -1;
289
290   if (PREDICT_FALSE (b->lock))
291     {
292       volatile BVT (clib_bihash_bucket) * bv = b;
293       while (bv->lock)
294         ;
295     }
296
297   hash >>= h->log2_nbuckets;
298   v = BV (clib_bihash_get_value) (h, b->offset);
299
300   /* If the bucket has unresolvable collisions, use linear search */
301   limit = BIHASH_KVP_PER_PAGE;
302   v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
303   if (PREDICT_FALSE (b->linear_search))
304     limit <<= b->log2_pages;
305
306   for (i = 0; i < limit; i++)
307     {
308       if (BV (clib_bihash_key_compare) (v->kvp[i].key, search_key->key))
309         {
310           *valuep = v->kvp[i];
311           return 0;
312         }
313     }
314   return -1;
315 }
316
317 static inline int BV (clib_bihash_search_inline_2)
318   (BVT (clib_bihash) * h,
319    BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
320 {
321   u64 hash;
322
323   hash = BV (clib_bihash_hash) (search_key);
324
325   return BV (clib_bihash_search_inline_2_with_hash) (h, hash, search_key,
326                                                      valuep);
327 }
328
329
330 #endif /* __included_bihash_template_h__ */
331
332 /** @endcond */
333
334 /*
335  * fd.io coding-style-patch-verification: ON
336  *
337  * Local Variables:
338  * eval: (c-set-style "gnu")
339  * End:
340  */