1
2
3
4#include <string.h>
5#include <stdio.h>
6#include <errno.h>
7
8#include <rte_common.h>
9#include <rte_cycles.h>
10#include <rte_prefetch.h>
11
12#include "rte_swx_table_learner.h"
13
14#ifndef RTE_SWX_TABLE_LEARNER_USE_HUGE_PAGES
15#define RTE_SWX_TABLE_LEARNER_USE_HUGE_PAGES 1
16#endif
17
18#ifndef RTE_SWX_TABLE_SELECTOR_HUGE_PAGES_DISABLE
19
20#include <rte_malloc.h>
21
22static void *
23env_calloc(size_t size, size_t alignment, int numa_node)
24{
25 return rte_zmalloc_socket(NULL, size, alignment, numa_node);
26}
27
28static void
29env_free(void *start, size_t size __rte_unused)
30{
31 rte_free(start);
32}
33
34#else
35
36#include <numa.h>
37
38static void *
39env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
40{
41 void *start;
42
43 if (numa_available() == -1)
44 return NULL;
45
46 start = numa_alloc_onnode(size, numa_node);
47 if (!start)
48 return NULL;
49
50 memset(start, 0, size);
51 return start;
52}
53
54static void
55env_free(void *start, size_t size)
56{
57 if ((numa_available() == -1) || !start)
58 return;
59
60 numa_free(start, size);
61}
62
63#endif
64
65#if defined(RTE_ARCH_X86_64)
66
67#include <x86intrin.h>
68
69#define crc32_u64(crc, v) _mm_crc32_u64(crc, v)
70
71#else
72
73static inline uint64_t
74crc32_u64_generic(uint64_t crc, uint64_t value)
75{
76 int i;
77
78 crc = (crc & 0xFFFFFFFFLLU) ^ value;
79 for (i = 63; i >= 0; i--) {
80 uint64_t mask;
81
82 mask = -(crc & 1LLU);
83 crc = (crc >> 1LLU) ^ (0x82F63B78LLU & mask);
84 }
85
86 return crc;
87}
88
89#define crc32_u64(crc, v) crc32_u64_generic(crc, v)
90
91#endif
92
93
94static inline uint32_t
95hash(void *key, void *key_mask, uint32_t key_size, uint32_t seed)
96{
97 uint64_t *k = key;
98 uint64_t *m = key_mask;
99 uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5;
100
101 switch (key_size) {
102 case 8:
103 crc0 = crc32_u64(seed, k[0] & m[0]);
104 return crc0;
105
106 case 16:
107 k0 = k[0] & m[0];
108
109 crc0 = crc32_u64(k0, seed);
110 crc1 = crc32_u64(k0 >> 32, k[1] & m[1]);
111
112 crc0 ^= crc1;
113
114 return crc0;
115
116 case 32:
117 k0 = k[0] & m[0];
118 k2 = k[2] & m[2];
119
120 crc0 = crc32_u64(k0, seed);
121 crc1 = crc32_u64(k0 >> 32, k[1] & m[1]);
122
123 crc2 = crc32_u64(k2, k[3] & m[3]);
124 crc3 = k2 >> 32;
125
126 crc0 = crc32_u64(crc0, crc1);
127 crc1 = crc32_u64(crc2, crc3);
128
129 crc0 ^= crc1;
130
131 return crc0;
132
133 case 64:
134 k0 = k[0] & m[0];
135 k2 = k[2] & m[2];
136 k5 = k[5] & m[5];
137
138 crc0 = crc32_u64(k0, seed);
139 crc1 = crc32_u64(k0 >> 32, k[1] & m[1]);
140
141 crc2 = crc32_u64(k2, k[3] & m[3]);
142 crc3 = crc32_u64(k2 >> 32, k[4] & m[4]);
143
144 crc4 = crc32_u64(k5, k[6] & m[6]);
145 crc5 = crc32_u64(k5 >> 32, k[7] & m[7]);
146
147 crc0 = crc32_u64(crc0, (crc1 << 32) ^ crc2);
148 crc1 = crc32_u64(crc3, (crc4 << 32) ^ crc5);
149
150 crc0 ^= crc1;
151
152 return crc0;
153
154 default:
155 crc0 = 0;
156 return crc0;
157 }
158}
159
160
161static void
162table_keycpy(void *dst, void *src, void *src_mask, uint32_t n_bytes)
163{
164 uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask;
165 uint32_t i;
166
167 for (i = 0; i < n_bytes / sizeof(uint64_t); i++)
168 dst64[i] = src64[i] & src_mask64[i];
169}
170
171
172
173
174static inline uint32_t
175table_keycmp(void *a, void *b, void *b_mask, uint32_t n_bytes)
176{
177 uint64_t *a64 = a, *b64 = b, *b_mask64 = b_mask;
178
179 switch (n_bytes) {
180 case 8: {
181 uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]);
182 uint32_t result = 1;
183
184 if (xor0)
185 result = 0;
186 return result;
187 }
188
189 case 16: {
190 uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]);
191 uint64_t xor1 = a64[1] ^ (b64[1] & b_mask64[1]);
192 uint64_t or = xor0 | xor1;
193 uint32_t result = 1;
194
195 if (or)
196 result = 0;
197 return result;
198 }
199
200 case 32: {
201 uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]);
202 uint64_t xor1 = a64[1] ^ (b64[1] & b_mask64[1]);
203 uint64_t xor2 = a64[2] ^ (b64[2] & b_mask64[2]);
204 uint64_t xor3 = a64[3] ^ (b64[3] & b_mask64[3]);
205 uint64_t or = (xor0 | xor1) | (xor2 | xor3);
206 uint32_t result = 1;
207
208 if (or)
209 result = 0;
210 return result;
211 }
212
213 case 64: {
214 uint64_t xor0 = a64[0] ^ (b64[0] & b_mask64[0]);
215 uint64_t xor1 = a64[1] ^ (b64[1] & b_mask64[1]);
216 uint64_t xor2 = a64[2] ^ (b64[2] & b_mask64[2]);
217 uint64_t xor3 = a64[3] ^ (b64[3] & b_mask64[3]);
218 uint64_t xor4 = a64[4] ^ (b64[4] & b_mask64[4]);
219 uint64_t xor5 = a64[5] ^ (b64[5] & b_mask64[5]);
220 uint64_t xor6 = a64[6] ^ (b64[6] & b_mask64[6]);
221 uint64_t xor7 = a64[7] ^ (b64[7] & b_mask64[7]);
222 uint64_t or = ((xor0 | xor1) | (xor2 | xor3)) |
223 ((xor4 | xor5) | (xor6 | xor7));
224 uint32_t result = 1;
225
226 if (or)
227 result = 0;
228 return result;
229 }
230
231 default: {
232 uint32_t i;
233
234 for (i = 0; i < n_bytes / sizeof(uint64_t); i++)
235 if (a64[i] != (b64[i] & b_mask64[i]))
236 return 0;
237 return 1;
238 }
239 }
240}
241
242#define TABLE_KEYS_PER_BUCKET 4
243
244#define TABLE_BUCKET_USEFUL_SIZE \
245 (TABLE_KEYS_PER_BUCKET * (sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t)))
246
247#define TABLE_BUCKET_PAD_SIZE \
248 (RTE_CACHE_LINE_SIZE - TABLE_BUCKET_USEFUL_SIZE)
249
250struct table_bucket {
251 uint32_t time[TABLE_KEYS_PER_BUCKET];
252 uint32_t sig[TABLE_KEYS_PER_BUCKET];
253 uint8_t key_timeout_id[TABLE_KEYS_PER_BUCKET];
254 uint8_t pad[TABLE_BUCKET_PAD_SIZE];
255 uint8_t key[];
256};
257
258struct table_params {
259
260 size_t key_size;
261
262
263
264
265
266 size_t key_size_pow2;
267
268
269 size_t key_size_log2;
270
271
272 size_t key_offset;
273
274
275 size_t action_data_size;
276
277
278
279
280 size_t data_size_pow2;
281
282
283 size_t data_size_log2;
284
285
286 size_t n_buckets;
287
288
289 size_t bucket_mask;
290
291
292
293
294 size_t bucket_key_all_size;
295
296
297 size_t bucket_size;
298
299
300 size_t bucket_size_log2;
301
302
303 uint64_t key_timeout[RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX];
304
305
306 uint32_t n_key_timeouts;
307
308
309 size_t total_size;
310};
311
312struct table {
313
314 struct table_params params;
315
316
317 uint8_t key_mask0[RTE_CACHE_LINE_SIZE];
318
319
320 uint8_t buckets[];
321} __rte_cache_aligned;
322
323
324
325
326
327
328static uint64_t
329timeout_convert(uint32_t timeout_in_seconds)
330{
331 uint64_t timeout_in_cycles = timeout_in_seconds * rte_get_tsc_hz();
332
333 if (!(timeout_in_cycles >> 32))
334 timeout_in_cycles = 1LLU << 32;
335
336 return timeout_in_cycles;
337}
338
339static int
340table_params_get(struct table_params *p, struct rte_swx_table_learner_params *params)
341{
342 uint32_t i;
343
344
345 if (!params ||
346 !params->key_size ||
347 (params->key_size > 64) ||
348 !params->n_keys_max ||
349 (params->n_keys_max > 1U << 31) ||
350 !params->key_timeout ||
351 !params->n_key_timeouts ||
352 (params->n_key_timeouts > RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX))
353 return -EINVAL;
354
355 for (i = 0; i < params->n_key_timeouts; i++)
356 if (!params->key_timeout[i])
357 return -EINVAL;
358
359
360 p->key_size = params->key_size;
361
362 p->key_size_pow2 = rte_align64pow2(p->key_size);
363 if (p->key_size_pow2 < 8)
364 p->key_size_pow2 = 8;
365
366 p->key_size_log2 = __builtin_ctzll(p->key_size_pow2);
367
368 p->key_offset = params->key_offset;
369
370
371 p->action_data_size = params->action_data_size;
372
373 p->data_size_pow2 = rte_align64pow2(sizeof(uint64_t) + p->action_data_size);
374
375 p->data_size_log2 = __builtin_ctzll(p->data_size_pow2);
376
377
378 p->n_buckets = rte_align32pow2(params->n_keys_max);
379
380 p->bucket_mask = p->n_buckets - 1;
381
382 p->bucket_key_all_size = TABLE_KEYS_PER_BUCKET * p->key_size_pow2;
383
384 p->bucket_size = rte_align64pow2(sizeof(struct table_bucket) +
385 p->bucket_key_all_size +
386 TABLE_KEYS_PER_BUCKET * p->data_size_pow2);
387
388 p->bucket_size_log2 = __builtin_ctzll(p->bucket_size);
389
390
391 for (i = 0; i < params->n_key_timeouts; i++)
392 p->key_timeout[i] = timeout_convert(params->key_timeout[i]);
393
394 p->n_key_timeouts = rte_align32pow2(params->n_key_timeouts);
395
396 for ( ; i < p->n_key_timeouts; i++)
397 p->key_timeout[i] = p->key_timeout[0];
398
399
400 p->total_size = sizeof(struct table) + p->n_buckets * p->bucket_size;
401
402 return 0;
403}
404
405static inline struct table_bucket *
406table_bucket_get(struct table *t, size_t bucket_id)
407{
408 return (struct table_bucket *)&t->buckets[bucket_id << t->params.bucket_size_log2];
409}
410
411static inline uint8_t *
412table_bucket_key_get(struct table *t, struct table_bucket *b, size_t bucket_key_pos)
413{
414 return &b->key[bucket_key_pos << t->params.key_size_log2];
415}
416
417static inline uint64_t *
418table_bucket_data_get(struct table *t, struct table_bucket *b, size_t bucket_key_pos)
419{
420 return (uint64_t *)&b->key[t->params.bucket_key_all_size +
421 (bucket_key_pos << t->params.data_size_log2)];
422}
423
424uint64_t
425rte_swx_table_learner_footprint_get(struct rte_swx_table_learner_params *params)
426{
427 struct table_params p;
428 int status;
429
430 status = table_params_get(&p, params);
431
432 return status ? 0 : p.total_size;
433}
434
435void *
436rte_swx_table_learner_create(struct rte_swx_table_learner_params *params, int numa_node)
437{
438 struct table_params p;
439 struct table *t;
440 int status;
441
442
443 status = table_params_get(&p, params);
444 if (status)
445 return NULL;
446
447
448 t = env_calloc(p.total_size, RTE_CACHE_LINE_SIZE, numa_node);
449 if (!t)
450 return NULL;
451
452
453 memcpy(&t->params, &p, sizeof(struct table_params));
454
455 if (params->key_mask0)
456 memcpy(t->key_mask0, params->key_mask0, params->key_size);
457 else
458 memset(t->key_mask0, 0xFF, params->key_size);
459
460 return t;
461}
462
463void
464rte_swx_table_learner_free(void *table)
465{
466 struct table *t = table;
467
468 if (!t)
469 return;
470
471 env_free(t, t->params.total_size);
472}
473
474int
475rte_swx_table_learner_timeout_update(void *table,
476 uint32_t key_timeout_id,
477 uint32_t key_timeout)
478{
479 struct table *t = table;
480
481 if (!t ||
482 (key_timeout_id >= t->params.n_key_timeouts) ||
483 !key_timeout)
484 return -EINVAL;
485
486 t->params.key_timeout[key_timeout_id] = timeout_convert(key_timeout);
487
488 return 0;
489}
490
491struct mailbox {
492
493 struct table_bucket *bucket;
494
495
496 uint32_t input_sig;
497
498
499 uint8_t *input_key;
500
501
502 uint32_t hit;
503
504
505 size_t bucket_key_pos;
506
507
508 int state;
509};
510
511uint64_t
512rte_swx_table_learner_mailbox_size_get(void)
513{
514 return sizeof(struct mailbox);
515}
516
517int
518rte_swx_table_learner_lookup(void *table,
519 void *mailbox,
520 uint64_t input_time,
521 uint8_t **key,
522 uint64_t *action_id,
523 uint8_t **action_data,
524 int *hit)
525{
526 struct table *t = table;
527 struct mailbox *m = mailbox;
528
529 switch (m->state) {
530 case 0: {
531 uint8_t *input_key;
532 struct table_bucket *b;
533 size_t bucket_id;
534 uint32_t input_sig;
535
536 input_key = &(*key)[t->params.key_offset];
537 input_sig = hash(input_key, t->key_mask0, t->params.key_size_pow2, 0);
538 bucket_id = input_sig & t->params.bucket_mask;
539 b = table_bucket_get(t, bucket_id);
540
541 rte_prefetch0(b);
542 rte_prefetch0(&b->key[0]);
543 rte_prefetch0(&b->key[RTE_CACHE_LINE_SIZE]);
544
545 m->bucket = b;
546 m->input_key = input_key;
547 m->input_sig = input_sig | 1;
548 m->state = 1;
549 return 0;
550 }
551
552 case 1: {
553 struct table_bucket *b = m->bucket;
554 uint32_t i;
555
556
557 for (i = 0; i < TABLE_KEYS_PER_BUCKET; i++) {
558 uint64_t time = b->time[i];
559 uint32_t sig = b->sig[i];
560 uint8_t *key = table_bucket_key_get(t, b, i);
561 uint32_t key_size_pow2 = t->params.key_size_pow2;
562
563 time <<= 32;
564
565 if ((time > input_time) &&
566 (sig == m->input_sig) &&
567 table_keycmp(key, m->input_key, t->key_mask0, key_size_pow2)) {
568 uint64_t *data = table_bucket_data_get(t, b, i);
569
570
571 rte_prefetch0(data);
572
573 m->hit = 1;
574 m->bucket_key_pos = i;
575 m->state = 0;
576
577 *action_id = data[0];
578 *action_data = (uint8_t *)&data[1];
579 *hit = 1;
580 return 1;
581 }
582 }
583
584
585 m->hit = 0;
586 m->state = 0;
587
588 *hit = 0;
589 return 1;
590 }
591
592 default:
593
594 m->hit = 0;
595 m->state = 0;
596
597 *hit = 0;
598 return 1;
599 }
600}
601
602void
603rte_swx_table_learner_rearm(void *table,
604 void *mailbox,
605 uint64_t input_time)
606{
607 struct table *t = table;
608 struct mailbox *m = mailbox;
609 struct table_bucket *b;
610 size_t bucket_key_pos;
611 uint64_t key_timeout;
612 uint32_t key_timeout_id;
613
614 if (!m->hit)
615 return;
616
617 b = m->bucket;
618 bucket_key_pos = m->bucket_key_pos;
619
620 key_timeout_id = b->key_timeout_id[bucket_key_pos];
621 key_timeout = t->params.key_timeout[key_timeout_id];
622 b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
623}
624
625void
626rte_swx_table_learner_rearm_new(void *table,
627 void *mailbox,
628 uint64_t input_time,
629 uint32_t key_timeout_id)
630{
631 struct table *t = table;
632 struct mailbox *m = mailbox;
633 struct table_bucket *b;
634 size_t bucket_key_pos;
635 uint64_t key_timeout;
636
637 if (!m->hit)
638 return;
639
640 b = m->bucket;
641 bucket_key_pos = m->bucket_key_pos;
642
643 key_timeout_id &= t->params.n_key_timeouts - 1;
644 key_timeout = t->params.key_timeout[key_timeout_id];
645 b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
646 b->key_timeout_id[bucket_key_pos] = (uint8_t)key_timeout_id;
647}
648
649uint32_t
650rte_swx_table_learner_add(void *table,
651 void *mailbox,
652 uint64_t input_time,
653 uint64_t action_id,
654 uint8_t *action_data,
655 uint32_t key_timeout_id)
656{
657 struct table *t = table;
658 struct mailbox *m = mailbox;
659 struct table_bucket *b = m->bucket;
660 uint64_t key_timeout;
661 uint32_t i;
662
663
664 key_timeout_id &= t->params.n_key_timeouts - 1;
665 key_timeout = t->params.key_timeout[key_timeout_id];
666
667
668
669
670
671
672 if (m->hit) {
673 size_t bucket_key_pos = m->bucket_key_pos;
674 uint64_t *data = table_bucket_data_get(t, b, bucket_key_pos);
675
676
677 b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
678 b->key_timeout_id[bucket_key_pos] = (uint8_t)key_timeout_id;
679
680
681 data[0] = action_id;
682 if (t->params.action_data_size && action_data)
683 memcpy(&data[1], action_data, t->params.action_data_size);
684
685 return 0;
686 }
687
688
689 for (i = 0; i < TABLE_KEYS_PER_BUCKET; i++) {
690 uint64_t time = b->time[i];
691
692 time <<= 32;
693
694
695
696
697
698 if (time < input_time) {
699 uint8_t *key = table_bucket_key_get(t, b, i);
700 uint64_t *data = table_bucket_data_get(t, b, i);
701
702
703 b->time[i] = (input_time + key_timeout) >> 32;
704 b->sig[i] = m->input_sig;
705 b->key_timeout_id[i] = (uint8_t)key_timeout_id;
706 table_keycpy(key, m->input_key, t->key_mask0, t->params.key_size_pow2);
707
708
709 data[0] = action_id;
710 if (t->params.action_data_size && action_data)
711 memcpy(&data[1], action_data, t->params.action_data_size);
712
713
714 m->hit = 1;
715 m->bucket_key_pos = i;
716
717 return 0;
718 }
719 }
720
721
722 return 1;
723}
724
725void
726rte_swx_table_learner_delete(void *table __rte_unused,
727 void *mailbox)
728{
729 struct mailbox *m = mailbox;
730
731 if (m->hit) {
732 struct table_bucket *b = m->bucket;
733
734
735 b->time[m->bucket_key_pos] = 0;
736
737
738 m->hit = 0;
739 }
740}
741