Add support for jumbo frames to libmemif
[govpp.git] / extras / libmemif / adapter.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //       http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // +build !windows,!darwin
16
17 package libmemif
18
19 import (
20         "encoding/binary"
21         "os"
22         "sync"
23         "syscall"
24         "unsafe"
25
26         logger "github.com/sirupsen/logrus"
27 )
28
29 /*
30 #cgo LDFLAGS: -lmemif
31
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <sys/eventfd.h>
37 #include <libmemif.h>
38
39 // Feature tests.
40 #ifndef MEMIF_HAVE_CANCEL_POLL_EVENT
41 // memif_cancel_poll_event that simply returns ErrUnsupported.
42 static int
43 memif_cancel_poll_event ()
44 {
45         return 102; // ErrUnsupported
46 }
47 #endif
48
49 // govpp_memif_conn_args_t replaces fixed sized arrays with C-strings which
50 // are much easier to work with in cgo.
51 typedef struct
52 {
53         char *socket_filename;
54         char *secret;
55         uint8_t num_s2m_rings;
56         uint8_t num_m2s_rings;
57         uint16_t buffer_size;
58         uint8_t log2_ring_size;
59         uint8_t is_master;
60         uint32_t interface_id;
61         char *interface_name;
62         memif_interface_mode_t mode;
63 } govpp_memif_conn_args_t;
64
65 // govpp_memif_details_t replaces strings represented with (uint8_t *)
66 // to the standard and easy to work with in cgo: (char *)
67 typedef struct
68 {
69         char *if_name;
70         char *inst_name;
71         char *remote_if_name;
72         char *remote_inst_name;
73         uint32_t id;
74         char *secret;
75         uint8_t role;
76         uint8_t mode;
77         char *socket_filename;
78         uint8_t rx_queues_num;
79         uint8_t tx_queues_num;
80         memif_queue_details_t *rx_queues;
81         memif_queue_details_t *tx_queues;
82         uint8_t link_up_down;
83 } govpp_memif_details_t;
84
85 extern int go_on_connect_callback(void *privateCtx);
86 extern int go_on_disconnect_callback(void *privateCtx);
87
88 // Callbacks strip the connection handle away.
89
90 static int
91 govpp_on_connect_callback(memif_conn_handle_t conn, void *private_ctx)
92 {
93         return go_on_connect_callback(private_ctx);
94 }
95
96 static int
97 govpp_on_disconnect_callback(memif_conn_handle_t conn, void *private_ctx)
98 {
99         return go_on_disconnect_callback(private_ctx);
100 }
101
102 // govpp_memif_create uses govpp_memif_conn_args_t.
103 static int
104 govpp_memif_create (memif_conn_handle_t *conn, govpp_memif_conn_args_t *go_args,
105                     void *private_ctx)
106 {
107         memif_conn_args_t args;
108         memset (&args, 0, sizeof (args));
109         args.socket_filename = (char *)go_args->socket_filename;
110         if (go_args->secret != NULL)
111         {
112                 strncpy ((char *)args.secret, go_args->secret,
113                                  sizeof (args.secret) - 1);
114         }
115         args.num_s2m_rings = go_args->num_s2m_rings;
116         args.num_m2s_rings = go_args->num_m2s_rings;
117         args.buffer_size = go_args->buffer_size;
118         args.log2_ring_size = go_args->log2_ring_size;
119         args.is_master = go_args->is_master;
120         args.interface_id = go_args->interface_id;
121         if (go_args->interface_name != NULL)
122         {
123                 strncpy ((char *)args.interface_name, go_args->interface_name,
124                                  sizeof(args.interface_name) - 1);
125         }
126         args.mode = go_args->mode;
127
128         return memif_create(conn, &args, govpp_on_connect_callback,
129                                                 govpp_on_disconnect_callback, NULL,
130                                                 private_ctx);
131 }
132
133 // govpp_memif_get_details keeps reallocating buffer until it is large enough.
134 // The buffer is returned to be deallocated when it is no longer needed.
135 static int
136 govpp_memif_get_details (memif_conn_handle_t conn, govpp_memif_details_t *govpp_md,
137                          char **buf)
138 {
139         int rv = 0;
140         size_t buflen = 1 << 7;
141         char *buffer = NULL, *new_buffer = NULL;
142         memif_details_t md = {0};
143
144         do {
145                 // initial malloc (256 bytes) or realloc
146                 buflen <<= 1;
147                 new_buffer = realloc(buffer, buflen);
148                 if (new_buffer == NULL)
149                 {
150                         free(buffer);
151                         return MEMIF_ERR_NOMEM;
152                 }
153                 buffer = new_buffer;
154                 // try to get details
155                 rv = memif_get_details(conn, &md, buffer, buflen);
156         } while (rv == MEMIF_ERR_NOBUF_DET);
157
158         if (rv == 0)
159         {
160                 *buf = buffer;
161                 govpp_md->if_name = (char *)md.if_name;
162                 govpp_md->inst_name = (char *)md.inst_name;
163                 govpp_md->remote_if_name = (char *)md.remote_if_name;
164                 govpp_md->remote_inst_name = (char *)md.remote_inst_name;
165                 govpp_md->id = md.id;
166                 govpp_md->secret = (char *)md.secret;
167                 govpp_md->role = md.role;
168                 govpp_md->mode = md.mode;
169                 govpp_md->socket_filename = (char *)md.socket_filename;
170                 govpp_md->rx_queues_num = md.rx_queues_num;
171                 govpp_md->tx_queues_num = md.tx_queues_num;
172                 govpp_md->rx_queues = md.rx_queues;
173                 govpp_md->tx_queues = md.tx_queues;
174                 govpp_md->link_up_down = md.link_up_down;
175         }
176         else
177                 free(buffer);
178         return rv;
179 }
180
181 // Used to avoid cumbersome tricks that use unsafe.Pointer() + unsafe.Sizeof()
182 // or even cast C-array directly into Go-slice.
183 static memif_queue_details_t
184 govpp_get_rx_queue_details (govpp_memif_details_t *md, int index)
185 {
186         return md->rx_queues[index];
187 }
188
189 // Used to avoid cumbersome tricks that use unsafe.Pointer() + unsafe.Sizeof()
190 // or even cast C-array directly into Go-slice.
191 static memif_queue_details_t
192 govpp_get_tx_queue_details (govpp_memif_details_t *md, int index)
193 {
194         return md->tx_queues[index];
195 }
196
197 // Copy packet data into the selected buffer with splitting when necessary
198 static void
199 govpp_copy_packet_data(memif_buffer_t *buffers, uint16_t allocated, int bufIndex, void *packetData, uint16_t packetSize)
200 {
201         int dataOffset = 0;
202
203         do {
204                 buffers[bufIndex].len = (packetSize > buffers[bufIndex].len ? buffers[bufIndex].len : packetSize);
205                 void * curData = (packetData + dataOffset);
206                 memcpy(buffers[bufIndex].data, curData, (size_t)buffers[bufIndex].len);
207                 dataOffset += buffers[bufIndex].len;
208                 bufIndex += 1;
209                 packetSize -= buffers[bufIndex].len;
210         } while(packetSize > 0 && bufIndex < allocated && buffers[bufIndex].flags & MEMIF_BUFFER_FLAG_NEXT > 0);
211 }
212
213 // Get packet data from the selected buffer.
214 // Used to avoid an ugly unsafe.Pointer() + unsafe.Sizeof().
215 static void *
216 govpp_get_packet_data(memif_buffer_t *buffers, int index, int *size)
217 {
218         *size = (int)buffers[index].len;
219         return buffers[index].data;
220 }
221
222 // Checks if memif buffer is chained
223 static int
224 govpp_is_buffer_chained(memif_buffer_t *buffers, int index)
225 {
226     return buffers[index].flags & MEMIF_BUFFER_FLAG_NEXT;
227 }
228
229 // Allocate memif buffers and return pointer to next free buffer
230 static int
231 govpp_memif_buffer_alloc(memif_conn_handle_t conn, uint16_t qid,
232                         memif_buffer_t * bufs, uint16_t offset, memif_buffer_t ** nextFreeBuf,
233                         uint16_t count, uint16_t * count_out, uint16_t size)
234 {
235     memif_buffer_t * offsetBufs = (bufs + offset);
236     int err = memif_buffer_alloc(conn, qid, offsetBufs, count, count_out, size);
237     *count_out += offset;
238     *nextFreeBuf = offsetBufs;
239     return err;
240 }
241
242 */
243 import "C"
244
245 // IfMode represents the mode (layer/behaviour) in which the interface operates.
246 type IfMode int
247
248 const (
249         // IfModeEthernet tells memif to operate on the L2 layer.
250         IfModeEthernet IfMode = iota
251
252         // IfModeIP tells memif to operate on the L3 layer.
253         IfModeIP
254
255         // IfModePuntInject tells memif to behave as Inject/Punt interface.
256         IfModePuntInject
257 )
258
259 // RxMode is used to switch between polling and interrupt for RX.
260 type RxMode int
261
262 const (
263         // RxModeInterrupt tells libmemif to send interrupt signal when data are available.
264         RxModeInterrupt RxMode = iota
265
266         // RxModePolling means that the user needs to explicitly poll for data on RX
267         // queues.
268         RxModePolling
269 )
270
271 // RawPacketData represents raw packet data. libmemif doesn't care what the
272 // actual content is, it only manipulates with raw bytes.
273 type RawPacketData []byte
274
275 // MemifMeta is used to store a basic memif metadata needed for identification
276 // and connection establishment.
277 type MemifMeta struct {
278         // IfName is the interface name. Has to be unique across all created memifs.
279         // Interface name is truncated if needed to have no more than 32 characters.
280         IfName string
281
282         // InstanceName identifies the endpoint. If omitted, the application
283         // name passed to Init() will be used instead.
284         // Instance name is truncated if needed to have no more than 32 characters.
285         InstanceName string
286
287         // ConnID is a connection ID used to match opposite sides of the memif
288         // connection.
289         ConnID uint32
290
291         // SocketFilename is the filename of the AF_UNIX socket through which
292         // the connection is established.
293         // The string is truncated if neede to fit into sockaddr_un.sun_path
294         // (108 characters on Linux).
295         SocketFilename string
296
297         // Secret must be the same on both sides for the authentication to succeed.
298         // Empty string is allowed.
299         // The secret is truncated if needed to have no more than 24 characters.
300         Secret string
301
302         // IsMaster is set to true if memif operates in the Master mode.
303         IsMaster bool
304
305         // Mode is the mode (layer/behaviour) in which the memif operates.
306         Mode IfMode
307 }
308
309 // MemifShmSpecs is used to store the specification of the shared memory segment
310 // used by memif to send/receive packets.
311 type MemifShmSpecs struct {
312         // NumRxQueues is the number of Rx queues.
313         // Default is 1 (used if the value is 0).
314         NumRxQueues uint8
315
316         // NumTxQueues is the number of Tx queues.
317         // Default is 1 (used if the value is 0).
318         NumTxQueues uint8
319
320         // BufferSize is the size of the buffer to hold one packet, or a single
321         // fragment of a jumbo frame. Default is 2048 (used if the value is 0).
322         BufferSize uint16
323
324         // Log2RingSize is the number of items in the ring represented through
325         // the logarithm base 2.
326         // Default is 10 (used if the value is 0).
327         Log2RingSize uint8
328 }
329
330 // MemifConfig is the memif configuration.
331 // Used as the input argument to CreateInterface().
332 // It is the slave's config that mostly decides the parameters of the connection,
333 // but master may limit some of the quantities if needed (based on the memif
334 // protocol or master's configuration)
335 type MemifConfig struct {
336         MemifMeta
337         MemifShmSpecs
338 }
339
340 // ConnUpdateCallback is a callback type declaration used with callbacks
341 // related to connection status changes.
342 type ConnUpdateCallback func(memif *Memif) (err error)
343
344 // MemifCallbacks is a container for all callbacks provided by memif.
345 // Any callback can be nil, in which case it will be simply skipped.
346 // Important: Do not call CreateInterface() or Memif.Close() from within a callback
347 // or a deadlock will occur. Instead send signal through a channel to another
348 // go routine which will be able to create/remove memif interface(s).
349 type MemifCallbacks struct {
350         // OnConnect is triggered when a connection for a given memif was established.
351         OnConnect ConnUpdateCallback
352
353         // OnDisconnect is triggered when a connection for a given memif was lost.
354         OnDisconnect ConnUpdateCallback
355 }
356
357 // Memif represents a single memif interface. It provides methods to send/receive
358 // packets in bursts in either the polling mode or in the interrupt mode with
359 // the help of golang channels.
360 type Memif struct {
361         MemifMeta
362
363         // Per-library references
364         ifIndex int                   // index used in the Go-libmemif context (Context.memifs)
365         cHandle C.memif_conn_handle_t // handle used in C-libmemif
366
367         // Callbacks
368         callbacks *MemifCallbacks
369
370         // Interrupt
371         intCh      chan uint8      // memif-global interrupt channel (value = queue ID)
372         queueIntCh []chan struct{} // per RX queue interrupt channel
373
374         // Rx/Tx queues
375         ringSize    int              // number of items in each ring
376         bufferSize  int              // max buffer size
377         stopQPollFd int              // event file descriptor used to stop pollRxQueue-s
378         wg          sync.WaitGroup   // wait group for all pollRxQueue-s
379         rxQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each RX queue
380         txQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each TX queue
381 }
382
383 // MemifDetails provides a detailed runtime information about a memif interface.
384 type MemifDetails struct {
385         MemifMeta
386         MemifConnDetails
387 }
388
389 // MemifConnDetails provides a detailed runtime information about a memif
390 // connection.
391 type MemifConnDetails struct {
392         // RemoteIfName is the name of the memif on the opposite side.
393         RemoteIfName string
394         // RemoteInstanceName is the name of the endpoint on the opposite side.
395         RemoteInstanceName string
396         // HasLink is true if the connection has link (= is established and functional).
397         HasLink bool
398         // RxQueues contains details for each Rx queue.
399         RxQueues []MemifQueueDetails
400         // TxQueues contains details for each Tx queue.
401         TxQueues []MemifQueueDetails
402 }
403
404 // MemifQueueDetails provides a detailed runtime information about a memif queue.
405 // Queue = Ring + the associated buffers (one directional).
406 type MemifQueueDetails struct {
407         // QueueID is the ID of the queue.
408         QueueID uint8
409         // RingSize is the number of slots in the ring (not logarithmic).
410         RingSize uint32
411         // BufferSize is the size of each buffer pointed to from the ring slots.
412         BufferSize uint16
413         /* Further ring information TO-BE-ADDED when C-libmemif supports them. */
414 }
415
416 // CPacketBuffers stores an array of memif buffers for use with TxBurst or RxBurst.
417 type CPacketBuffers struct {
418         buffers *C.memif_buffer_t
419         count   int
420         rxChainBuf []RawPacketData
421 }
422
423 // Context is a global Go-libmemif runtime context.
424 type Context struct {
425         lock           sync.RWMutex
426         initialized    bool
427         memifs         map[int] /* ifIndex */ *Memif /* slice of all active memif interfaces */
428         nextMemifIndex int
429
430         wg sync.WaitGroup /* wait-group for pollEvents() */
431 }
432
433 type txPacketBuffer struct {
434         packets []RawPacketData
435         size    int
436 }
437
438 var (
439         // logger used by the adapter.
440         log *logger.Logger
441
442         // Global Go-libmemif context.
443         context = &Context{initialized: false}
444 )
445
446 // init initializes global logger, which logs debug level messages to stdout.
447 func init() {
448         log = logger.New()
449         log.Out = os.Stdout
450         log.Level = logger.DebugLevel
451 }
452
453 // SetLogger changes the logger for Go-libmemif to the provided one.
454 // The logger is not used for logging of C-libmemif.
455 func SetLogger(l *logger.Logger) {
456         log = l
457 }
458
459 // Init initializes the libmemif library. Must by called exactly once and before
460 // any libmemif functions. Do not forget to call Cleanup() before exiting
461 // your application.
462 // <appName> should be a human-readable string identifying your application.
463 // For example, VPP returns the version information ("show version" from VPP CLI).
464 func Init(appName string) error {
465         context.lock.Lock()
466         defer context.lock.Unlock()
467
468         if context.initialized {
469                 return ErrAlreadyInit
470         }
471
472         log.Debug("Initializing libmemif library")
473
474         // Initialize C-libmemif.
475         var errCode int
476         if appName == "" {
477                 errCode = int(C.memif_init(nil, nil, nil, nil))
478         } else {
479                 appName := C.CString(appName)
480                 defer C.free(unsafe.Pointer(appName))
481                 errCode = int(C.memif_init(nil, appName, nil, nil))
482         }
483         err := getMemifError(errCode)
484         if err != nil {
485                 return err
486         }
487
488         // Initialize the map of memory interfaces.
489         context.memifs = make(map[int]*Memif)
490
491         // Start event polling.
492         context.wg.Add(1)
493         go pollEvents()
494
495         context.initialized = true
496         log.Debug("libmemif library was initialized")
497         return err
498 }
499
500 // Cleanup cleans up all the resources allocated by libmemif.
501 func Cleanup() error {
502         context.lock.Lock()
503         defer context.lock.Unlock()
504
505         if !context.initialized {
506                 return ErrNotInit
507         }
508
509         log.Debug("Closing libmemif library")
510
511         // Delete all active interfaces.
512         for _, memif := range context.memifs {
513                 memif.Close()
514         }
515
516         // Stop the event loop (if supported by C-libmemif).
517         errCode := C.memif_cancel_poll_event()
518         err := getMemifError(int(errCode))
519         if err == nil {
520                 log.Debug("Waiting for pollEvents() to stop...")
521                 context.wg.Wait()
522                 log.Debug("pollEvents() has stopped...")
523         } else {
524                 log.WithField("err", err).Debug("NOT Waiting for pollEvents to stop...")
525         }
526
527         // Run cleanup for C-libmemif.
528         err = getMemifError(int(C.memif_cleanup()))
529         if err == nil {
530                 context.initialized = false
531                 log.Debug("libmemif library was closed")
532         }
533         return err
534 }
535
536 // CreateInterface creates a new memif interface with the given configuration.
537 // The same callbacks can be used with multiple memifs. The first callback input
538 // argument (*Memif) can be used to tell which memif the callback was triggered for.
539 // The method is thread-safe.
540 func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Memif, err error) {
541         context.lock.Lock()
542         defer context.lock.Unlock()
543
544         if !context.initialized {
545                 return nil, ErrNotInit
546         }
547
548         log.WithField("ifName", config.IfName).Debug("Creating a new memif interface")
549
550         log2RingSize := config.Log2RingSize
551         if log2RingSize == 0 {
552                 log2RingSize = 10
553         }
554
555         bufferSize := config.BufferSize
556         if bufferSize <= 0 {
557                 bufferSize = 2048
558         }
559
560         // Create memif-wrapper for Go-libmemif.
561         memif = &Memif{
562                 MemifMeta:  config.MemifMeta,
563                 callbacks:  &MemifCallbacks{},
564                 ifIndex:    context.nextMemifIndex,
565                 ringSize:   1 << log2RingSize,
566                 bufferSize: int(bufferSize),
567         }
568
569         // Initialize memif callbacks.
570         if callbacks != nil {
571                 memif.callbacks.OnConnect = callbacks.OnConnect
572                 memif.callbacks.OnDisconnect = callbacks.OnDisconnect
573         }
574
575         // Initialize memif-global interrupt channel.
576         memif.intCh = make(chan uint8, 1<<6)
577
578         // Initialize event file descriptor for stopping Rx/Tx queue polling.
579         memif.stopQPollFd = int(C.eventfd(0, C.EFD_NONBLOCK))
580         if memif.stopQPollFd < 0 {
581                 return nil, ErrSyscall
582         }
583
584         // Initialize memif input arguments.
585         args := &C.govpp_memif_conn_args_t{}
586         // - socket file name
587         if config.SocketFilename != "" {
588                 args.socket_filename = C.CString(config.SocketFilename)
589                 defer C.free(unsafe.Pointer(args.socket_filename))
590         }
591         // - interface ID
592         args.interface_id = C.uint32_t(config.ConnID)
593         // - interface name
594         if config.IfName != "" {
595                 args.interface_name = C.CString(config.IfName)
596                 defer C.free(unsafe.Pointer(args.interface_name))
597         }
598         // - mode
599         switch config.Mode {
600         case IfModeEthernet:
601                 args.mode = C.MEMIF_INTERFACE_MODE_ETHERNET
602         case IfModeIP:
603                 args.mode = C.MEMIF_INTERFACE_MODE_IP
604         case IfModePuntInject:
605                 args.mode = C.MEMIF_INTERFACE_MODE_PUNT_INJECT
606         default:
607                 args.mode = C.MEMIF_INTERFACE_MODE_ETHERNET
608         }
609         // - secret
610         if config.Secret != "" {
611                 args.secret = C.CString(config.Secret)
612                 defer C.free(unsafe.Pointer(args.secret))
613         }
614         // - master/slave flag + number of Rx/Tx queues
615         if config.IsMaster {
616                 args.num_s2m_rings = C.uint8_t(config.NumRxQueues)
617                 args.num_m2s_rings = C.uint8_t(config.NumTxQueues)
618                 args.is_master = C.uint8_t(1)
619         } else {
620                 args.num_s2m_rings = C.uint8_t(config.NumTxQueues)
621                 args.num_m2s_rings = C.uint8_t(config.NumRxQueues)
622                 args.is_master = C.uint8_t(0)
623         }
624         // - buffer size
625         args.buffer_size = C.uint16_t(config.BufferSize)
626         // - log_2(ring size)
627         args.log2_ring_size = C.uint8_t(config.Log2RingSize)
628
629         // Create memif in C-libmemif.
630         errCode := C.govpp_memif_create(&memif.cHandle, args, unsafe.Pointer(uintptr(memif.ifIndex)))
631         err = getMemifError(int(errCode))
632         if err != nil {
633                 return nil, err
634         }
635
636         // Register the new memif.
637         context.memifs[memif.ifIndex] = memif
638         context.nextMemifIndex++
639         log.WithField("ifName", config.IfName).Debug("A new memif interface was created")
640
641         return memif, nil
642 }
643
644 // GetInterruptChan returns a channel which is continuously being filled with
645 // IDs of queues with data ready to be received.
646 // Since there is only one interrupt signal sent for an entire burst of packets,
647 // an interrupt handling routine should repeatedly call RxBurst() until
648 // the function returns an empty slice of packets. This way it is ensured
649 // that there are no packets left on the queue unread when the interrupt signal
650 // is cleared.
651 // The method is thread-safe.
652 func (memif *Memif) GetInterruptChan() (ch <-chan uint8 /* queue ID */) {
653         return memif.intCh
654 }
655
656 // GetQueueInterruptChan returns an empty-data channel which fires every time
657 // there are data to read on a given queue.
658 // It is only valid to call this function if memif is in the connected state.
659 // Channel is automatically closed when the connection goes down (but after
660 // the user provided callback OnDisconnect has executed).
661 // Since there is only one interrupt signal sent for an entire burst of packets,
662 // an interrupt handling routine should repeatedly call RxBurst() until
663 // the function returns an empty slice of packets. This way it is ensured
664 // that there are no packets left on the queue unread when the interrupt signal
665 // is cleared.
666 // The method is thread-safe.
667 func (memif *Memif) GetQueueInterruptChan(queueID uint8) (ch <-chan struct{}, err error) {
668         if int(queueID) >= len(memif.queueIntCh) {
669                 return nil, ErrQueueID
670         }
671         return memif.queueIntCh[queueID], nil
672 }
673
674 // SetRxMode allows to switch between the interrupt and the polling mode for Rx.
675 // The method is thread-safe.
676 func (memif *Memif) SetRxMode(queueID uint8, rxMode RxMode) (err error) {
677         var cRxMode C.memif_rx_mode_t
678         switch rxMode {
679         case RxModeInterrupt:
680                 cRxMode = C.MEMIF_RX_MODE_INTERRUPT
681         case RxModePolling:
682                 cRxMode = C.MEMIF_RX_MODE_POLLING
683         default:
684                 cRxMode = C.MEMIF_RX_MODE_INTERRUPT
685         }
686         errCode := C.memif_set_rx_mode(memif.cHandle, cRxMode, C.uint16_t(queueID))
687         return getMemifError(int(errCode))
688 }
689
690 // GetDetails returns a detailed runtime information about this memif.
691 // The method is thread-safe.
692 func (memif *Memif) GetDetails() (details *MemifDetails, err error) {
693         cDetails := C.govpp_memif_details_t{}
694         var buf *C.char
695
696         // Get memif details from C-libmemif.
697         errCode := C.govpp_memif_get_details(memif.cHandle, &cDetails, &buf)
698         err = getMemifError(int(errCode))
699         if err != nil {
700                 return nil, err
701         }
702         defer C.free(unsafe.Pointer(buf))
703
704         // Convert details from C to Go.
705         details = &MemifDetails{}
706         // - metadata:
707         details.IfName = C.GoString(cDetails.if_name)
708         details.InstanceName = C.GoString(cDetails.inst_name)
709         details.ConnID = uint32(cDetails.id)
710         details.SocketFilename = C.GoString(cDetails.socket_filename)
711         if cDetails.secret != nil {
712                 details.Secret = C.GoString(cDetails.secret)
713         }
714         details.IsMaster = cDetails.role == C.uint8_t(0)
715         switch cDetails.mode {
716         case C.MEMIF_INTERFACE_MODE_ETHERNET:
717                 details.Mode = IfModeEthernet
718         case C.MEMIF_INTERFACE_MODE_IP:
719                 details.Mode = IfModeIP
720         case C.MEMIF_INTERFACE_MODE_PUNT_INJECT:
721                 details.Mode = IfModePuntInject
722         default:
723                 details.Mode = IfModeEthernet
724         }
725         // - connection details:
726         details.RemoteIfName = C.GoString(cDetails.remote_if_name)
727         details.RemoteInstanceName = C.GoString(cDetails.remote_inst_name)
728         details.HasLink = cDetails.link_up_down == C.uint8_t(1)
729         // - RX queues:
730         var i uint8
731         for i = 0; i < uint8(cDetails.rx_queues_num); i++ {
732                 cRxQueue := C.govpp_get_rx_queue_details(&cDetails, C.int(i))
733                 queueDetails := MemifQueueDetails{
734                         QueueID:    uint8(cRxQueue.qid),
735                         RingSize:   uint32(cRxQueue.ring_size),
736                         BufferSize: uint16(cRxQueue.buffer_size),
737                 }
738                 details.RxQueues = append(details.RxQueues, queueDetails)
739         }
740         // - TX queues:
741         for i = 0; i < uint8(cDetails.tx_queues_num); i++ {
742                 cTxQueue := C.govpp_get_tx_queue_details(&cDetails, C.int(i))
743                 queueDetails := MemifQueueDetails{
744                         QueueID:    uint8(cTxQueue.qid),
745                         RingSize:   uint32(cTxQueue.ring_size),
746                         BufferSize: uint16(cTxQueue.buffer_size),
747                 }
748                 details.TxQueues = append(details.TxQueues, queueDetails)
749         }
750
751         return details, nil
752 }
753
754 // TxBurst is used to send multiple packets in one call into a selected queue.
755 // The actual number of packets sent may be smaller and is returned as <count>.
756 // The method is non-blocking even if the ring is full and no packet can be sent.
757 // It is only valid to call this function if memif is in the connected state.
758 // Multiple TxBurst-s can run concurrently provided that each targets a different
759 // TX queue.
760 func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint16, err error) {
761         if len(packets) == 0 {
762                 return 0, nil
763         }
764
765         if int(queueID) >= len(memif.txQueueBufs) {
766                 return 0, ErrQueueID
767         }
768
769         var bufCount int
770         var buffers []*txPacketBuffer
771         cQueueID := C.uint16_t(queueID)
772
773         for _, packet := range packets {
774                 packetLen := len(packet)
775                 log.Debugf("%v - preparing packet with len %v", cQueueID, packetLen)
776
777                 if packetLen > memif.bufferSize {
778                         // Create jumbo buffer
779                         buffer := &txPacketBuffer{
780                                 size:    packetLen,
781                                 packets: []RawPacketData{packet},
782                         }
783
784                         buffers = append(buffers, buffer)
785
786                         // Increment bufCount by number of splits in this jumbo
787                         bufCount += (buffer.size + memif.bufferSize - 1) / memif.bufferSize
788                 } else {
789                         buffersLen := len(buffers)
790
791                         // This is very first buffer so there is no data to append to, prepare empty one
792                         if buffersLen == 0 {
793                                 buffers = []*txPacketBuffer{{}}
794                                 buffersLen = 1
795                         }
796
797                         lastBuffer := buffers[buffersLen-1]
798
799                         // Last buffer is jumbo buffer, create new buffer
800                         if lastBuffer.size > memif.bufferSize {
801                                 lastBuffer = &txPacketBuffer{}
802                                 buffers = append(buffers, lastBuffer)
803                         }
804
805                         // Determine buffer size by max packet size in buffer
806                         if packetLen > lastBuffer.size {
807                                 lastBuffer.size = packetLen
808                         }
809
810                         lastBuffer.packets = append(lastBuffer.packets, packet)
811                         bufCount += 1
812                 }
813         }
814
815         // Reallocate Tx buffers if needed to fit the input packets.
816         log.Debugf("%v - total buffer to allocate count %v", cQueueID, bufCount)
817         pb := &memif.txQueueBufs[queueID]
818         if pb.count < bufCount {
819                 newBuffers := C.realloc(unsafe.Pointer(pb.buffers), C.size_t(bufCount*int(C.sizeof_memif_buffer_t)))
820                 if newBuffers == nil {
821                         // Realloc failed, <count> will be less than len(packets).
822                         bufCount = pb.count
823                 } else {
824                         pb.buffers = (*C.memif_buffer_t)(newBuffers)
825                         pb.count = bufCount
826                 }
827         }
828
829         // Allocate ring slots.
830         var allocated C.uint16_t
831         var subCount C.uint16_t
832         for _, buffer := range buffers {
833                 packetCount := C.uint16_t(len(buffer.packets))
834                 isJumbo := buffer.size > memif.bufferSize
835
836                 log.Debugf("%v - trying to send max buff size %v, packets len %v, buffer len %v, jumbo %v",
837                         cQueueID, buffer.size, len(buffer.packets), packetCount, isJumbo)
838
839                 var nextFreeBuff *C.memif_buffer_t
840                 startOffset := allocated
841                 errCode := C.govpp_memif_buffer_alloc(memif.cHandle, cQueueID, pb.buffers, startOffset, &nextFreeBuff,
842                         packetCount, &allocated, C.uint16_t(buffer.size))
843
844                 err = getMemifError(int(errCode))
845                 endEarly := err == ErrNoBufRing
846                 if endEarly {
847                         // Not enough ring slots, <count> will be less than packetCount.
848                         err = nil
849                 }
850                 if err != nil {
851                         return 0, err
852                 }
853
854                 // Copy packet data into the buffers.
855                 nowAllocated := allocated - startOffset
856                 toFill := nowAllocated
857                 if !isJumbo {
858                         // If this is not jumbo frame, only 1 packet needs to be copied each iteration
859                         toFill = 1
860                 }
861
862                 // Iterate over all packets and try to fill them into allocated buffers
863                 // If packet is jumbo frame, continue filling to allocated buffers until no buffer is left
864                 for i, packet := range buffer.packets {
865                         if i >= int(nowAllocated) {
866                                 // There was less allocated buffers than actual packet count so exit early
867                                 break
868                         }
869
870                         packetData := unsafe.Pointer(&packet[0])
871                         C.govpp_copy_packet_data(nextFreeBuff, toFill, C.int(i), packetData, C.uint16_t(len(packet)))
872                 }
873
874                 if isJumbo && nowAllocated > 0 {
875                         // If we successfully allocated required amount of buffers for entire jumbo to be sent
876                         // simply sub entire amount of jumbo frame packets and leave only 1 so sender will think
877                         // it only sent 1 packet so it does not need to know anything about jumbo frames
878                         subCount += nowAllocated - 1
879                 }
880
881                 // If we do not have enough buffers left to allocate, simply end here to avoid packet loss and try
882                 // to handle it next burst
883                 if endEarly {
884                         break
885                 }
886         }
887
888         var sentCount C.uint16_t
889         errCode := C.memif_tx_burst(memif.cHandle, cQueueID, pb.buffers, allocated, &sentCount)
890         err = getMemifError(int(errCode))
891         if err != nil {
892                 return 0, err
893         }
894
895         // Prevent negative values
896         realSent := uint16(sentCount) - uint16(subCount)
897         if subCount > sentCount {
898                 sentCount = 0
899         }
900
901         log.Debugf("%v - sent %v total allocated buffs %v", cQueueID, sentCount, allocated)
902         return realSent, nil
903 }
904
905 // RxBurst is used to receive multiple packets in one call from a selected queue.
906 // <count> is the number of packets to receive. The actual number of packets
907 // received may be smaller. <count> effectively limits the maximum number
908 // of packets to receive in one burst (for a flat, predictable memory usage).
909 // The method is non-blocking even if there are no packets to receive.
910 // It is only valid to call this function if memif is in the connected state.
911 // Multiple RxBurst-s can run concurrently provided that each targets a different
912 // Rx queue.
913 func (memif *Memif) RxBurst(queueID uint8, count uint16) (packets []RawPacketData, err error) {
914         var recvCount C.uint16_t
915
916         if count == 0 {
917                 return packets, nil
918         }
919
920         if int(queueID) >= len(memif.rxQueueBufs) {
921                 return packets, ErrQueueID
922         }
923
924         // Reallocate Rx buffers if needed to fit the output packets.
925         pb := &memif.rxQueueBufs[queueID]
926         bufCount := int(count)
927         if pb.count < bufCount {
928                 newBuffers := C.realloc(unsafe.Pointer(pb.buffers), C.size_t(bufCount*int(C.sizeof_memif_buffer_t)))
929                 if newBuffers == nil {
930                         // Realloc failed, len(<packets>) will be certainly less than <count>.
931                         bufCount = pb.count
932                 } else {
933                         pb.buffers = (*C.memif_buffer_t)(newBuffers)
934                         pb.count = bufCount
935                 }
936         }
937
938         cQueueID := C.uint16_t(queueID)
939         errCode := C.memif_rx_burst(memif.cHandle, cQueueID, pb.buffers, C.uint16_t(bufCount), &recvCount)
940         err = getMemifError(int(errCode))
941         if err == ErrNoBuf {
942                 // More packets to read - the user is expected to run RxBurst() until there
943                 // are no more packets to receive.
944                 err = nil
945         }
946         if err != nil {
947                 return packets, err
948         }
949
950         chained := len(pb.rxChainBuf) > 0
951         if chained {
952                 // We had stored data from previous burst because last buffer in previous burst was chained
953                 // so we need to continue appending to this data
954                 packets = pb.rxChainBuf
955                 pb.rxChainBuf = nil
956         }
957
958         // Copy packet data into the instances of RawPacketData.
959         for i := 0; i < int(recvCount); i++ {
960                 var packetSize C.int
961                 packetData := C.govpp_get_packet_data(pb.buffers, C.int(i), &packetSize)
962                 packetBytes := C.GoBytes(packetData, packetSize)
963
964                 if chained {
965                         // We have chained buffers, so start merging packet data with last read packet
966                         prevPacket := packets[len(packets)-1]
967                         packets[len(packets)-1] = append(prevPacket, packetBytes...)
968                 } else {
969                         packets = append(packets, packetBytes)
970                 }
971
972                 // Mark last buffer as chained based on property on current buffer so next buffers
973                 // will try to append data to this one in case we got jumbo frame
974                 chained = C.govpp_is_buffer_chained(pb.buffers, C.int(i)) > 0
975         }
976
977         if recvCount > 0 {
978                 errCode = C.memif_refill_queue(memif.cHandle, cQueueID, recvCount, 0)
979         }
980         err = getMemifError(int(errCode))
981         if err != nil {
982                 // Throw away packets to avoid duplicities.
983                 packets = nil
984         }
985
986         if chained {
987                 // We did not had enough space to process all chained buffers to the end so simply tell
988                 // reader that it should not process any packets here and save them for next burst
989                 // to finish reading the buffer chain
990                 pb.rxChainBuf = packets
991                 packets = nil
992                 err = ErrNoBuf
993         }
994
995         return packets, err
996 }
997
998 // Close removes the memif interface. If the memif is in the connected state,
999 // the connection is first properly closed.
1000 // Do not access memif after it is closed, let garbage collector to remove it.
1001 func (memif *Memif) Close() error {
1002         log.WithField("ifName", memif.IfName).Debug("Closing the memif interface")
1003
1004         // Delete memif from C-libmemif.
1005         err := getMemifError(int(C.memif_delete(&memif.cHandle)))
1006
1007         if err != nil {
1008                 // Close memif-global interrupt channel.
1009                 close(memif.intCh)
1010                 // Close file descriptor stopQPollFd.
1011                 C.close(C.int(memif.stopQPollFd))
1012         }
1013
1014         context.lock.Lock()
1015         defer context.lock.Unlock()
1016         // Unregister the interface from the context.
1017         delete(context.memifs, memif.ifIndex)
1018         log.WithField("ifName", memif.IfName).Debug("memif interface was closed")
1019
1020         return err
1021 }
1022
1023 // initQueues allocates resources associated with Rx/Tx queues.
1024 func (memif *Memif) initQueues() error {
1025         // Get Rx/Tx queues count.
1026         details, err := memif.GetDetails()
1027         if err != nil {
1028                 return err
1029         }
1030
1031         log.WithFields(logger.Fields{
1032                 "ifName":   memif.IfName,
1033                 "Rx-count": len(details.RxQueues),
1034                 "Tx-count": len(details.TxQueues),
1035         }).Debug("Initializing Rx/Tx queues.")
1036
1037         // Initialize interrupt channels.
1038         var i int
1039         for i = 0; i < len(details.RxQueues); i++ {
1040                 queueIntCh := make(chan struct{}, 1)
1041                 memif.queueIntCh = append(memif.queueIntCh, queueIntCh)
1042         }
1043
1044         // Initialize Rx/Tx packet buffers.
1045         for i = 0; i < len(details.RxQueues); i++ {
1046                 memif.rxQueueBufs = append(memif.rxQueueBufs, CPacketBuffers{})
1047                 if !memif.IsMaster {
1048                         errCode := C.memif_refill_queue(memif.cHandle, C.uint16_t(i), C.uint16_t(memif.ringSize-1), 0)
1049                         err = getMemifError(int(errCode))
1050                         if err != nil {
1051                                 log.Warn(err.Error())
1052                         }
1053                 }
1054         }
1055         for i = 0; i < len(details.TxQueues); i++ {
1056                 memif.txQueueBufs = append(memif.txQueueBufs, CPacketBuffers{})
1057         }
1058
1059         return nil
1060 }
1061
1062 // closeQueues deallocates all resources associated with Rx/Tx queues.
1063 func (memif *Memif) closeQueues() {
1064         log.WithFields(logger.Fields{
1065                 "ifName":   memif.IfName,
1066                 "Rx-count": len(memif.rxQueueBufs),
1067                 "Tx-count": len(memif.txQueueBufs),
1068         }).Debug("Closing Rx/Tx queues.")
1069
1070         // Close interrupt channels.
1071         for _, ch := range memif.queueIntCh {
1072                 close(ch)
1073         }
1074         memif.queueIntCh = nil
1075
1076         // Deallocate Rx/Tx packet buffers.
1077         for _, pb := range memif.rxQueueBufs {
1078                 C.free(unsafe.Pointer(pb.buffers))
1079         }
1080         memif.rxQueueBufs = nil
1081         for _, pb := range memif.txQueueBufs {
1082                 C.free(unsafe.Pointer(pb.buffers))
1083         }
1084         memif.txQueueBufs = nil
1085 }
1086
1087 // pollEvents repeatedly polls for a libmemif event.
1088 func pollEvents() {
1089         defer context.wg.Done()
1090         for {
1091                 errCode := C.memif_poll_event(C.int(-1))
1092                 err := getMemifError(int(errCode))
1093                 if err == ErrPollCanceled {
1094                         return
1095                 }
1096         }
1097 }
1098
1099 // pollRxQueue repeatedly polls an Rx queue for interrupts.
1100 func pollRxQueue(memif *Memif, queueID uint8) {
1101         defer memif.wg.Done()
1102
1103         log.WithFields(logger.Fields{
1104                 "ifName":   memif.IfName,
1105                 "queue-ID": queueID,
1106         }).Debug("Started queue interrupt polling.")
1107
1108         var qfd C.int
1109         errCode := C.memif_get_queue_efd(memif.cHandle, C.uint16_t(queueID), &qfd)
1110         err := getMemifError(int(errCode))
1111         if err != nil {
1112                 log.WithField("err", err).Error("memif_get_queue_efd() failed")
1113                 return
1114         }
1115
1116         // Create epoll file descriptor.
1117         var event [1]syscall.EpollEvent
1118         epFd, err := syscall.EpollCreate1(0)
1119         if err != nil {
1120                 log.WithField("err", err).Error("epoll_create1() failed")
1121                 return
1122         }
1123         defer syscall.Close(epFd)
1124
1125         // Add Rx queue interrupt file descriptor.
1126         event[0].Events = syscall.EPOLLIN
1127         event[0].Fd = int32(qfd)
1128         if err = syscall.EpollCtl(epFd, syscall.EPOLL_CTL_ADD, int(qfd), &event[0]); err != nil {
1129                 log.WithField("err", err).Error("epoll_ctl() failed")
1130                 return
1131         }
1132
1133         // Add file descriptor used to stop this go routine.
1134         event[0].Events = syscall.EPOLLIN
1135         event[0].Fd = int32(memif.stopQPollFd)
1136         if err = syscall.EpollCtl(epFd, syscall.EPOLL_CTL_ADD, memif.stopQPollFd, &event[0]); err != nil {
1137                 log.WithField("err", err).Error("epoll_ctl() failed")
1138                 return
1139         }
1140
1141         // Poll for interrupts.
1142         for {
1143                 _, err := syscall.EpollWait(epFd, event[:], -1)
1144                 if err != nil {
1145                         log.WithField("err", err).Error("epoll_wait() failed")
1146                         return
1147                 }
1148
1149                 // Handle Rx Interrupt.
1150                 if event[0].Fd == int32(qfd) {
1151                         // Consume the interrupt event.
1152                         buf := make([]byte, 8)
1153                         _, err = syscall.Read(int(qfd), buf[:])
1154                         if err != nil {
1155                                 log.WithField("err", err).Warn("read() failed")
1156                         }
1157
1158                         // Send signal to memif-global interrupt channel.
1159                         select {
1160                         case memif.intCh <- queueID:
1161                                 break
1162                         default:
1163                                 break
1164                         }
1165
1166                         // Send signal to queue-specific interrupt channel.
1167                         select {
1168                         case memif.queueIntCh[queueID] <- struct{}{}:
1169                                 break
1170                         default:
1171                                 break
1172                         }
1173                 }
1174
1175                 // Stop the go routine if requested.
1176                 if event[0].Fd == int32(memif.stopQPollFd) {
1177                         log.WithFields(logger.Fields{
1178                                 "ifName":   memif.IfName,
1179                                 "queue-ID": queueID,
1180                         }).Debug("Stopped queue interrupt polling.")
1181                         return
1182                 }
1183         }
1184 }
1185
1186 //export go_on_connect_callback
1187 func go_on_connect_callback(privateCtx unsafe.Pointer) C.int {
1188         log.Debug("go_on_connect_callback BEGIN")
1189         defer log.Debug("go_on_connect_callback END")
1190         context.lock.RLock()
1191         defer context.lock.RUnlock()
1192
1193         // Get memif reference.
1194         ifIndex := int(uintptr(privateCtx))
1195         memif, exists := context.memifs[ifIndex]
1196         if !exists {
1197                 return C.int(ErrNoConn.Code())
1198         }
1199
1200         // Initialize Rx/Tx queues.
1201         err := memif.initQueues()
1202         if err != nil {
1203                 if memifErr, ok := err.(*MemifError); ok {
1204                         return C.int(memifErr.Code())
1205                 }
1206                 return C.int(ErrUnknown.Code())
1207         }
1208
1209         // Call the user callback.
1210         if memif.callbacks.OnConnect != nil {
1211                 memif.callbacks.OnConnect(memif)
1212         }
1213
1214         // Start polling the RX queues for interrupts.
1215         for i := 0; i < len(memif.queueIntCh); i++ {
1216                 memif.wg.Add(1)
1217                 go pollRxQueue(memif, uint8(i))
1218         }
1219
1220         return C.int(0)
1221 }
1222
1223 //export go_on_disconnect_callback
1224 func go_on_disconnect_callback(privateCtx unsafe.Pointer) C.int {
1225         log.Debug("go_on_disconnect_callback BEGIN")
1226         defer log.Debug("go_on_disconnect_callback END")
1227         context.lock.RLock()
1228         defer context.lock.RUnlock()
1229
1230         // Get memif reference.
1231         ifIndex := int(uintptr(privateCtx))
1232         memif, exists := context.memifs[ifIndex]
1233         if !exists {
1234                 // Already closed.
1235                 return C.int(0)
1236         }
1237
1238         // Stop polling the RX queues for interrupts.
1239         buf := make([]byte, 8)
1240         binary.PutUvarint(buf, 1)
1241         // - add an event
1242         _, err := syscall.Write(memif.stopQPollFd, buf[:])
1243         if err != nil {
1244                 return C.int(ErrSyscall.Code())
1245         }
1246         // - wait
1247         memif.wg.Wait()
1248         // - remove the event
1249         _, err = syscall.Read(memif.stopQPollFd, buf[:])
1250         if err != nil {
1251                 return C.int(ErrSyscall.Code())
1252         }
1253
1254         // Call the user callback.
1255         if memif.callbacks.OnDisconnect != nil {
1256                 memif.callbacks.OnDisconnect(memif)
1257         }
1258
1259         // Close Rx/Tx queues.
1260         memif.closeQueues()
1261
1262         return C.int(0)
1263 }