FD.io VPP  v18.11-rc0-18-g2a3fb1a
Vector Packet Processing
bihash_template.h
Go to the documentation of this file.
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 #if BIHASH_KVP_CACHE_SIZE > 5
53 #error Requested KVP cache LRU data exceeds 16 bits
54 #endif
55 
56 typedef struct
57 {
58  union
59  {
60  struct
61  {
62  u64 offset:37;
63  u64 linear_search:1;
64  u64 log2_pages:8;
65  i64 refcnt:16;
66  };
67  u64 as_u64;
68  };
69 #if BIHASH_KVP_CACHE_SIZE > 0
70  u16 cache_lru;
71  BVT (clib_bihash_kv) cache[BIHASH_KVP_CACHE_SIZE];
72 #endif
73 } BVT (clib_bihash_bucket);
74 
75 #if BIHASH_KVP_CACHE_SIZE == 0
76 STATIC_ASSERT_SIZEOF (BVT (clib_bihash_bucket), sizeof (u64));
77 #endif
78 
79 typedef struct
80 {
81  BVT (clib_bihash_value) * values;
82  BVT (clib_bihash_bucket) * buckets;
83  volatile u32 *writer_lock;
84 
85  BVT (clib_bihash_value) ** working_copies;
86  int *working_copy_lengths;
87  BVT (clib_bihash_bucket) saved_bucket;
88 
89  u32 nbuckets;
90  u32 log2_nbuckets;
91  u8 *name;
92 
93  u64 cache_hits;
94  u64 cache_misses;
95 
96  BVT (clib_bihash_value) ** freelists;
97 
98  /*
99  * Backing store allocation. Since bihash manages its own
100  * freelists, we simple dole out memory at alloc_arena_next.
101  */
102  uword alloc_arena;
103  uword alloc_arena_next;
104  uword alloc_arena_size;
105 
106  /**
107  * A custom format function to print the Key and Value of bihash_key instead of default hexdump
108  */
109  format_function_t *fmt_fn;
110 
111 } BVT (clib_bihash);
112 
113 
114 static inline void
115 BV (clib_bihash_update_lru) (BVT (clib_bihash_bucket) * b, u8 slot)
116 {
117 #if BIHASH_KVP_CACHE_SIZE > 1
118  u16 value, tmp, mask;
119  u8 found_lru_pos;
120  u16 save_hi;
121 
122  ASSERT (slot < BIHASH_KVP_CACHE_SIZE);
123 
124  /* First, find the slot in cache_lru */
125  mask = slot;
126  if (BIHASH_KVP_CACHE_SIZE > 1)
127  mask |= slot << 3;
128  if (BIHASH_KVP_CACHE_SIZE > 2)
129  mask |= slot << 6;
130  if (BIHASH_KVP_CACHE_SIZE > 3)
131  mask |= slot << 9;
132  if (BIHASH_KVP_CACHE_SIZE > 4)
133  mask |= slot << 12;
134 
135  value = b->cache_lru;
136  tmp = value ^ mask;
137 
138  /* Already the most-recently used? */
139  if ((tmp & 7) == 0)
140  return;
141 
142  found_lru_pos = ((tmp & (7 << 3)) == 0) ? 1 : 0;
143  if (BIHASH_KVP_CACHE_SIZE > 2)
144  found_lru_pos = ((tmp & (7 << 6)) == 0) ? 2 : found_lru_pos;
145  if (BIHASH_KVP_CACHE_SIZE > 3)
146  found_lru_pos = ((tmp & (7 << 9)) == 0) ? 3 : found_lru_pos;
147  if (BIHASH_KVP_CACHE_SIZE > 4)
148  found_lru_pos = ((tmp & (7 << 12)) == 0) ? 4 : found_lru_pos;
149 
150  ASSERT (found_lru_pos);
151 
152  /* create a mask to kill bits in or above slot */
153  mask = 0xFFFF << found_lru_pos;
154  mask <<= found_lru_pos;
155  mask <<= found_lru_pos;
156  mask ^= 0xFFFF;
157  tmp = value & mask;
158 
159  /* Save bits above slot */
160  mask ^= 0xFFFF;
161  mask <<= 3;
162  save_hi = value & mask;
163 
164  value = save_hi | (tmp << 3) | slot;
165 
166  b->cache_lru = value;
167 #endif
168 }
169 
170 void
171 BV (clib_bihash_update_lru_not_inline) (BVT (clib_bihash_bucket) * b,
172  u8 slot);
173 
174 static inline u8 BV (clib_bihash_get_lru) (BVT (clib_bihash_bucket) * b)
175 {
176 #if BIHASH_KVP_CACHE_SIZE > 0
177  return (b->cache_lru >> (3 * (BIHASH_KVP_CACHE_SIZE - 1))) & 7;
178 #else
179  return 0;
180 #endif
181 }
182 
183 static inline void BV (clib_bihash_reset_cache) (BVT (clib_bihash_bucket) * b)
184 {
185 #if BIHASH_KVP_CACHE_SIZE > 0
186  u16 initial_lru_value;
187 
188  memset (b->cache, 0xff, sizeof (b->cache));
189 
190  /*
191  * We'll want the cache to be loaded from slot 0 -> slot N, so
192  * the initial LRU order is reverse index order.
193  */
194  if (BIHASH_KVP_CACHE_SIZE == 1)
195  initial_lru_value = 0;
196  else if (BIHASH_KVP_CACHE_SIZE == 2)
197  initial_lru_value = (0 << 3) | (1 << 0);
198  else if (BIHASH_KVP_CACHE_SIZE == 3)
199  initial_lru_value = (0 << 6) | (1 << 3) | (2 << 0);
200  else if (BIHASH_KVP_CACHE_SIZE == 4)
201  initial_lru_value = (0 << 9) | (1 << 6) | (2 << 3) | (3 << 0);
202  else if (BIHASH_KVP_CACHE_SIZE == 5)
203  initial_lru_value = (0 << 12) | (1 << 9) | (2 << 6) | (3 << 3) | (4 << 0);
204 
205  b->cache_lru = initial_lru_value;
206 #endif
207 }
208 
209 static inline int BV (clib_bihash_lock_bucket) (BVT (clib_bihash_bucket) * b)
210 {
211 #if BIHASH_KVP_CACHE_SIZE > 0
212  u16 cache_lru_bit;
213  u16 rv;
214 
215  cache_lru_bit = 1 << 15;
216 
217  rv = __sync_fetch_and_or (&b->cache_lru, cache_lru_bit);
218  /* Was already locked? */
219  if (rv & (1 << 15))
220  return 0;
221 #endif
222  return 1;
223 }
224 
225 static inline void BV (clib_bihash_unlock_bucket)
226  (BVT (clib_bihash_bucket) * b)
227 {
228 #if BIHASH_KVP_CACHE_SIZE > 0
229  u16 cache_lru;
230 
231  cache_lru = b->cache_lru & ~(1 << 15);
232  b->cache_lru = cache_lru;
233 #endif
234 }
235 
236 static inline void *BV (clib_bihash_get_value) (BVT (clib_bihash) * h,
237  uword offset)
238 {
239  u8 *hp = (u8 *) h->alloc_arena;
240  u8 *vp = hp + offset;
241 
242  return (void *) vp;
243 }
244 
245 static inline int BV (clib_bihash_bucket_is_empty)
246  (BVT (clib_bihash_bucket) * b)
247 {
248  return b->as_u64 == 0;
249 }
250 
251 static inline uword BV (clib_bihash_get_offset) (BVT (clib_bihash) * h,
252  void *v)
253 {
254  u8 *hp, *vp;
255 
256  hp = (u8 *) h->alloc_arena;
257  vp = (u8 *) v;
258 
259  return vp - hp;
260 }
261 
262 void BV (clib_bihash_init)
263  (BVT (clib_bihash) * h, char *name, u32 nbuckets, uword memory_size);
264 
265 void BV (clib_bihash_set_kvp_format_fn) (BVT (clib_bihash) * h,
266  format_function_t * fmt_fn);
267 
268 void BV (clib_bihash_free) (BVT (clib_bihash) * h);
269 
270 int BV (clib_bihash_add_del) (BVT (clib_bihash) * h,
271  BVT (clib_bihash_kv) * add_v, int is_add);
272 int BV (clib_bihash_search) (BVT (clib_bihash) * h,
273  BVT (clib_bihash_kv) * search_v,
274  BVT (clib_bihash_kv) * return_v);
275 
276 void BV (clib_bihash_foreach_key_value_pair) (BVT (clib_bihash) * h,
277  void *callback, void *arg);
278 
279 format_function_t BV (format_bihash);
280 format_function_t BV (format_bihash_kvp);
281 format_function_t BV (format_bihash_lru);
282 
283 static inline int BV (clib_bihash_search_inline_with_hash)
284  (BVT (clib_bihash) * h, u64 hash, BVT (clib_bihash_kv) * key_result)
285 {
286  u32 bucket_index;
287  BVT (clib_bihash_value) * v;
288  BVT (clib_bihash_bucket) * b;
289 #if BIHASH_KVP_CACHE_SIZE > 0
290  BVT (clib_bihash_kv) * kvp;
291 #endif
292  int i, limit;
293 
294  bucket_index = hash & (h->nbuckets - 1);
295  b = &h->buckets[bucket_index];
296 
297  if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
298  return -1;
299 
300 #if BIHASH_KVP_CACHE_SIZE > 0
301  /* Check the cache, if not currently locked */
302  if (PREDICT_TRUE ((b->cache_lru & (1 << 15)) == 0))
303  {
304  limit = BIHASH_KVP_CACHE_SIZE;
305  kvp = b->cache;
306  for (i = 0; i < limit; i++)
307  {
308  if (BV (clib_bihash_key_compare) (kvp[i].key, key_result->key))
309  {
310  *key_result = kvp[i];
311  h->cache_hits++;
312  return 0;
313  }
314  }
315  }
316 #endif
317 
318  hash >>= h->log2_nbuckets;
319 
320  v = BV (clib_bihash_get_value) (h, b->offset);
321 
322  /* If the bucket has unresolvable collisions, use linear search */
323  limit = BIHASH_KVP_PER_PAGE;
324  v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
325  if (PREDICT_FALSE (b->linear_search))
326  limit <<= b->log2_pages;
327 
328  for (i = 0; i < limit; i++)
329  {
330  if (BV (clib_bihash_key_compare) (v->kvp[i].key, key_result->key))
331  {
332  *key_result = v->kvp[i];
333 
334 #if BIHASH_KVP_CACHE_SIZE > 0
335  u8 cache_slot;
336  /* Try to lock the bucket */
337  if (BV (clib_bihash_lock_bucket) (b))
338  {
339  cache_slot = BV (clib_bihash_get_lru) (b);
340  b->cache[cache_slot] = v->kvp[i];
341  BV (clib_bihash_update_lru) (b, cache_slot);
342 
343  /* Unlock the bucket */
344  BV (clib_bihash_unlock_bucket) (b);
345  h->cache_misses++;
346  }
347 #endif
348  return 0;
349  }
350  }
351  return -1;
352 }
353 
354 static inline int BV (clib_bihash_search_inline)
355  (BVT (clib_bihash) * h, BVT (clib_bihash_kv) * key_result)
356 {
357  u64 hash;
358 
359  hash = BV (clib_bihash_hash) (key_result);
360 
361  return BV (clib_bihash_search_inline_with_hash) (h, hash, key_result);
362 }
363 
364 static inline void BV (clib_bihash_prefetch_bucket)
365  (BVT (clib_bihash) * h, u64 hash)
366 {
367  u32 bucket_index;
368  BVT (clib_bihash_bucket) * b;
369 
370  bucket_index = hash & (h->nbuckets - 1);
371  b = &h->buckets[bucket_index];
372 
374 }
375 
376 static inline void BV (clib_bihash_prefetch_data)
377  (BVT (clib_bihash) * h, u64 hash)
378 {
379  u32 bucket_index;
380  BVT (clib_bihash_value) * v;
381  BVT (clib_bihash_bucket) * b;
382 
383  bucket_index = hash & (h->nbuckets - 1);
384  b = &h->buckets[bucket_index];
385 
386  if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
387  return;
388 
389  hash >>= h->log2_nbuckets;
390  v = BV (clib_bihash_get_value) (h, b->offset);
391 
392  v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
393 
395 }
396 
397 static inline int BV (clib_bihash_search_inline_2_with_hash)
398  (BVT (clib_bihash) * h,
399  u64 hash, BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
400 {
401  u32 bucket_index;
402  BVT (clib_bihash_value) * v;
403  BVT (clib_bihash_bucket) * b;
404 #if BIHASH_KVP_CACHE_SIZE > 0
405  BVT (clib_bihash_kv) * kvp;
406 #endif
407  int i, limit;
408 
409  ASSERT (valuep);
410 
411  bucket_index = hash & (h->nbuckets - 1);
412  b = &h->buckets[bucket_index];
413 
414  if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
415  return -1;
416 
417  /* Check the cache, if currently unlocked */
418 #if BIHASH_KVP_CACHE_SIZE > 0
419  if (PREDICT_TRUE ((b->cache_lru & (1 << 15)) == 0))
420  {
421  limit = BIHASH_KVP_CACHE_SIZE;
422  kvp = b->cache;
423  for (i = 0; i < limit; i++)
424  {
425  if (BV (clib_bihash_key_compare) (kvp[i].key, search_key->key))
426  {
427  *valuep = kvp[i];
428  h->cache_hits++;
429  return 0;
430  }
431  }
432  }
433 #endif
434 
435  hash >>= h->log2_nbuckets;
436  v = BV (clib_bihash_get_value) (h, b->offset);
437 
438  /* If the bucket has unresolvable collisions, use linear search */
439  limit = BIHASH_KVP_PER_PAGE;
440  v += (b->linear_search == 0) ? hash & ((1 << b->log2_pages) - 1) : 0;
441  if (PREDICT_FALSE (b->linear_search))
442  limit <<= b->log2_pages;
443 
444  for (i = 0; i < limit; i++)
445  {
446  if (BV (clib_bihash_key_compare) (v->kvp[i].key, search_key->key))
447  {
448  *valuep = v->kvp[i];
449 
450 #if BIHASH_KVP_CACHE_SIZE > 0
451  u8 cache_slot;
452 
453  /* Try to lock the bucket */
454  if (BV (clib_bihash_lock_bucket) (b))
455  {
456  cache_slot = BV (clib_bihash_get_lru) (b);
457  b->cache[cache_slot] = v->kvp[i];
458  BV (clib_bihash_update_lru) (b, cache_slot);
459 
460  /* Reenable the cache */
461  BV (clib_bihash_unlock_bucket) (b);
462  h->cache_misses++;
463  }
464 #endif
465  return 0;
466  }
467  }
468  return -1;
469 }
470 
471 static inline int BV (clib_bihash_search_inline_2)
472  (BVT (clib_bihash) * h,
473  BVT (clib_bihash_kv) * search_key, BVT (clib_bihash_kv) * valuep)
474 {
475  u64 hash;
476 
477  hash = BV (clib_bihash_hash) (search_key);
478 
479  return BV (clib_bihash_search_inline_2_with_hash) (h, hash, search_key,
480  valuep);
481 }
482 
483 
484 #endif /* __included_bihash_template_h__ */
485 
486 /** @endcond */
487 
488 /*
489  * fd.io coding-style-patch-verification: ON
490  *
491  * Local Variables:
492  * eval: (c-set-style "gnu")
493  * End:
494  */
int clib_bihash_search_inline_with_hash(clib_bihash *h, u64 hash, clib_bihash_kv *in_out_kv)
Search a bi-hash table, use supplied hash code.
#define BIHASH_KVP_PER_PAGE
Definition: bihash_16_8.h:20
u64 as_u64
Definition: bihash_doc.h:63
void clib_bihash_free(clib_bihash *h)
Destroy a bounded index extensible hash table.
#define PREDICT_TRUE(x)
Definition: clib.h:106
unsigned long u64
Definition: types.h:89
Fixed length block allocator.
for(i=1;i<=collision_buckets;i++)
int i
u8 *( format_function_t)(u8 *s, va_list *args)
Definition: format.h:48
unsigned char u8
Definition: types.h:56
int clib_bihash_add_del(clib_bihash *h, clib_bihash_kv *add_v, int is_add)
Add or delete a (key,value) pair from a bi-hash table.
static BVT(clib_bihash)
Definition: adj_nbr.c:26
unsigned int u32
Definition: types.h:88
static uword clib_bihash_get_offset(clib_bihash *h, void *v)
Get clib mheap offset given a pointer.
#define v
Definition: acl.c:491
unsigned short u16
Definition: types.h:57
u64 memory_size
Definition: vhost_user.h:110
signed long i64
Definition: types.h:82
#define PREDICT_FALSE(x)
Definition: clib.h:105
int clib_bihash_search_inline(clib_bihash *h, clib_bihash_kv *in_out_kv)
Search a bi-hash table.
void clib_bihash_init(clib_bihash *h, char *name, u32 nbuckets, uword memory_size)
initialize a bounded index extensible hash table
void clib_bihash_prefetch_bucket(clib_bihash *h, u64 hash)
Prefetch a bi-hash bucket given a hash code.
void clib_bihash_foreach_key_value_pair(clib_bihash *h, void *callback, void *arg)
Visit active (key,value) pairs in a bi-hash table.
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:77
void clib_bihash_prefetch_data(clib_bihash *h, u64 hash)
Prefetch bi-hash (key,value) data given a hash code.
#define ASSERT(truth)
int clib_bihash_search_inline_2(clib_bihash *h, clib_bihash_kv *search_key, clib_bihash_kv *valuep)
Search a bi-hash table.
u8 log2_pages
Definition: bihash_doc.h:62
template key/value backing page structure
Definition: bihash_doc.h:44
u64 uword
Definition: types.h:112
struct clib_bihash_value offset
template key/value backing page structure
static void * clib_bihash_get_value(clib_bihash *h, uword offset)
Get pointer to value page given its clib mheap offset.
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:62
#define STATIC_ASSERT_SIZEOF(d, s)
#define BIHASH_KVP_CACHE_SIZE
Definition: bihash_16_8.h:21