-
Notifications
You must be signed in to change notification settings - Fork 160
/
Copy pathlow.go
841 lines (730 loc) · 29.4 KB
/
low.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
// Copyright 2017 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package low
// Libraries below are DPDK libraries. PMD drivers and some basic DPDK
// libraries have to be specified between --whole-archive and
// --start-group options because PMD driver libraries have to be
// linked entirely and dependencies for them have to be resolved
// circularly. Don't add any more libraries inside of --whole-archive
// group of libraries unless they are really necessary there because
// it increases executable size and build time.
/*
#include "low.h"
*/
import "C"
import (
"os"
"runtime"
"strconv"
"sync/atomic"
"syscall"
"unsafe"
"github.com/intel-go/nff-go/asm"
"github.com/intel-go/nff-go/common"
"github.com/intel-go/nff-go/types"
)
var (
CountersEnabledInFramework bool = bool(C.counters_enabled_in_framework)
UseInterlockedCounters bool = bool(C.use_interlocked_counters)
AnalyzePacketSizes bool = bool(C.analyze_packet_sizes)
)
var ringName = 1
// DirectStop frees mbufs.
func DirectStop(pktsForFreeNumber int, buf []uintptr) {
C.directStop(C.int(pktsForFreeNumber), (**C.struct_rte_mbuf)(unsafe.Pointer(&(buf[0]))))
}
// DirectSend sends one mbuf.
func DirectSend(m *Mbuf, port uint16) bool {
return bool(C.directSend((*C.struct_rte_mbuf)(m), C.uint16_t(port)))
}
// Ring is a ring buffer for pointers
type Ring C.struct_nff_go_ring
type Rings []*Ring
// Mbuf is a message buffer.
type Mbuf C.struct_rte_mbuf
// Mempool is a pool of objects.
type Mempool C.struct_rte_mempool
type Port C.struct_cPort
var mbufNumberT uint
var mbufCacheSizeT uint
type mempoolPair struct {
mempool *C.struct_rte_mempool
name string
}
var usedMempools []mempoolPair
func GetPort(n uint16) *Port {
p := new(Port)
p.PortId = C.uint16_t(n)
p.QueuesNumber = 1
return p
}
func CheckRSSPacketCount(p *Port, queue int16) int64 {
return int64(C.checkRSSPacketCount((*C.struct_cPort)(p), (C.int16_t(queue))))
}
// GetPortMACAddress gets MAC address of given port.
func GetPortMACAddress(port uint16) [types.EtherAddrLen]uint8 {
var mac [types.EtherAddrLen]uint8
var cmac C.struct_rte_ether_addr
C.rte_eth_macaddr_get(C.uint16_t(port), &cmac)
for i := range mac {
mac[i] = uint8(cmac.addr_bytes[i])
}
return mac
}
// GetPortByName gets the port id from device name. The device name should be
// specified as below:
//
// - PCIe address (Domain:Bus:Device.Function), for example- 0000:2:00.0
// - SoC device name, for example- fsl-gmac0
// - vdev dpdk name, for example- net_[pcap0|null0|tap0]
func GetPortByName(name string) (uint16, error) {
var port C.uint16_t
ret := C.rte_eth_dev_get_port_by_name(C.CString(name), &port)
switch ret {
case 0:
return uint16(port), nil
case -C.ENODEV, -C.EINVAL:
msg := common.LogError(common.Debug,
"GetPortByName cannot find device: no such device")
return 0, common.WrapWithNFError(nil, msg, common.BadArgument)
default:
msg := common.LogError(common.Debug, "GetPortByName got an unknown error")
return 0, common.WrapWithNFError(nil, msg, common.Fail)
}
}
// GetNameByPort gets the device name from port id. The device name is specified as below:
//
// - PCIe address (Domain:Bus:Device.Function), for example- 0000:02:00.0
// - SoC device name, for example- fsl-gmac0
// - vdev dpdk name, for example- net_[pcap0|null0|tun0|tap0]
func GetNameByPort(port uint16) (string, error) {
s := make([]C.char, C.RTE_ETH_NAME_MAX_LEN)
ret := C.rte_eth_dev_get_name_by_port(C.uint16_t(port), &s[0])
switch ret {
case 0:
return C.GoString(&s[0]), nil
case -C.EINVAL:
msg := common.LogError(common.Debug, "GetNameByPort cannot find port")
return "", common.WrapWithNFError(nil, msg, common.BadArgument)
default:
msg := common.LogError(common.Debug, "GetNameByPort got an unknown error")
return "", common.WrapWithNFError(nil, msg, common.Fail)
}
}
// GetPacketDataStartPointer returns the pointer to the
// beginning of packet.
func GetPacketDataStartPointer(mb *Mbuf) uintptr {
return uintptr(mb.buf_addr) + uintptr(mb.data_off)
}
var packetStructSize int
// SetPacketStructSize sets the size of the packet.
func SetPacketStructSize(t int) error {
if t > C.RTE_PKTMBUF_HEADROOM {
msg := common.LogError(common.Initialization, "Packet structure can't be placed inside mbuf.",
"Increase CONFIG_RTE_PKTMBUF_HEADROOM in dpdk/config/common_base and rebuild dpdk.")
return common.WrapWithNFError(nil, msg, common.PktMbufHeadRoomTooSmall)
}
minPacketHeadroom := 64
if C.RTE_PKTMBUF_HEADROOM-t < minPacketHeadroom {
common.LogWarning(common.Initialization, "Packet will have only", C.RTE_PKTMBUF_HEADROOM-t,
"bytes for prepend something, increase CONFIG_RTE_PKTMBUF_HEADROOM in dpdk/config/common_base and rebuild dpdk.")
}
packetStructSize = t
return nil
}
// PrependMbuf prepends length bytes to mbuf data area.
// TODO 4 following functions support only not chained mbufs now
// Heavily based on DPDK rte_pktmbuf_prepend
func PrependMbuf(mb *Mbuf, length uint) bool {
if C.uint16_t(length) > mb.data_off-C.uint16_t(packetStructSize) {
return false
}
mb.data_off -= C.uint16_t(length)
mb.data_len += C.uint16_t(length)
mb.pkt_len += C.uint32_t(length)
return true
}
// AppendMbuf appends length bytes to mbuf.
// Heavily based on DPDK rte_pktmbuf_append
func AppendMbuf(mb *Mbuf, length uint) bool {
if C.uint16_t(length) > mb.buf_len-mb.data_off-mb.data_len {
return false
}
mb.data_len += C.uint16_t(length)
mb.pkt_len += C.uint32_t(length)
return true
}
// AdjMbuf removes length bytes at mbuf beginning.
// Heavily based on DPDK rte_pktmbuf_adj
func AdjMbuf(m *Mbuf, length uint) bool {
if C.uint16_t(length) > m.data_len {
return false
}
m.data_off += C.uint16_t(length)
m.data_len -= C.uint16_t(length)
m.pkt_len -= C.uint32_t(length)
return true
}
// TrimMbuf removes length bytes at the mbuf end.
// Heavily based on DPDK rte_pktmbuf_trim
func TrimMbuf(m *Mbuf, length uint) bool {
if C.uint16_t(length) > m.data_len {
return false
}
m.data_len -= C.uint16_t(length)
m.pkt_len -= C.uint32_t(length)
return true
}
func setMbufLen(mb *Mbuf, l2len, l3len uint32) {
// Assign l2_len:7 and l3_len:9 fields in rte_mbuf
mb.anon5[0] = uint8((l2len & 0x7f) | ((l3len & 1) << 7))
mb.anon5[1] = uint8(l3len >> 1)
mb.anon5[2] = 0
mb.anon5[3] = 0
mb.anon5[4] = 0
mb.anon5[5] = 0
mb.anon5[6] = 0
mb.anon5[7] = 0
}
// SetTXIPv4OLFlags sets mbuf flags for IPv4 header
// checksum calculation hardware offloading.
func SetTXIPv4OLFlags(mb *Mbuf, l2len, l3len uint32) {
// PKT_TX_IP_CKSUM | PKT_TX_IPV4
mb.ol_flags = (1 << 54) | (1 << 55)
setMbufLen(mb, l2len, l3len)
}
// SetTXIPv4UDPOLFlags sets mbuf flags for IPv4 and UDP
// headers checksum calculation hardware offloading.
func SetTXIPv4UDPOLFlags(mb *Mbuf, l2len, l3len uint32) {
// PKT_TX_UDP_CKSUM | PKT_TX_IP_CKSUM | PKT_TX_IPV4
mb.ol_flags = (3 << 52) | (1 << 54) | (1 << 55)
setMbufLen(mb, l2len, l3len)
}
// SetTXIPv4TCPOLFlags sets mbuf flags for IPv4 and TCP
// headers checksum calculation hardware offloading.
func SetTXIPv4TCPOLFlags(mb *Mbuf, l2len, l3len uint32) {
// PKT_TX_TCP_CKSUM | PKT_TX_IP_CKSUM | PKT_TX_IPV4
mb.ol_flags = (1 << 52) | (1 << 54) | (1 << 55)
setMbufLen(mb, l2len, l3len)
}
// SetTXIPv6UDPOLFlags sets mbuf flags for IPv6 UDP header
// checksum calculation hardware offloading.
func SetTXIPv6UDPOLFlags(mb *Mbuf, l2len, l3len uint32) {
// PKT_TX_UDP_CKSUM | PKT_TX_IPV6
mb.ol_flags = (3 << 52) | (1 << 56)
setMbufLen(mb, l2len, l3len)
}
// SetTXIPv6TCPOLFlags sets mbuf flags for IPv6 TCP
// header checksum calculation hardware offloading.
func SetTXIPv6TCPOLFlags(mb *Mbuf, l2len, l3len uint32) {
// PKT_TX_TCP_CKSUM | PKT_TX_IPV4
mb.ol_flags = (1 << 52) | (1 << 56)
setMbufLen(mb, l2len, l3len)
}
// These constants are used by packet package to parse protocol headers
const (
RtePtypeL2Ether = C.RTE_PTYPE_L2_ETHER
RtePtypeL3Ipv4 = C.RTE_PTYPE_L3_IPV4
RtePtypeL3Ipv6 = C.RTE_PTYPE_L3_IPV6
RtePtypeL4Tcp = C.RTE_PTYPE_L4_TCP
RtePtypeL4Udp = C.RTE_PTYPE_L4_UDP
)
// These constants are used by packet package for longest prefix match lookup
const (
RteLpmValidExtEntryBitmask = C.RTE_LPM_VALID_EXT_ENTRY_BITMASK
RteLpmTbl8GroupNumEntries = C.RTE_LPM_TBL8_GROUP_NUM_ENTRIES
RteLpmLookupSuccess = C.RTE_LPM_LOOKUP_SUCCESS
)
// Lookup ring with given name.
func LookupRing(name string) *Ring {
return (*Ring)(unsafe.Pointer(C.nff_go_ring_lookup(C.CString(name))))
}
// CreateRing creates ring with given name and count.
func CreateRing(count uint) *Ring {
name := strconv.Itoa(ringName)
ringName++
// Flag 0x0000 means ring default mode which is Multiple Consumer / Multiple Producer
return (*Ring)(unsafe.Pointer(C.nff_go_ring_create(C.CString(name), C.uint(count), C.SOCKET_ID_ANY, 0x0000)))
}
// CreateRings creates ring with given name and count.
func CreateRings(count uint, inIndexNumber int32) Rings {
rings := make(Rings, inIndexNumber, inIndexNumber)
for i := int32(0); i < inIndexNumber; i++ {
rings[i] = CreateRing(count)
}
return rings
}
// EnqueueBurst enqueues data to ring buffer.
func (ring *Ring) EnqueueBurst(buffer []uintptr, count uint) uint {
return nffgoRingMpEnqueueBurst((*C.struct_nff_go_ring)(ring), buffer, count)
}
// DequeueBurst dequeues data from ring buffer.
func (ring *Ring) DequeueBurst(buffer []uintptr, count uint) uint {
return nffgoRingMcDequeueBurst((*C.struct_nff_go_ring)(ring), buffer, count)
}
// Heavily based on DPDK ENQUEUE_PTRS
// in C version it is a macros with do-while(0). I suppose that we don't need while(0) now because it is a function.
func enqueuePtrs(r *C.struct_nff_go_ring, prodHead C.uint32_t, mask C.uint32_t, n uint, objTable []uintptr /*const*/) {
var size C.uint32_t = r.DPDK_ring.size
var idx C.uint32_t = prodHead & mask
var i uint
if idx+C.uint32_t(n) < size { //likely
for i = 0; i < (n & (^(uint(0x3)))); i, idx = i+4, idx+4 {
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset))) = objTable[i]
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+1)*r.offset))) = objTable[i+1]
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+2)*r.offset))) = objTable[i+2]
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+3)*r.offset))) = objTable[i+3]
}
switch n & 0x3 {
case 3:
{
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset))) = objTable[i]
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+1)*r.offset))) = objTable[i+1]
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+2)*r.offset))) = objTable[i+2]
}
case 2:
{
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset))) = objTable[i]
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+1)*r.offset))) = objTable[i+1]
}
case 1:
{
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset))) = objTable[i]
}
}
} else {
for i = 0; idx < size; i, idx = i+1, idx+1 {
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset))) = objTable[i]
}
for idx = 0; i < n; i, idx = i+1, idx+1 {
*(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset))) = objTable[i]
}
}
}
// Heavily based on DPDK DEQUEUE_PTRS
// in C version it is a macros with do-while(0). I suppose that we don't need while(0) now because it is a function.
func dequeuePtrs(r *C.struct_nff_go_ring, consHead C.uint32_t, mask C.uint32_t, n uint, objTable []uintptr) {
var idx C.uint32_t = consHead & mask
var size C.uint32_t = r.DPDK_ring.size
var i uint
if idx+C.uint32_t(n) < size { //likely
for i = 0; i < (n & (^(uint(0x3)))); i, idx = i+4, idx+4 {
objTable[i] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset)))
objTable[i+1] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+1)*r.offset)))
objTable[i+2] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+2)*r.offset)))
objTable[i+3] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+3)*r.offset)))
}
switch n & 0x3 {
case 3:
{
objTable[i] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset)))
objTable[i+1] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+1)*r.offset)))
objTable[i+2] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+2)*r.offset)))
}
case 2:
{
objTable[i] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset)))
objTable[i+1] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx+1)*r.offset)))
}
case 1:
{
objTable[i] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset)))
}
}
} else {
for i = 0; idx < size; i, idx = i+1, idx+1 {
objTable[i] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset)))
}
for idx = 0; i < n; i, idx = i+1, idx+1 {
objTable[i] = *(*uintptr)(unsafe.Pointer(uintptr(r.internal_DPDK_ring) + uintptr((idx)*r.offset)))
}
}
}
// Heavily based on DPDK mp_do_enqueue
func nffgoRingMpDoEnqueue(r *C.struct_nff_go_ring, objTable []uintptr, n uint) uint {
var prodHead, prodNext C.uint32_t
var consTail, freeEntries C.uint32_t
var max = n //max should be const but can't
var success = false
var mask C.uint32_t = r.DPDK_ring.mask
// move prod.head atomically
for success == false { //unlikely
// Reset n to the initial burst count
n = max
prodHead = r.DPDK_ring.prod.head
consTail = r.DPDK_ring.cons.tail
// The subtraction is done between two unsigned 32bits value
// (the result is always modulo 32 bits even if we have
// prodHead > consTail). So 'freeEntries' is always between 0
// and size(ring)-1.
freeEntries = (mask + consTail - prodHead)
// check that we have enough room in ring
if C.uint32_t(n) > freeEntries { //unlikely
// No free entry available
if freeEntries == 0 { //unlikely
return 0
}
n = uint(freeEntries)
}
prodNext = prodHead + C.uint32_t(n)
// I suppose we shouldn't write inline asm here but improve standart library instead.
success = atomic.CompareAndSwapUint32((*uint32)(unsafe.Pointer(&r.DPDK_ring.prod.head)), uint32(prodHead), uint32(prodNext))
}
// write entries in ring
enqueuePtrs(r, prodHead, mask, n, objTable)
// TODO GO use atomicLoad instead of barrier. rte_smp_wmb(); it is rte_compiler_barrier in x86
asm.RteCompilerWmb()
// If there are other enqueues in progress that preceded us,
// we need to wait for them to complete
for r.DPDK_ring.prod.tail != prodHead { //unlikely
C.rte_pause()
}
r.DPDK_ring.prod.tail = prodNext
return n
}
// Heavily based on DPDK mc_do_dequeue
func nffgoRingMcDoDequeue(r *C.struct_nff_go_ring, objTable []uintptr, n uint) uint {
var consHead, prodTail C.uint32_t
var consNext, entries C.uint32_t
var max = n // max should be const but can't
var success = false
var mask C.uint32_t = r.DPDK_ring.mask
// move cons.head atomically
for success == false { //unlikely
// Restore n as it may change every loop
n = max
consHead = r.DPDK_ring.cons.head
prodTail = r.DPDK_ring.prod.tail
// The subtraction is done between two unsigned 32bits value
// (the result is always modulo 32 bits even if we have
// consHead > prodTail). So 'entries' is always between 0
// and size(ring)-1.
entries = (prodTail - consHead)
// Set the actual entries for dequeue
if C.uint32_t(n) > entries {
if entries == 0 { //unlikely
return 0
}
n = uint(entries)
}
consNext = consHead + C.uint32_t(n)
// I suppose we shouldn't write inline asm here but improve standard library instead.
success = atomic.CompareAndSwapUint32((*uint32)(unsafe.Pointer(&r.DPDK_ring.cons.head)), uint32(consHead), uint32(consNext))
//success = C.rte_atomic32_cmpset(&r.C_ring.cons.head, consHead, consNext)
}
// copy in table
dequeuePtrs(r, consHead, mask, n, objTable)
// TODO rte_smp_rmb(); it is rte_compiler_barrier in x86
asm.RteCompilerRmb()
// If there are other dequeues in progress that preceded us,
// we need to wait for them to complete
for r.DPDK_ring.cons.tail != consHead { //unlikely
C.rte_pause()
}
r.DPDK_ring.cons.tail = consNext
return n
}
// Heavily based on DPDK mp_enqueue_burst
func nffgoRingMpEnqueueBurst(r *C.struct_nff_go_ring, objTable []uintptr, n uint) uint {
return nffgoRingMpDoEnqueue(r, objTable, n)
}
// Heavily based on DPDK mc_dequeue_burst
func nffgoRingMcDequeueBurst(r *C.struct_nff_go_ring, objTable []uintptr, n uint) uint {
return nffgoRingMcDoDequeue(r, objTable, n)
}
// GetRingCount gets number of objects in ring.
func (ring *Ring) GetRingCount() uint32 {
return uint32((ring.DPDK_ring.prod.tail - ring.DPDK_ring.cons.tail) & ring.DPDK_ring.mask)
}
// ReceiveRSS - get packets from port and enqueue on a Ring.
func ReceiveRSS(port uint16, inIndex []int32, OUT Rings, flag *int32, coreID int, race *int32, stats *common.RXTXStats) {
if C.rte_eth_dev_socket_id(C.uint16_t(port)) != C.int(C.rte_lcore_to_socket_id(C.uint(coreID))) {
common.LogWarning(common.Initialization, "Receive port", port, "is on remote NUMA node to polling thread - not optimal performance.")
}
C.receiveRSS(C.uint16_t(port), (*C.int32_t)(unsafe.Pointer(&(inIndex[0]))), C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(OUT[0]))), C.int32_t(len(OUT))),
(*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.int)(unsafe.Pointer(race)), (*C.RXTXStats)(unsafe.Pointer(stats)))
}
func SrKNI(port uint16, flag *int32, coreID int, recv bool, OUT Rings, send bool, IN Rings, stats *common.RXTXStats) {
var nOut *C.struct_rte_ring
var nIn **C.struct_rte_ring
if OUT != nil {
nOut = OUT[0].DPDK_ring
}
if IN != nil {
nIn = C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN)))
}
C.nff_go_KNI(C.uint16_t(port), (*C.int)(unsafe.Pointer(flag)), C.int(coreID),
C.bool(recv), nOut, C.bool(send), nIn, C.int32_t(len(IN)), (*C.RXTXStats)(unsafe.Pointer(stats)))
}
// Send - dequeue packets and send.
func Send(port uint16, IN Rings, unrestrictedClones bool, flag *int32, coreID int, stats *common.RXTXStats,
sendThreadIndex, totalSendTreads int) {
if C.rte_eth_dev_socket_id(C.uint16_t(port)) != C.int(C.rte_lcore_to_socket_id(C.uint(coreID))) {
common.LogWarning(common.Initialization, "Send port", port, "is on remote NUMA node to polling thread - not optimal performance.")
}
C.nff_go_send(C.uint16_t(port),
C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))),
C.int32_t(len(IN)),
C.bool(unrestrictedClones),
(*C.int)(unsafe.Pointer(flag)), C.int(coreID),
(*C.RXTXStats)(unsafe.Pointer(stats)),
C.int32_t(sendThreadIndex),
C.int32_t(totalSendTreads))
}
// Stop - dequeue and free packets.
func Stop(IN Rings, flag *int32, coreID int, stats *common.RXTXStats) {
C.nff_go_stop(C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))), C.int(len(IN)), (*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.RXTXStats)(unsafe.Pointer(stats)))
}
// InitDPDKArguments allocates and initializes arguments for dpdk.
func InitDPDKArguments(args []string) (C.int, **C.char) {
DPDKArgs := append([]string{}, os.Args[0])
DPDKArgs = append(DPDKArgs, "--log-level="+common.GetDPDKLogLevel())
DPDKArgs = append(DPDKArgs, args...)
argv := C.makeArgv(C.int(len(DPDKArgs)))
for i, arg := range DPDKArgs {
C.handleArgv(argv, C.CString(arg), C.int(i))
}
return C.int(len(DPDKArgs)), argv
}
// InitDPDK initializes the Environment Abstraction Layer (EAL) in DPDK.
func InitDPDK(argc C.int, argv **C.char, burstSize uint, mbufNumber uint, mbufCacheSize uint, needKNI int,
NoPacketHeadChange bool, needChainedReassembly bool, needChainedJumbo bool, needMemoryJumbo bool) error {
if needChainedReassembly && needChainedJumbo || needChainedReassembly && needMemoryJumbo || needChainedJumbo && needMemoryJumbo {
return common.WrapWithNFError(nil, "Memory jumbo, chained jumbo or IP reassembly is unsupported together\n", common.FailToInitDPDK)
}
ret := C.eal_init(argc, argv, C.uint32_t(burstSize), C.int32_t(needKNI),
C.bool(NoPacketHeadChange), C.bool(needChainedReassembly), C.bool(needChainedJumbo), C.bool(needMemoryJumbo))
if ret < 0 {
return common.WrapWithNFError(nil, "Error with EAL initialization\n", common.FailToInitDPDK)
}
if ret > 0 {
return common.WrapWithNFError(nil, "rte_eal_init can't parse all parameters\n", common.FailToInitDPDK)
}
mbufNumberT = mbufNumber
mbufCacheSizeT = mbufCacheSize
return nil
}
func StopDPDK() {
C.rte_eal_cleanup()
}
func FreeMempools() {
for i := range usedMempools {
C.rte_mempool_free(usedMempools[i].mempool)
}
usedMempools = nil
}
func StopPort(port uint16) {
C.rte_eth_dev_stop(C.uint16_t(port))
}
func FreeKNI(port uint16) error {
if C.free_kni(C.uint16_t(port)) < 0 {
return common.WrapWithNFError(nil, "Problem with KNI releasing\n", common.FailToReleaseKNI)
}
return nil
}
const (
RteMaxEthPorts = C.RTE_MAX_ETHPORTS
)
func GetNextPort(port uint16) uint16 {
return uint16(C.rte_eth_find_next_owned_by(C.uint16_t(port), C.RTE_ETH_DEV_NO_OWNER))
}
// GetPortsNumber gets total number of available Ethernet devices.
func GetPortsNumber() int {
return int(C.rte_eth_dev_count())
}
func CheckPortRSS(port uint16) int32 {
return int32(C.check_max_port_rx_queues(C.uint16_t(port)))
}
func CheckPortMaxTXQueues(port uint16) int32 {
return int32(C.check_max_port_tx_queues(C.uint16_t(port)))
}
// CreatePort initializes a new port using global settings and parameters.
func CreatePort(port uint16, willReceive bool, promiscuous bool, hwtxchecksum,
hwrxpacketstimestamp bool, inIndex int32, tXQueuesNumberPerPort int) error {
var mempools **C.struct_rte_mempool
if willReceive {
m := CreateMempools("receive", inIndex)
mempools = (**C.struct_rte_mempool)(unsafe.Pointer(&(m[0])))
} else {
mempools = nil
}
if C.port_init(C.uint16_t(port), C.bool(willReceive), mempools,
C._Bool(promiscuous), C._Bool(hwtxchecksum), C._Bool(hwrxpacketstimestamp), C.int32_t(inIndex), C.int32_t(tXQueuesNumberPerPort)) != 0 {
msg := common.LogError(common.Initialization, "Cannot init port ", port, "!")
return common.WrapWithNFError(nil, msg, common.FailToInitPort)
}
return nil
}
// CreateMempool creates and returns a new memory pool.
func CreateMempool(name string) *Mempool {
nameC := 1
tName := name
for i := range usedMempools {
if usedMempools[i].name == tName {
tName = name + strconv.Itoa(nameC)
nameC++
}
}
mempool := C.createMempool(C.uint32_t(mbufNumberT), C.uint32_t(mbufCacheSizeT))
usedMempools = append(usedMempools, mempoolPair{mempool, tName})
return (*Mempool)(mempool)
}
func CreateMempools(name string, inIndex int32) []*Mempool {
m := make([]*Mempool, inIndex, inIndex)
for i := int32(0); i < inIndex; i++ {
m[i] = CreateMempool(name)
}
return m
}
// SetAffinity sets cpu affinity mask.
func SetAffinity(coreID int) error {
// go tool trace shows that each proc executes different goroutine. However it is expected behavior
// (golang issue #20853) and each goroutine is locked to one OS thread.
runtime.LockOSThread()
var cpuset C.cpu_set_t
C.initCPUSet(C.int(coreID), &cpuset)
_, _, errno := syscall.RawSyscall(syscall.SYS_SCHED_SETAFFINITY, uintptr(0), unsafe.Sizeof(cpuset), uintptr(unsafe.Pointer(&cpuset)))
if errno != 0 {
return common.WrapWithNFError(nil, errno.Error(), common.SetAffinityErr)
}
return nil
}
// AllocateMbufs allocates n mbufs.
func AllocateMbufs(mb []uintptr, mempool *Mempool, n uint) error {
if err := C.allocateMbufs((*C.struct_rte_mempool)(mempool), (**C.struct_rte_mbuf)(unsafe.Pointer(&mb[0])), C.unsigned(n)); err != 0 {
msg := common.LogError(common.Debug, "AllocateMbufs cannot allocate mbuf, dpdk returned: ", err)
return common.WrapWithNFError(nil, msg, common.AllocMbufErr)
}
return nil
}
// AllocateMbuf allocates one mbuf.
func AllocateMbuf(mb *uintptr, mempool *Mempool) error {
if err := C.allocateMbufs((*C.struct_rte_mempool)(mempool), (**C.struct_rte_mbuf)(unsafe.Pointer(mb)), 1); err != 0 {
msg := common.LogError(common.Debug, "AllocateMbuf cannot allocate mbuf, dpdk returned: ", err)
return common.WrapWithNFError(nil, msg, common.AllocMbufErr)
}
return nil
}
// WriteDataToMbuf copies data to mbuf.
func WriteDataToMbuf(mb *Mbuf, data []byte) {
d := unsafe.Pointer(GetPacketDataStartPointer(mb))
slice := (*[types.MaxLength]byte)(d)[:len(data)] // copy requires slice
//TODO need to investigate maybe we need to use C function C.rte_memcpy here
copy(slice, data)
}
// GetRawPacketBytesMbuf returns raw data from packet.
func GetRawPacketBytesMbuf(mb *Mbuf) []byte {
dataLen := uintptr(mb.data_len)
dataPtr := uintptr(mb.buf_addr) + uintptr(mb.data_off)
return (*[1 << 30]byte)(unsafe.Pointer(dataPtr))[:dataLen]
}
// GetPktLenMbuf returns amount of data in a given chain of Mbufs - whole packet
func GetPktLenMbuf(mb *Mbuf) uint {
return uint(mb.pkt_len)
}
// GetDataLenMbuf returns amount of data in a given Mbuf - one segment if scattered
func GetDataLenMbuf(mb *Mbuf) uint {
return uint(mb.data_len)
}
// Statistics print statistics about current
// speed of stop ring, recv/send speed and drops.
func Statistics(N float32) {
C.statistics(C.float(N))
}
// PortStatistics print statistics about NIC port.
func PortStatistics(port uint16) {
C.portStatistics(C.uint16_t(port))
}
// ReportMempoolsState prints used and free space of mempools.
func ReportMempoolsState() {
for _, m := range usedMempools {
use := C.getMempoolSpace(m.mempool)
common.LogDebug(common.Verbose, "Mempool usage", m.name, use, "from", mbufNumberT)
if float32(mbufNumberT-uint(use))/float32(mbufNumberT)*100 < 10 {
common.LogDrop(common.Debug, m.name, "mempool has less than 10% free space. This can lead to dropping packets while receive.")
}
}
}
// CreateKni creates a KNI device
func CreateKni(portId uint16, core uint, name string) error {
mempool := (*C.struct_rte_mempool)(CreateMempool("KNI"))
if C.create_kni(C.uint16_t(portId), C.uint32_t(core), C.CString(name), mempool) != 0 {
return common.WrapWithNFError(nil, "Error with KNI allocation\n", common.FailToCreateKNI)
}
return nil
}
// CreateLPM creates LPM table
// We can't reuse any structures like rte_lpm or rte_lpm_tbl_entry due to
// CGO problems with bitfields (uint24 compile time error).
// We we pass pointers via "unsafe.Pointer" and "void*"
// C function will return pointers to correct rte_lpm fields to tbl24 and tbl8,
// so we shouldn't worry about rte_lpm changings. However we use indexing
// while lookup, so we will need to check our sizes if DPDK changes rte_lpm_tbl_entry struct size
func CreateLPM(name string, socket uint8, maxRules uint32, numberTbl8 uint32, tbl24 unsafe.Pointer, tbl8 unsafe.Pointer) unsafe.Pointer {
return unsafe.Pointer(C.lpm_create(C.CString(name), C.int(socket), C.uint32_t(maxRules), C.uint32_t(numberTbl8),
(**[1]C.uint32_t)(tbl24), (**[1]C.uint32_t)(tbl8)))
}
// AddLPMRule adds one rule to LPM table
func AddLPMRule(lpm unsafe.Pointer, ip types.IPv4Address, depth uint8, nextHop types.IPv4Address) int {
return int(C.lpm_add(lpm, C.uint32_t(ip), C.uint8_t(depth), C.uint32_t(nextHop)))
}
// DeleteLPMRule removes one rule from LPM table
func DeleteLPMRule(lpm unsafe.Pointer, ip types.IPv4Address, depth uint8) int {
return int(C.lpm_delete(lpm, C.uint32_t(ip), C.uint8_t(depth)))
}
// FreeLPM frees lpm structure
func FreeLPM(lpm unsafe.Pointer) {
C.lpm_free(lpm)
}
func BoolToInt(value bool) uint8 {
return *((*uint8)(unsafe.Pointer(&value)))
}
func IntArrayToBool(value *[32]uint8) *[32]bool {
return (*[32]bool)(unsafe.Pointer(value))
}
func CheckHWTXChecksumCapability(port uint16) bool {
return bool(C.check_hwtxchecksum_capability(C.uint16_t(port)))
}
func CheckHWRXPacketsTimestamp(port uint16) bool {
return bool(C.check_hwrxpackets_timestamp_capability(C.uint16_t(port)))
}
func InitDevice(device string) int {
return int(C.initDevice(C.CString(device)))
}
func ReceiveOS(socket int, OUT *Ring, flag *int32, coreID int, stats *common.RXTXStats) {
m := CreateMempool("receiveOS")
C.receiveOS(C.int(socket), OUT.DPDK_ring, (*C.struct_rte_mempool)(unsafe.Pointer(m)),
(*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.RXTXStats)(unsafe.Pointer(stats)))
}
func SendOS(socket int, IN Rings, flag *int32, coreID int, stats *common.RXTXStats) {
C.sendOS(C.int(socket), C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))),
C.int32_t(len(IN))), C.int32_t(len(IN)), (*C.int)(unsafe.Pointer(flag)), C.int(coreID),
(*C.RXTXStats)(unsafe.Pointer(stats)))
}
func SetCountersEnabledInApplication(enabled bool) {
C.counters_enabled_in_application = C.bool(true)
}
func SetNextMbuf(next *Mbuf, prev *Mbuf) {
prev.next = (*C.struct_rte_mbuf)(next)
}
func GetPacketOffloadFlags(mb *Mbuf) uint64 {
return uint64(mb.ol_flags)
}
func GetPacketTimestamp(mb *Mbuf) uint64 {
return uint64(mb.timestamp)
}
type XDPSocket *C.struct_xsk_socket_info
func InitXDP(device string, queue int) XDPSocket {
return C.initXDP(C.CString(device), C.int(queue))
}
func ReceiveXDP(socket XDPSocket, OUT *Ring, flag *int32, coreID int, stats *common.RXTXStats) {
m := CreateMempool("receiveXDP")
C.receiveXDP(socket, OUT.DPDK_ring, (*C.struct_rte_mempool)(unsafe.Pointer(m)),
(*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.RXTXStats)(unsafe.Pointer(stats)))
}
func SendXDP(socket XDPSocket, IN Rings, flag *int32, coreID int, stats *common.RXTXStats) {
C.sendXDP(socket, C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))),
C.int32_t(len(IN))), C.int32_t(len(IN)), (*C.int)(unsafe.Pointer(flag)), C.int(coreID),
(*C.RXTXStats)(unsafe.Pointer(stats)))
}