1 // Copyright (c) 2017 Cisco and/or its affiliates.
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:
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 // +build !windows,!darwin
26 logger "github.com/Sirupsen/logrus"
36 #include <sys/eventfd.h>
40 #ifndef MEMIF_HAVE_CANCEL_POLL_EVENT
41 // memif_cancel_poll_event that simply returns ErrUnsupported.
43 memif_cancel_poll_event ()
45 return 102; // ErrUnsupported
49 // govpp_memif_conn_args_t replaces fixed sized arrays with C-strings which
50 // are much easier to work with in cgo.
53 char *socket_filename;
55 uint8_t num_s2m_rings;
56 uint8_t num_m2s_rings;
58 memif_log2_ring_size_t log2_ring_size;
60 memif_interface_id_t interface_id;
63 memif_interface_mode_t mode;
64 } govpp_memif_conn_args_t;
66 // govpp_memif_details_t replaces strings represented with (uint8_t *)
67 // to the standard and easy to work with in cgo: (char *)
73 char *remote_inst_name;
78 char *socket_filename;
79 uint8_t rx_queues_num;
80 uint8_t tx_queues_num;
81 memif_queue_details_t *rx_queues;
82 memif_queue_details_t *tx_queues;
84 } govpp_memif_details_t;
86 extern int go_on_connect_callback(void *privateCtx);
87 extern int go_on_disconnect_callback(void *privateCtx);
89 // Callbacks strip the connection handle away.
92 govpp_on_connect_callback(memif_conn_handle_t conn, void *private_ctx)
94 return go_on_connect_callback(private_ctx);
98 govpp_on_disconnect_callback(memif_conn_handle_t conn, void *private_ctx)
100 return go_on_disconnect_callback(private_ctx);
103 // govpp_memif_create uses govpp_memif_conn_args_t.
105 govpp_memif_create (memif_conn_handle_t *conn, govpp_memif_conn_args_t *go_args,
108 memif_conn_args_t args;
109 memset (&args, 0, sizeof (args));
110 args.socket_filename = (char *)go_args->socket_filename;
111 if (go_args->secret != NULL)
113 strncpy ((char *)args.secret, go_args->secret,
114 sizeof (args.secret) - 1);
116 args.num_s2m_rings = go_args->num_s2m_rings;
117 args.num_m2s_rings = go_args->num_m2s_rings;
118 args.buffer_size = go_args->buffer_size;
119 args.log2_ring_size = go_args->log2_ring_size;
120 args.is_master = go_args->is_master;
121 args.interface_id = go_args->interface_id;
122 if (go_args->interface_name != NULL)
124 strncpy ((char *)args.interface_name, go_args->interface_name,
125 sizeof(args.interface_name) - 1);
127 if (go_args->instance_name != NULL)
129 strncpy ((char *)args.instance_name, go_args->instance_name,
130 sizeof (args.instance_name) - 1);
132 args.mode = go_args->mode;
134 return memif_create(conn, &args, govpp_on_connect_callback,
135 govpp_on_disconnect_callback, NULL,
139 // govpp_memif_get_details keeps reallocating buffer until it is large enough.
140 // The buffer is returned to be deallocated when it is no longer needed.
142 govpp_memif_get_details (memif_conn_handle_t conn, govpp_memif_details_t *govpp_md,
146 size_t buflen = 1 << 7;
147 char *buffer = NULL, *new_buffer = NULL;
148 memif_details_t md = {0};
151 // initial malloc (256 bytes) or realloc
153 new_buffer = realloc(buffer, buflen);
154 if (new_buffer == NULL)
157 return MEMIF_ERR_NOMEM;
160 // try to get details
161 rv = memif_get_details(conn, &md, buffer, buflen);
162 } while (rv == MEMIF_ERR_NOBUF_DET);
167 govpp_md->if_name = (char *)md.if_name;
168 govpp_md->inst_name = (char *)md.inst_name;
169 govpp_md->remote_if_name = (char *)md.remote_if_name;
170 govpp_md->remote_inst_name = (char *)md.remote_inst_name;
171 govpp_md->id = md.id;
172 govpp_md->secret = (char *)md.secret;
173 govpp_md->role = md.role;
174 govpp_md->mode = md.mode;
175 govpp_md->socket_filename = (char *)md.socket_filename;
176 govpp_md->rx_queues_num = md.rx_queues_num;
177 govpp_md->tx_queues_num = md.tx_queues_num;
178 govpp_md->rx_queues = md.rx_queues;
179 govpp_md->tx_queues = md.tx_queues;
180 govpp_md->link_up_down = md.link_up_down;
187 // Used to avoid cumbersome tricks that use unsafe.Pointer() + unsafe.Sizeof()
188 // or even cast C-array directly into Go-slice.
189 static memif_queue_details_t
190 govpp_get_rx_queue_details (govpp_memif_details_t *md, int index)
192 return md->rx_queues[index];
195 // Used to avoid cumbersome tricks that use unsafe.Pointer() + unsafe.Sizeof()
196 // or even cast C-array directly into Go-slice.
197 static memif_queue_details_t
198 govpp_get_tx_queue_details (govpp_memif_details_t *md, int index)
200 return md->tx_queues[index];
203 // Copy packet data into the selected buffer.
205 govpp_copy_packet_data(memif_buffer_t *buffers, int index, void *data, uint32_t size)
207 buffers[index].data_len = (size > buffers[index].buffer_len ? buffers[index].buffer_len : size);
208 memcpy(buffers[index].data, data, (size_t)buffers[index].data_len);
211 // Get packet data from the selected buffer.
212 // Used to avoid an ugly unsafe.Pointer() + unsafe.Sizeof().
214 govpp_get_packet_data(memif_buffer_t *buffers, int index, int *size)
216 *size = (int)buffers[index].data_len;
217 return buffers[index].data;
223 // IfMode represents the mode (layer/behaviour) in which the interface operates.
227 // IfModeEthernet tells memif to operate on the L2 layer.
228 IfModeEthernet IfMode = iota
230 // IfModeIP tells memif to operate on the L3 layer.
233 // IfModePuntInject tells memif to behave as Inject/Punt interface.
237 // RxMode is used to switch between polling and interrupt for RX.
241 // RxModeInterrupt tells libmemif to send interrupt signal when data are available.
242 RxModeInterrupt RxMode = iota
244 // RxModePolling means that the user needs to explicitly poll for data on RX
249 // RawPacketData represents raw packet data. libmemif doesn't care what the
250 // actual content is, it only manipulates with raw bytes.
251 type RawPacketData []byte
253 // MemifMeta is used to store a basic memif metadata needed for identification
254 // and connection establishment.
255 type MemifMeta struct {
256 // IfName is the interface name. Has to be unique across all created memifs.
257 // Interface name is truncated if needed to have no more than 32 characters.
260 // InstanceName identifies the endpoint. If omitted, the application
261 // name passed to Init() will be used instead.
262 // Instance name is truncated if needed to have no more than 32 characters.
265 // ConnID is a connection ID used to match opposite sides of the memif
269 // SocketFilename is the filename of the AF_UNIX socket through which
270 // the connection is established.
271 // The string is truncated if neede to fit into sockaddr_un.sun_path
272 // (108 characters on Linux).
273 SocketFilename string
275 // Secret must be the same on both sides for the authentication to succeed.
276 // Empty string is allowed.
277 // The secret is truncated if needed to have no more than 24 characters.
280 // IsMaster is set to true if memif operates in the Master mode.
283 // Mode is the mode (layer/behaviour) in which the memif operates.
287 // MemifShmSpecs is used to store the specification of the shared memory segment
288 // used by memif to send/receive packets.
289 type MemifShmSpecs struct {
290 // NumRxQueues is the number of Rx queues.
291 // Default is 1 (used if the value is 0).
294 // NumTxQueues is the number of Tx queues.
295 // Default is 1 (used if the value is 0).
298 // BufferSize is the size of the buffer to hold one packet, or a single
299 // fragment of a jumbo frame. Default is 2048 (used if the value is 0).
302 // Log2RingSize is the number of items in the ring represented through
303 // the logarithm base 2.
304 // Default is 10 (used if the value is 0).
308 // MemifConfig is the memif configuration.
309 // Used as the input argument to CreateInterface().
310 // It is the slave's config that mostly decides the parameters of the connection,
311 // but master may limit some of the quantities if needed (based on the memif
312 // protocol or master's configuration)
313 type MemifConfig struct {
318 // ConnUpdateCallback is a callback type declaration used with callbacks
319 // related to connection status changes.
320 type ConnUpdateCallback func(memif *Memif) (err error)
322 // MemifCallbacks is a container for all callbacks provided by memif.
323 // Any callback can be nil, in which case it will be simply skipped.
324 // Important: Do not call CreateInterface() or Memif.Close() from within a callback
325 // or a deadlock will occur. Instead send signal through a channel to another
326 // go routine which will be able to create/remove memif interface(s).
327 type MemifCallbacks struct {
328 // OnConnect is triggered when a connection for a given memif was established.
329 OnConnect ConnUpdateCallback
331 // OnDisconnect is triggered when a connection for a given memif was lost.
332 OnDisconnect ConnUpdateCallback
335 // Memif represents a single memif interface. It provides methods to send/receive
336 // packets in bursts in either the polling mode or in the interrupt mode with
337 // the help of golang channels.
341 // Per-library references
342 ifIndex int // index used in the Go-libmemif context (Context.memifs)
343 cHandle C.memif_conn_handle_t // handle used in C-libmemif
346 callbacks *MemifCallbacks
349 intCh chan uint8 // memif-global interrupt channel (value = queue ID)
350 queueIntCh []chan struct{} // per RX queue interrupt channel
353 stopQPollFd int // event file descriptor used to stop pollRxQueue-s
354 wg sync.WaitGroup // wait group for all pollRxQueue-s
355 rxQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each RX queue
356 txQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each TX queue
359 // MemifDetails provides a detailed runtime information about a memif interface.
360 type MemifDetails struct {
365 // MemifConnDetails provides a detailed runtime information about a memif
367 type MemifConnDetails struct {
368 // RemoteIfName is the name of the memif on the opposite side.
370 // RemoteInstanceName is the name of the endpoint on the opposite side.
371 RemoteInstanceName string
372 // HasLink is true if the connection has link (= is established and functional).
374 // RxQueues contains details for each Rx queue.
375 RxQueues []MemifQueueDetails
376 // TxQueues contains details for each Tx queue.
377 TxQueues []MemifQueueDetails
380 // MemifQueueDetails provides a detailed runtime information about a memif queue.
381 // Queue = Ring + the associated buffers (one directional).
382 type MemifQueueDetails struct {
383 // QueueID is the ID of the queue.
385 // RingSize is the number of slots in the ring (not logarithmic).
387 // BufferSize is the size of each buffer pointed to from the ring slots.
389 /* Further ring information TO-BE-ADDED when C-libmemif supports them. */
392 // CPacketBuffers stores an array of memif buffers for use with TxBurst or RxBurst.
393 type CPacketBuffers struct {
394 buffers *C.memif_buffer_t
398 // Context is a global Go-libmemif runtime context.
399 type Context struct {
402 memifs map[int] /* ifIndex */ *Memif /* slice of all active memif interfaces */
405 wg sync.WaitGroup /* wait-group for pollEvents() */
409 // logger used by the adapter.
412 // Global Go-libmemif context.
413 context = &Context{initialized: false}
416 // init initializes global logger, which logs debug level messages to stdout.
420 log.Level = logger.DebugLevel
423 // SetLogger changes the logger for Go-libmemif to the provided one.
424 // The logger is not used for logging of C-libmemif.
425 func SetLogger(l *logger.Logger) {
429 // Init initializes the libmemif library. Must by called exactly once and before
430 // any libmemif functions. Do not forget to call Cleanup() before exiting
432 // <appName> should be a human-readable string identifying your application.
433 // For example, VPP returns the version information ("show version" from VPP CLI).
434 func Init(appName string) error {
436 defer context.lock.Unlock()
438 if context.initialized {
439 return ErrAlreadyInit
442 log.Debug("Initializing libmemif library")
444 // Initialize C-libmemif.
447 errCode = int(C.memif_init(nil, nil))
449 appName := C.CString(appName)
450 defer C.free(unsafe.Pointer(appName))
451 errCode = int(C.memif_init(nil, appName))
453 err := getMemifError(errCode)
458 // Initialize the map of memory interfaces.
459 context.memifs = make(map[int]*Memif)
461 // Start event polling.
465 context.initialized = true
466 log.Debug("libmemif library was initialized")
470 // Cleanup cleans up all the resources allocated by libmemif.
471 func Cleanup() error {
473 defer context.lock.Unlock()
475 if !context.initialized {
479 log.Debug("Closing libmemif library")
481 // Delete all active interfaces.
482 for _, memif := range context.memifs {
486 // Stop the event loop (if supported by C-libmemif).
487 errCode := C.memif_cancel_poll_event()
488 err := getMemifError(int(errCode))
490 log.Debug("Waiting for pollEvents() to stop...")
492 log.Debug("pollEvents() has stopped...")
494 log.WithField("err", err).Debug("NOT Waiting for pollEvents to stop...")
497 // Run cleanup for C-libmemif.
498 err = getMemifError(int(C.memif_cleanup()))
500 context.initialized = false
501 log.Debug("libmemif library was closed")
506 // CreateInterface creates a new memif interface with the given configuration.
507 // The same callbacks can be used with multiple memifs. The first callback input
508 // argument (*Memif) can be used to tell which memif the callback was triggered for.
509 // The method is thread-safe.
510 func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Memif, err error) {
512 defer context.lock.Unlock()
514 if !context.initialized {
515 return nil, ErrNotInit
518 log.WithField("ifName", config.IfName).Debug("Creating a new memif interface")
520 // Create memif-wrapper for Go-libmemif.
522 MemifMeta: config.MemifMeta,
523 callbacks: &MemifCallbacks{},
524 ifIndex: context.nextMemifIndex,
527 // Initialize memif callbacks.
528 if callbacks != nil {
529 memif.callbacks.OnConnect = callbacks.OnConnect
530 memif.callbacks.OnDisconnect = callbacks.OnDisconnect
533 // Initialize memif-global interrupt channel.
534 memif.intCh = make(chan uint8, 1<<6)
536 // Initialize event file descriptor for stopping Rx/Tx queue polling.
537 memif.stopQPollFd = int(C.eventfd(0, C.EFD_NONBLOCK))
538 if memif.stopQPollFd < 0 {
539 return nil, ErrSyscall
542 // Initialize memif input arguments.
543 args := &C.govpp_memif_conn_args_t{}
544 // - socket file name
545 if config.SocketFilename != "" {
546 args.socket_filename = C.CString(config.SocketFilename)
547 defer C.free(unsafe.Pointer(args.socket_filename))
550 args.interface_id = C.memif_interface_id_t(config.ConnID)
552 if config.IfName != "" {
553 args.interface_name = C.CString(config.IfName)
554 defer C.free(unsafe.Pointer(args.interface_name))
557 if config.InstanceName != "" {
558 args.instance_name = C.CString(config.InstanceName)
559 defer C.free(unsafe.Pointer(args.instance_name))
564 args.mode = C.MEMIF_INTERFACE_MODE_ETHERNET
566 args.mode = C.MEMIF_INTERFACE_MODE_IP
567 case IfModePuntInject:
568 args.mode = C.MEMIF_INTERFACE_MODE_PUNT_INJECT
570 args.mode = C.MEMIF_INTERFACE_MODE_ETHERNET
573 if config.Secret != "" {
574 args.secret = C.CString(config.Secret)
575 defer C.free(unsafe.Pointer(args.secret))
577 // - master/slave flag + number of Rx/Tx queues
579 args.num_s2m_rings = C.uint8_t(config.NumRxQueues)
580 args.num_m2s_rings = C.uint8_t(config.NumTxQueues)
581 args.is_master = C.uint8_t(1)
583 args.num_s2m_rings = C.uint8_t(config.NumTxQueues)
584 args.num_m2s_rings = C.uint8_t(config.NumRxQueues)
585 args.is_master = C.uint8_t(0)
588 args.buffer_size = C.uint16_t(config.BufferSize)
589 // - log_2(ring size)
590 args.log2_ring_size = C.memif_log2_ring_size_t(config.Log2RingSize)
592 // Create memif in C-libmemif.
593 errCode := C.govpp_memif_create(&memif.cHandle, args, unsafe.Pointer(uintptr(memif.ifIndex)))
594 err = getMemifError(int(errCode))
599 // Register the new memif.
600 context.memifs[memif.ifIndex] = memif
601 context.nextMemifIndex++
602 log.WithField("ifName", config.IfName).Debug("A new memif interface was created")
607 // GetInterruptChan returns a channel which is continuously being filled with
608 // IDs of queues with data ready to be received.
609 // Since there is only one interrupt signal sent for an entire burst of packets,
610 // an interrupt handling routine should repeatedly call RxBurst() until
611 // the function returns an empty slice of packets. This way it is ensured
612 // that there are no packets left on the queue unread when the interrupt signal
614 // The method is thread-safe.
615 func (memif *Memif) GetInterruptChan() (ch <-chan uint8 /* queue ID */) {
619 // GetQueueInterruptChan returns an empty-data channel which fires every time
620 // there are data to read on a given queue.
621 // It is only valid to call this function if memif is in the connected state.
622 // Channel is automatically closed when the connection goes down (but after
623 // the user provided callback OnDisconnect has executed).
624 // Since there is only one interrupt signal sent for an entire burst of packets,
625 // an interrupt handling routine should repeatedly call RxBurst() until
626 // the function returns an empty slice of packets. This way it is ensured
627 // that there are no packets left on the queue unread when the interrupt signal
629 // The method is thread-safe.
630 func (memif *Memif) GetQueueInterruptChan(queueID uint8) (ch <-chan struct{}, err error) {
631 if int(queueID) >= len(memif.queueIntCh) {
632 return nil, ErrQueueID
634 return memif.queueIntCh[queueID], nil
637 // SetRxMode allows to switch between the interrupt and the polling mode for Rx.
638 // The method is thread-safe.
639 func (memif *Memif) SetRxMode(queueID uint8, rxMode RxMode) (err error) {
640 var cRxMode C.memif_rx_mode_t
642 case RxModeInterrupt:
643 cRxMode = C.MEMIF_RX_MODE_INTERRUPT
645 cRxMode = C.MEMIF_RX_MODE_POLLING
647 cRxMode = C.MEMIF_RX_MODE_INTERRUPT
649 errCode := C.memif_set_rx_mode(memif.cHandle, cRxMode, C.uint16_t(queueID))
650 return getMemifError(int(errCode))
653 // GetDetails returns a detailed runtime information about this memif.
654 // The method is thread-safe.
655 func (memif *Memif) GetDetails() (details *MemifDetails, err error) {
656 cDetails := C.govpp_memif_details_t{}
659 // Get memif details from C-libmemif.
660 errCode := C.govpp_memif_get_details(memif.cHandle, &cDetails, &buf)
661 err = getMemifError(int(errCode))
665 defer C.free(unsafe.Pointer(buf))
667 // Convert details from C to Go.
668 details = &MemifDetails{}
670 details.IfName = C.GoString(cDetails.if_name)
671 details.InstanceName = C.GoString(cDetails.inst_name)
672 details.ConnID = uint32(cDetails.id)
673 details.SocketFilename = C.GoString(cDetails.socket_filename)
674 if cDetails.secret != nil {
675 details.Secret = C.GoString(cDetails.secret)
677 details.IsMaster = cDetails.role == C.uint8_t(0)
678 switch cDetails.mode {
679 case C.MEMIF_INTERFACE_MODE_ETHERNET:
680 details.Mode = IfModeEthernet
681 case C.MEMIF_INTERFACE_MODE_IP:
682 details.Mode = IfModeIP
683 case C.MEMIF_INTERFACE_MODE_PUNT_INJECT:
684 details.Mode = IfModePuntInject
686 details.Mode = IfModeEthernet
688 // - connection details:
689 details.RemoteIfName = C.GoString(cDetails.remote_if_name)
690 details.RemoteInstanceName = C.GoString(cDetails.remote_inst_name)
691 details.HasLink = cDetails.link_up_down == C.uint8_t(1)
694 for i = 0; i < uint8(cDetails.rx_queues_num); i++ {
695 cRxQueue := C.govpp_get_rx_queue_details(&cDetails, C.int(i))
696 queueDetails := MemifQueueDetails{
697 QueueID: uint8(cRxQueue.qid),
698 RingSize: uint32(cRxQueue.ring_size),
699 BufferSize: uint16(cRxQueue.buffer_size),
701 details.RxQueues = append(details.RxQueues, queueDetails)
704 for i = 0; i < uint8(cDetails.tx_queues_num); i++ {
705 cTxQueue := C.govpp_get_tx_queue_details(&cDetails, C.int(i))
706 queueDetails := MemifQueueDetails{
707 QueueID: uint8(cTxQueue.qid),
708 RingSize: uint32(cTxQueue.ring_size),
709 BufferSize: uint16(cTxQueue.buffer_size),
711 details.TxQueues = append(details.TxQueues, queueDetails)
717 // TxBurst is used to send multiple packets in one call into a selected queue.
718 // The actual number of packets sent may be smaller and is returned as <count>.
719 // The method is non-blocking even if the ring is full and no packet can be sent.
720 // It is only valid to call this function if memif is in the connected state.
721 // Multiple TxBurst-s can run concurrently provided that each targets a different
723 func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint16, err error) {
724 var sentCount C.uint16_t
725 var allocated C.uint16_t
728 if len(packets) == 0 {
732 if int(queueID) >= len(memif.txQueueBufs) {
736 // The largest packet in the set determines the packet buffer size.
737 for _, packet := range packets {
738 if len(packet) > int(bufSize) {
739 bufSize = len(packet)
743 // Reallocate Tx buffers if needed to fit the input packets.
744 pb := memif.txQueueBufs[queueID]
745 bufCount := len(packets)
746 if pb.count < bufCount {
747 newBuffers := C.realloc(unsafe.Pointer(pb.buffers), C.size_t(bufCount*int(C.sizeof_memif_buffer_t)))
748 if newBuffers == nil {
749 // Realloc failed, <count> will be less than len(packets).
752 pb.buffers = (*C.memif_buffer_t)(newBuffers)
757 // Allocate ring slots.
758 cQueueID := C.uint16_t(queueID)
759 errCode := C.memif_buffer_alloc(memif.cHandle, cQueueID, pb.buffers, C.uint16_t(bufCount),
760 &allocated, C.uint16_t(bufSize))
761 err = getMemifError(int(errCode))
762 if err == ErrNoBufRing {
763 // Not enough ring slots, <count> will be less than bufCount.
770 // Copy packet data into the buffers.
771 for i := 0; i < int(allocated); i++ {
772 packetData := unsafe.Pointer(&packets[i][0])
773 C.govpp_copy_packet_data(pb.buffers, C.int(i), packetData, C.uint32_t(len(packets[i])))
776 errCode = C.memif_tx_burst(memif.cHandle, cQueueID, pb.buffers, allocated, &sentCount)
777 err = getMemifError(int(errCode))
781 count = uint16(sentCount)
786 // RxBurst is used to receive multiple packets in one call from a selected queue.
787 // <count> is the number of packets to receive. The actual number of packets
788 // received may be smaller. <count> effectively limits the maximum number
789 // of packets to receive in one burst (for a flat, predictable memory usage).
790 // The method is non-blocking even if there are no packets to receive.
791 // It is only valid to call this function if memif is in the connected state.
792 // Multiple RxBurst-s can run concurrently provided that each targets a different
794 func (memif *Memif) RxBurst(queueID uint8, count uint16) (packets []RawPacketData, err error) {
795 var recvCount C.uint16_t
802 if int(queueID) >= len(memif.rxQueueBufs) {
803 return packets, ErrQueueID
806 // Reallocate Rx buffers if needed to fit the output packets.
807 pb := memif.rxQueueBufs[queueID]
808 bufCount := int(count)
809 if pb.count < bufCount {
810 newBuffers := C.realloc(unsafe.Pointer(pb.buffers), C.size_t(bufCount*int(C.sizeof_memif_buffer_t)))
811 if newBuffers == nil {
812 // Realloc failed, len(<packets>) will be certainly less than <count>.
815 pb.buffers = (*C.memif_buffer_t)(newBuffers)
820 cQueueID := C.uint16_t(queueID)
821 errCode := C.memif_rx_burst(memif.cHandle, cQueueID, pb.buffers, C.uint16_t(bufCount), &recvCount)
822 err = getMemifError(int(errCode))
824 // More packets to read - the user is expected to run RxBurst() until there
825 // are no more packets to receive.
832 // Copy packet data into the instances of RawPacketData.
833 for i := 0; i < int(recvCount); i++ {
835 packetData := C.govpp_get_packet_data(pb.buffers, C.int(i), &packetSize)
836 packets = append(packets, C.GoBytes(packetData, packetSize))
839 errCode = C.memif_buffer_free(memif.cHandle, cQueueID, pb.buffers, recvCount, &freed)
840 err = getMemifError(int(errCode))
842 // Throw away packets to avoid duplicities.
849 // Close removes the memif interface. If the memif is in the connected state,
850 // the connection is first properly closed.
851 // Do not access memif after it is closed, let garbage collector to remove it.
852 func (memif *Memif) Close() error {
853 log.WithField("ifName", memif.IfName).Debug("Closing the memif interface")
855 // Delete memif from C-libmemif.
856 err := getMemifError(int(C.memif_delete(&memif.cHandle)))
859 // Close memif-global interrupt channel.
861 // Close file descriptor stopQPollFd.
862 C.close(C.int(memif.stopQPollFd))
866 defer context.lock.Unlock()
867 // Unregister the interface from the context.
868 delete(context.memifs, memif.ifIndex)
869 log.WithField("ifName", memif.IfName).Debug("memif interface was closed")
874 // initQueues allocates resources associated with Rx/Tx queues.
875 func (memif *Memif) initQueues() error {
876 // Get Rx/Tx queues count.
877 details, err := memif.GetDetails()
882 log.WithFields(logger.Fields{
883 "ifName": memif.IfName,
884 "Rx-count": len(details.RxQueues),
885 "Tx-count": len(details.TxQueues),
886 }).Debug("Initializing Rx/Tx queues.")
888 // Initialize interrupt channels.
890 for i = 0; i < len(details.RxQueues); i++ {
891 queueIntCh := make(chan struct{}, 1)
892 memif.queueIntCh = append(memif.queueIntCh, queueIntCh)
895 // Initialize Rx/Tx packet buffers.
896 for i = 0; i < len(details.RxQueues); i++ {
897 memif.rxQueueBufs = append(memif.rxQueueBufs, CPacketBuffers{})
899 for i = 0; i < len(details.TxQueues); i++ {
900 memif.txQueueBufs = append(memif.txQueueBufs, CPacketBuffers{})
906 // closeQueues deallocates all resources associated with Rx/Tx queues.
907 func (memif *Memif) closeQueues() {
908 log.WithFields(logger.Fields{
909 "ifName": memif.IfName,
910 "Rx-count": len(memif.rxQueueBufs),
911 "Tx-count": len(memif.txQueueBufs),
912 }).Debug("Closing Rx/Tx queues.")
914 // Close interrupt channels.
915 for _, ch := range memif.queueIntCh {
918 memif.queueIntCh = nil
920 // Deallocate Rx/Tx packet buffers.
921 for _, pb := range memif.rxQueueBufs {
922 C.free(unsafe.Pointer(pb.buffers))
924 memif.rxQueueBufs = nil
925 for _, pb := range memif.txQueueBufs {
926 C.free(unsafe.Pointer(pb.buffers))
928 memif.txQueueBufs = nil
931 // pollEvents repeatedly polls for a libmemif event.
933 defer context.wg.Done()
935 errCode := C.memif_poll_event(C.int(-1))
936 err := getMemifError(int(errCode))
937 if err == ErrPollCanceled {
943 // pollRxQueue repeatedly polls an Rx queue for interrupts.
944 func pollRxQueue(memif *Memif, queueID uint8) {
945 defer memif.wg.Done()
947 log.WithFields(logger.Fields{
948 "ifName": memif.IfName,
950 }).Debug("Started queue interrupt polling.")
953 errCode := C.memif_get_queue_efd(memif.cHandle, C.uint16_t(queueID), &qfd)
954 err := getMemifError(int(errCode))
956 log.WithField("err", err).Error("memif_get_queue_efd() failed")
960 // Create epoll file descriptor.
961 var event [1]syscall.EpollEvent
962 epFd, err := syscall.EpollCreate1(0)
964 log.WithField("err", err).Error("epoll_create1() failed")
967 defer syscall.Close(epFd)
969 // Add Rx queue interrupt file descriptor.
970 event[0].Events = syscall.EPOLLIN
971 event[0].Fd = int32(qfd)
972 if err = syscall.EpollCtl(epFd, syscall.EPOLL_CTL_ADD, int(qfd), &event[0]); err != nil {
973 log.WithField("err", err).Error("epoll_ctl() failed")
977 // Add file descriptor used to stop this go routine.
978 event[0].Events = syscall.EPOLLIN
979 event[0].Fd = int32(memif.stopQPollFd)
980 if err = syscall.EpollCtl(epFd, syscall.EPOLL_CTL_ADD, memif.stopQPollFd, &event[0]); err != nil {
981 log.WithField("err", err).Error("epoll_ctl() failed")
985 // Poll for interrupts.
987 _, err := syscall.EpollWait(epFd, event[:], -1)
989 log.WithField("err", err).Error("epoll_wait() failed")
993 // Handle Rx Interrupt.
994 if event[0].Fd == int32(qfd) {
995 // Consume the interrupt event.
996 buf := make([]byte, 8)
997 _, err = syscall.Read(int(qfd), buf[:])
999 log.WithField("err", err).Warn("read() failed")
1002 // Send signal to memif-global interrupt channel.
1004 case memif.intCh <- queueID:
1010 // Send signal to queue-specific interrupt channel.
1012 case memif.queueIntCh[queueID] <- struct{}{}:
1019 // Stop the go routine if requested.
1020 if event[0].Fd == int32(memif.stopQPollFd) {
1021 log.WithFields(logger.Fields{
1022 "ifName": memif.IfName,
1023 "queue-ID": queueID,
1024 }).Debug("Stopped queue interrupt polling.")
1030 //export go_on_connect_callback
1031 func go_on_connect_callback(privateCtx unsafe.Pointer) C.int {
1032 log.Debug("go_on_connect_callback BEGIN")
1033 defer log.Debug("go_on_connect_callback END")
1034 context.lock.RLock()
1035 defer context.lock.RUnlock()
1037 // Get memif reference.
1038 ifIndex := int(uintptr(privateCtx))
1039 memif, exists := context.memifs[ifIndex]
1041 return C.int(ErrNoConn.Code())
1044 // Initialize Rx/Tx queues.
1045 err := memif.initQueues()
1047 if memifErr, ok := err.(*MemifError); ok {
1048 return C.int(memifErr.Code())
1050 return C.int(ErrUnknown.Code())
1053 // Call the user callback.
1054 if memif.callbacks.OnConnect != nil {
1055 memif.callbacks.OnConnect(memif)
1058 // Start polling the RX queues for interrupts.
1059 for i := 0; i < len(memif.queueIntCh); i++ {
1061 go pollRxQueue(memif, uint8(i))
1067 //export go_on_disconnect_callback
1068 func go_on_disconnect_callback(privateCtx unsafe.Pointer) C.int {
1069 log.Debug("go_on_disconnect_callback BEGIN")
1070 defer log.Debug("go_on_disconnect_callback END")
1071 context.lock.RLock()
1072 defer context.lock.RUnlock()
1074 // Get memif reference.
1075 ifIndex := int(uintptr(privateCtx))
1076 memif, exists := context.memifs[ifIndex]
1082 // Stop polling the RX queues for interrupts.
1083 buf := make([]byte, 8)
1084 binary.PutUvarint(buf, 1)
1086 _, err := syscall.Write(memif.stopQPollFd, buf[:])
1088 return C.int(ErrSyscall.Code())
1092 // - remove the event
1093 _, err = syscall.Read(memif.stopQPollFd, buf[:])
1095 return C.int(ErrSyscall.Code())
1098 // Call the user callback.
1099 if memif.callbacks.OnDisconnect != nil {
1100 memif.callbacks.OnDisconnect(memif)
1103 // Close Rx/Tx queues.