1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2017 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <rte_errno.h>
35 #include <rte_malloc.h>
36 #include <rte_prefetch.h>
37 #include <rte_random.h>
38 #include <rte_log.h>
39 
40 #include "rte_member.h"
41 #include "rte_member_ht.h"
42 
43 #if defined(RTE_ARCH_X86)
44 #include "rte_member_x86.h"
45 #endif
46 
47 /* Search bucket for entry with tmp_sig and update set_id */
48 static inline int
49 update_entry_search(uint32_t bucket_id, member_sig_t tmp_sig,
50 		struct member_ht_bucket *buckets,
51 		member_set_t set_id)
52 {
53 	uint32_t i;
54 
55 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
56 		if (buckets[bucket_id].sigs[i] == tmp_sig) {
57 			buckets[bucket_id].sets[i] = set_id;
58 			return 1;
59 		}
60 	}
61 	return 0;
62 }
63 
64 static inline int
65 search_bucket_single(uint32_t bucket_id, member_sig_t tmp_sig,
66 		struct member_ht_bucket *buckets,
67 		member_set_t *set_id)
68 {
69 	uint32_t iter;
70 
71 	for (iter = 0; iter < RTE_MEMBER_BUCKET_ENTRIES; iter++) {
72 		if (tmp_sig == buckets[bucket_id].sigs[iter] &&
73 				buckets[bucket_id].sets[iter] !=
74 				RTE_MEMBER_NO_MATCH) {
75 			*set_id = buckets[bucket_id].sets[iter];
76 			return 1;
77 		}
78 	}
79 	return 0;
80 }
81 
82 static inline void
83 search_bucket_multi(uint32_t bucket_id, member_sig_t tmp_sig,
84 		struct member_ht_bucket *buckets,
85 		uint32_t *counter,
86 		uint32_t matches_per_key,
87 		member_set_t *set_id)
88 {
89 	uint32_t iter;
90 
91 	for (iter = 0; iter < RTE_MEMBER_BUCKET_ENTRIES; iter++) {
92 		if (tmp_sig == buckets[bucket_id].sigs[iter] &&
93 				buckets[bucket_id].sets[iter] !=
94 				RTE_MEMBER_NO_MATCH) {
95 			set_id[*counter] = buckets[bucket_id].sets[iter];
96 			(*counter)++;
97 			if (*counter >= matches_per_key)
98 				return;
99 		}
100 	}
101 }
102 
103 int
104 rte_member_create_ht(struct rte_member_setsum *ss,
105 		const struct rte_member_parameters *params)
106 {
107 	uint32_t i, j;
108 	uint32_t size_bucket_t;
109 	uint32_t num_entries = rte_align32pow2(params->num_keys);
110 
111 	if ((num_entries > RTE_MEMBER_ENTRIES_MAX) ||
112 			!rte_is_power_of_2(RTE_MEMBER_BUCKET_ENTRIES) ||
113 			num_entries < RTE_MEMBER_BUCKET_ENTRIES) {
114 		rte_errno = EINVAL;
115 		RTE_MEMBER_LOG(ERR,
116 			"Membership HT create with invalid parameters\n");
117 		return -EINVAL;
118 	}
119 
120 	uint32_t num_buckets = num_entries / RTE_MEMBER_BUCKET_ENTRIES;
121 
122 	size_bucket_t = sizeof(struct member_ht_bucket);
123 
124 	struct member_ht_bucket *buckets = rte_zmalloc_socket(NULL,
125 			num_buckets * size_bucket_t,
126 			RTE_CACHE_LINE_SIZE, ss->socket_id);
127 
128 	if (buckets == NULL) {
129 		RTE_MEMBER_LOG(ERR, "memory allocation failed for HT "
130 						"setsummary\n");
131 		return -ENOMEM;
132 	}
133 
134 	ss->table = buckets;
135 	ss->bucket_cnt = num_buckets;
136 	ss->bucket_mask = num_buckets - 1;
137 	ss->cache = params->is_cache;
138 
139 	for (i = 0; i < num_buckets; i++) {
140 		for (j = 0; j < RTE_MEMBER_BUCKET_ENTRIES; j++)
141 			buckets[i].sets[j] = RTE_MEMBER_NO_MATCH;
142 	}
143 #if defined(RTE_ARCH_X86)
144 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2) &&
145 			RTE_MEMBER_BUCKET_ENTRIES == 16)
146 		ss->sig_cmp_fn = RTE_MEMBER_COMPARE_AVX2;
147 	else
148 #endif
149 		ss->sig_cmp_fn = RTE_MEMBER_COMPARE_SCALAR;
150 
151 	RTE_MEMBER_LOG(DEBUG, "Hash table based filter created, "
152 			"the table has %u entries, %u buckets\n",
153 			num_entries, num_buckets);
154 	return 0;
155 }
156 
157 static inline void
158 get_buckets_index(const struct rte_member_setsum *ss, const void *key,
159 		uint32_t *prim_bkt, uint32_t *sec_bkt, member_sig_t *sig)
160 {
161 	uint32_t first_hash = MEMBER_HASH_FUNC(key, ss->key_len,
162 						ss->prim_hash_seed);
163 	uint32_t sec_hash = MEMBER_HASH_FUNC(&first_hash, sizeof(uint32_t),
164 						ss->sec_hash_seed);
165 	/*
166 	 * We use the first hash value for the signature, and the second hash
167 	 * value to derive the primary and secondary bucket locations.
168 	 *
169 	 * For non-cache mode, we use the lower bits for the primary bucket
170 	 * location. Then we xor primary bucket location and the signature
171 	 * to get the secondary bucket location. This is called "partial-key
172 	 * cuckoo hashing" proposed by B. Fan, et al's paper
173 	 * "Cuckoo Filter: Practically Better Than Bloom". The benefit to use
174 	 * xor is that one could derive the alternative bucket location
175 	 * by only using the current bucket location and the signature. This is
176 	 * generally required by non-cache mode's eviction and deletion
177 	 * process without the need to store alternative hash value nor the full
178 	 * key.
179 	 *
180 	 * For cache mode, we use the lower bits for the primary bucket
181 	 * location and the higher bits for the secondary bucket location. In
182 	 * cache mode, keys are simply overwritten if bucket is full. We do not
183 	 * use xor since lower/higher bits are more independent hash values thus
184 	 * should provide slightly better table load.
185 	 */
186 	*sig = first_hash;
187 	if (ss->cache) {
188 		*prim_bkt = sec_hash & ss->bucket_mask;
189 		*sec_bkt =  (sec_hash >> 16) & ss->bucket_mask;
190 	} else {
191 		*prim_bkt = sec_hash & ss->bucket_mask;
192 		*sec_bkt =  (*prim_bkt ^ *sig) & ss->bucket_mask;
193 	}
194 }
195 
196 int
197 rte_member_lookup_ht(const struct rte_member_setsum *ss,
198 		const void *key, member_set_t *set_id)
199 {
200 	uint32_t prim_bucket, sec_bucket;
201 	member_sig_t tmp_sig;
202 	struct member_ht_bucket *buckets = ss->table;
203 
204 	*set_id = RTE_MEMBER_NO_MATCH;
205 	get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
206 
207 	switch (ss->sig_cmp_fn) {
208 #if defined(RTE_ARCH_X86) && defined(RTE_MACHINE_CPUFLAG_AVX2)
209 	case RTE_MEMBER_COMPARE_AVX2:
210 		if (search_bucket_single_avx(prim_bucket, tmp_sig, buckets,
211 				set_id) ||
212 				search_bucket_single_avx(sec_bucket, tmp_sig,
213 					buckets, set_id))
214 			return 1;
215 		break;
216 #endif
217 	default:
218 		if (search_bucket_single(prim_bucket, tmp_sig, buckets,
219 				set_id) ||
220 				search_bucket_single(sec_bucket, tmp_sig,
221 					buckets, set_id))
222 			return 1;
223 	}
224 
225 	return 0;
226 }
227 
228 uint32_t
229 rte_member_lookup_bulk_ht(const struct rte_member_setsum *ss,
230 		const void **keys, uint32_t num_keys, member_set_t *set_id)
231 {
232 	uint32_t i;
233 	uint32_t num_matches = 0;
234 	struct member_ht_bucket *buckets = ss->table;
235 	member_sig_t tmp_sig[RTE_MEMBER_LOOKUP_BULK_MAX];
236 	uint32_t prim_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
237 	uint32_t sec_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
238 
239 	for (i = 0; i < num_keys; i++) {
240 		get_buckets_index(ss, keys[i], &prim_buckets[i],
241 				&sec_buckets[i], &tmp_sig[i]);
242 		rte_prefetch0(&buckets[prim_buckets[i]]);
243 		rte_prefetch0(&buckets[sec_buckets[i]]);
244 	}
245 
246 	for (i = 0; i < num_keys; i++) {
247 		switch (ss->sig_cmp_fn) {
248 #if defined(RTE_ARCH_X86) && defined(RTE_MACHINE_CPUFLAG_AVX2)
249 		case RTE_MEMBER_COMPARE_AVX2:
250 			if (search_bucket_single_avx(prim_buckets[i],
251 					tmp_sig[i], buckets, &set_id[i]) ||
252 				search_bucket_single_avx(sec_buckets[i],
253 					tmp_sig[i], buckets, &set_id[i]))
254 				num_matches++;
255 			else
256 				set_id[i] = RTE_MEMBER_NO_MATCH;
257 			break;
258 #endif
259 		default:
260 			if (search_bucket_single(prim_buckets[i], tmp_sig[i],
261 					buckets, &set_id[i]) ||
262 					search_bucket_single(sec_buckets[i],
263 					tmp_sig[i], buckets, &set_id[i]))
264 				num_matches++;
265 			else
266 				set_id[i] = RTE_MEMBER_NO_MATCH;
267 		}
268 	}
269 	return num_matches;
270 }
271 
272 uint32_t
273 rte_member_lookup_multi_ht(const struct rte_member_setsum *ss,
274 		const void *key, uint32_t match_per_key,
275 		member_set_t *set_id)
276 {
277 	uint32_t num_matches = 0;
278 	uint32_t prim_bucket, sec_bucket;
279 	member_sig_t tmp_sig;
280 	struct member_ht_bucket *buckets = ss->table;
281 
282 	get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
283 
284 	switch (ss->sig_cmp_fn) {
285 #if defined(RTE_ARCH_X86) && defined(RTE_MACHINE_CPUFLAG_AVX2)
286 	case RTE_MEMBER_COMPARE_AVX2:
287 		search_bucket_multi_avx(prim_bucket, tmp_sig, buckets,
288 			&num_matches, match_per_key, set_id);
289 		if (num_matches < match_per_key)
290 			search_bucket_multi_avx(sec_bucket, tmp_sig,
291 				buckets, &num_matches, match_per_key, set_id);
292 		return num_matches;
293 #endif
294 	default:
295 		search_bucket_multi(prim_bucket, tmp_sig, buckets, &num_matches,
296 				 match_per_key, set_id);
297 		if (num_matches < match_per_key)
298 			search_bucket_multi(sec_bucket, tmp_sig,
299 				buckets, &num_matches, match_per_key, set_id);
300 		return num_matches;
301 	}
302 }
303 
304 uint32_t
305 rte_member_lookup_multi_bulk_ht(const struct rte_member_setsum *ss,
306 		const void **keys, uint32_t num_keys, uint32_t match_per_key,
307 		uint32_t *match_count,
308 		member_set_t *set_ids)
309 {
310 	uint32_t i;
311 	uint32_t num_matches = 0;
312 	struct member_ht_bucket *buckets = ss->table;
313 	uint32_t match_cnt_tmp;
314 	member_sig_t tmp_sig[RTE_MEMBER_LOOKUP_BULK_MAX];
315 	uint32_t prim_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
316 	uint32_t sec_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
317 
318 	for (i = 0; i < num_keys; i++) {
319 		get_buckets_index(ss, keys[i], &prim_buckets[i],
320 				&sec_buckets[i], &tmp_sig[i]);
321 		rte_prefetch0(&buckets[prim_buckets[i]]);
322 		rte_prefetch0(&buckets[sec_buckets[i]]);
323 	}
324 	for (i = 0; i < num_keys; i++) {
325 		match_cnt_tmp = 0;
326 
327 		switch (ss->sig_cmp_fn) {
328 #if defined(RTE_ARCH_X86) && defined(RTE_MACHINE_CPUFLAG_AVX2)
329 		case RTE_MEMBER_COMPARE_AVX2:
330 			search_bucket_multi_avx(prim_buckets[i], tmp_sig[i],
331 				buckets, &match_cnt_tmp, match_per_key,
332 				&set_ids[i*match_per_key]);
333 			if (match_cnt_tmp < match_per_key)
334 				search_bucket_multi_avx(sec_buckets[i],
335 					tmp_sig[i], buckets, &match_cnt_tmp,
336 					match_per_key,
337 					&set_ids[i*match_per_key]);
338 			match_count[i] = match_cnt_tmp;
339 			if (match_cnt_tmp != 0)
340 				num_matches++;
341 			break;
342 #endif
343 		default:
344 			search_bucket_multi(prim_buckets[i], tmp_sig[i],
345 				buckets, &match_cnt_tmp, match_per_key,
346 				&set_ids[i*match_per_key]);
347 			if (match_cnt_tmp < match_per_key)
348 				search_bucket_multi(sec_buckets[i], tmp_sig[i],
349 					buckets, &match_cnt_tmp, match_per_key,
350 					&set_ids[i*match_per_key]);
351 			match_count[i] = match_cnt_tmp;
352 			if (match_cnt_tmp != 0)
353 				num_matches++;
354 		}
355 	}
356 	return num_matches;
357 }
358 
359 static inline int
360 try_insert(struct member_ht_bucket *buckets, uint32_t prim, uint32_t sec,
361 		member_sig_t sig, member_set_t set_id)
362 {
363 	int i;
364 	/* If not full then insert into one slot */
365 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
366 		if (buckets[prim].sets[i] == RTE_MEMBER_NO_MATCH) {
367 			buckets[prim].sigs[i] = sig;
368 			buckets[prim].sets[i] = set_id;
369 			return 0;
370 		}
371 	}
372 	/* If prim failed, we need to access second bucket */
373 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
374 		if (buckets[sec].sets[i] == RTE_MEMBER_NO_MATCH) {
375 			buckets[sec].sigs[i] = sig;
376 			buckets[sec].sets[i] = set_id;
377 			return 0;
378 		}
379 	}
380 	return -1;
381 }
382 
383 static inline int
384 try_update(struct member_ht_bucket *buckets, uint32_t prim, uint32_t sec,
385 		member_sig_t sig, member_set_t set_id,
386 		enum rte_member_sig_compare_function cmp_fn)
387 {
388 	switch (cmp_fn) {
389 #if defined(RTE_ARCH_X86) && defined(RTE_MACHINE_CPUFLAG_AVX2)
390 	case RTE_MEMBER_COMPARE_AVX2:
391 		if (update_entry_search_avx(prim, sig, buckets, set_id) ||
392 				update_entry_search_avx(sec, sig, buckets,
393 					set_id))
394 			return 0;
395 		break;
396 #endif
397 	default:
398 		if (update_entry_search(prim, sig, buckets, set_id) ||
399 				update_entry_search(sec, sig, buckets,
400 					set_id))
401 			return 0;
402 	}
403 	return -1;
404 }
405 
406 static inline int
407 evict_from_bucket(void)
408 {
409 	/* For now, we randomly pick one entry to evict */
410 	return rte_rand() & (RTE_MEMBER_BUCKET_ENTRIES - 1);
411 }
412 
413 /*
414  * This function is similar to the cuckoo hash make_space function in hash
415  * library
416  */
417 static inline int
418 make_space_bucket(const struct rte_member_setsum *ss, uint32_t bkt_idx,
419 			unsigned int *nr_pushes)
420 {
421 	unsigned int i, j;
422 	int ret;
423 	struct member_ht_bucket *buckets = ss->table;
424 	uint32_t next_bucket_idx;
425 	struct member_ht_bucket *next_bkt[RTE_MEMBER_BUCKET_ENTRIES];
426 	struct member_ht_bucket *bkt = &buckets[bkt_idx];
427 	/* MSB is set to indicate if an entry has been already pushed */
428 	member_set_t flag_mask = 1U << (sizeof(member_set_t) * 8 - 1);
429 
430 	/*
431 	 * Push existing item (search for bucket with space in
432 	 * alternative locations) to its alternative location
433 	 */
434 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
435 		/* Search for space in alternative locations */
436 		next_bucket_idx = (bkt->sigs[i] ^ bkt_idx) & ss->bucket_mask;
437 		next_bkt[i] = &buckets[next_bucket_idx];
438 		for (j = 0; j < RTE_MEMBER_BUCKET_ENTRIES; j++) {
439 			if (next_bkt[i]->sets[j] == RTE_MEMBER_NO_MATCH)
440 				break;
441 		}
442 
443 		if (j != RTE_MEMBER_BUCKET_ENTRIES)
444 			break;
445 	}
446 
447 	/* Alternative location has spare room (end of recursive function) */
448 	if (i != RTE_MEMBER_BUCKET_ENTRIES) {
449 		next_bkt[i]->sigs[j] = bkt->sigs[i];
450 		next_bkt[i]->sets[j] = bkt->sets[i];
451 		return i;
452 	}
453 
454 	/* Pick entry that has not been pushed yet */
455 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++)
456 		if ((bkt->sets[i] & flag_mask) == 0)
457 			break;
458 
459 	/* All entries have been pushed, so entry cannot be added */
460 	if (i == RTE_MEMBER_BUCKET_ENTRIES ||
461 			++(*nr_pushes) > RTE_MEMBER_MAX_PUSHES)
462 		return -ENOSPC;
463 
464 	next_bucket_idx = (bkt->sigs[i] ^ bkt_idx) & ss->bucket_mask;
465 	/* Set flag to indicate that this entry is going to be pushed */
466 	bkt->sets[i] |= flag_mask;
467 
468 	/* Need room in alternative bucket to insert the pushed entry */
469 	ret = make_space_bucket(ss, next_bucket_idx, nr_pushes);
470 	/*
471 	 * After recursive function.
472 	 * Clear flags and insert the pushed entry
473 	 * in its alternative location if successful,
474 	 * or return error
475 	 */
476 	bkt->sets[i] &= ~flag_mask;
477 	if (ret >= 0) {
478 		next_bkt[i]->sigs[ret] = bkt->sigs[i];
479 		next_bkt[i]->sets[ret] = bkt->sets[i];
480 		return i;
481 	} else
482 		return ret;
483 }
484 
485 int
486 rte_member_add_ht(const struct rte_member_setsum *ss,
487 		const void *key, member_set_t set_id)
488 {
489 	int ret;
490 	unsigned int nr_pushes = 0;
491 	uint32_t prim_bucket, sec_bucket;
492 	member_sig_t tmp_sig;
493 	struct member_ht_bucket *buckets = ss->table;
494 	member_set_t flag_mask = 1U << (sizeof(member_set_t) * 8 - 1);
495 
496 	if (set_id == RTE_MEMBER_NO_MATCH || (set_id & flag_mask) != 0)
497 		return -EINVAL;
498 
499 	get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
500 
501 	/*
502 	 * If it is cache based setsummary, we try overwriting (updating)
503 	 * existing entry with the same signature first. In cache mode, we allow
504 	 * false negatives and only cache the most recent keys.
505 	 *
506 	 * For non-cache mode, we do not update existing entry with the same
507 	 * signature. This is because if two keys with same signature update
508 	 * each other, false negative may happen, which is not the expected
509 	 * behavior for non-cache setsummary.
510 	 */
511 	if (ss->cache) {
512 		ret = try_update(buckets, prim_bucket, sec_bucket, tmp_sig,
513 					set_id, ss->sig_cmp_fn);
514 		if (ret != -1)
515 			return ret;
516 	}
517 	/* If not full then insert into one slot */
518 	ret = try_insert(buckets, prim_bucket, sec_bucket, tmp_sig, set_id);
519 	if (ret != -1)
520 		return ret;
521 
522 	/* Random pick prim or sec for recursive displacement */
523 	uint32_t select_bucket = (tmp_sig && 1U) ? prim_bucket : sec_bucket;
524 	if (ss->cache) {
525 		ret = evict_from_bucket();
526 		buckets[select_bucket].sigs[ret] = tmp_sig;
527 		buckets[select_bucket].sets[ret] = set_id;
528 		return 1;
529 	}
530 
531 	ret = make_space_bucket(ss, select_bucket, &nr_pushes);
532 	if (ret >= 0) {
533 		buckets[select_bucket].sigs[ret] = tmp_sig;
534 		buckets[select_bucket].sets[ret] = set_id;
535 		ret = 1;
536 	}
537 
538 	return ret;
539 }
540 
541 void
542 rte_member_free_ht(struct rte_member_setsum *ss)
543 {
544 	rte_free(ss->table);
545 }
546 
547 int
548 rte_member_delete_ht(const struct rte_member_setsum *ss, const void *key,
549 		member_set_t set_id)
550 {
551 	int i;
552 	uint32_t prim_bucket, sec_bucket;
553 	member_sig_t tmp_sig;
554 	struct member_ht_bucket *buckets = ss->table;
555 
556 	get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
557 
558 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
559 		if (tmp_sig == buckets[prim_bucket].sigs[i] &&
560 				set_id == buckets[prim_bucket].sets[i]) {
561 			buckets[prim_bucket].sets[i] = RTE_MEMBER_NO_MATCH;
562 			return 0;
563 		}
564 	}
565 
566 	for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
567 		if (tmp_sig == buckets[sec_bucket].sigs[i] &&
568 				set_id == buckets[sec_bucket].sets[i]) {
569 			buckets[sec_bucket].sets[i] = RTE_MEMBER_NO_MATCH;
570 			return 0;
571 		}
572 	}
573 	return -ENOENT;
574 }
575 
576 void
577 rte_member_reset_ht(const struct rte_member_setsum *ss)
578 {
579 	uint32_t i, j;
580 	struct member_ht_bucket *buckets = ss->table;
581 
582 	for (i = 0; i < ss->bucket_cnt; i++) {
583 		for (j = 0; j < RTE_MEMBER_BUCKET_ENTRIES; j++)
584 			buckets[i].sets[j] = RTE_MEMBER_NO_MATCH;
585 	}
586 }
587