gomemif: migrate to govpp repository

Type: make
Signed-off-by: Matus Halaj <mhalaj@cisco.com>
Change-Id: I1d48c7e44fdf23438132996fd3288b29da1fe36e
This commit is contained in:
Matus Halaj 2022-10-13 14:46:39 +02:00 committed by Dave Wallace
parent 70892fcada
commit af36e96d07
17 changed files with 1 additions and 3287 deletions

View File

@ -1,50 +0,0 @@
.. _gomemif_doc:
Gomemif library
=======================
Memif library implemented in Go. The package contains 3 examples: Bridge and ICMP responder in interrupt and polling mode.
setup and run
-------------
To Build all examples
::
bazel build //...
To Run ICMP responder in interrupt mode:
::
DBGvpp# create interface memif id 0 master no-zero-copy
DBGvpp# set int ip addr memif0/0 192.168.1.2/24
DBGvpp# set int state memif0/0 up
bazel-bin/examples/linux_amd64_stripped/icmp_responder_cb
gomemif# start
DBGvpp# ping 192.168.1.1
To Run ICMP responder in polling mode:
::
DBGvpp# create interface memif id 0 master no-zero-copy
DBGvpp# set int ip addr memif0/0 192.168.1.2/24
DBGvpp# set int state memif0/0 up
bazel-bin/examples/linux_amd64_stripped/icmp_responder_poll
gomemif# start
DBGvpp# ping 192.168.1.1
To Run Bridge:
::
bazel-bin/examples/linux_amd64_stripped/bridge
gomemif# start

View File

@ -1,40 +0,0 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "io_bazel_rules_go",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.21.2/rules_go-v0.21.2.tar.gz",
"https://github.com/bazelbuild/rules_go/releases/download/v0.21.2/rules_go-v0.21.2.tar.gz",
],
sha256 = "f99a9d76e972e0c8f935b2fe6d0d9d778f67c760c6d2400e23fc2e469016e2bd",
)
load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
http_archive(
name = "bazel_gazelle",
sha256 = "86c6d481b3f7aedc1d60c1c211c6f76da282ae197c3b3160f54bd3a8f847896f",
urls = [
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/bazel-gazelle/releases/download/v0.19.1/bazel-gazelle-v0.19.1.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.19.1/bazel-gazelle-v0.19.1.tar.gz",
],
)
go_rules_dependencies()
go_register_toolchains()
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
gazelle_dependencies()
go_repository(
name = "com_github_profile",
importpath = "github.com/pkg/profile",
commit = "acd64d450fd45fb2afa41f833f3788c8a7797219"
)
go_repository(
name = "com_github_gopacket",
importpath = "github.com/google/gopacket",
commit = "3eaba08943250fd212520e5cff00ed808b8fc60a"
)

View File

@ -1,36 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
go_binary(
name = "icmp_responder_poll",
srcs = ["icmp_responder_poll.go"],
visibility = ["//visibility:public",],
deps = [
"//memif:memif",
"@com_github_profile//:go_default_library",
"@com_github_gopacket//layers:go_default_library",
"@com_github_gopacket//:go_default_library"
],
)
go_binary(
name = "bridge",
srcs = ["bridge.go"],
visibility = ["//visibility:public",],
deps = [
"//memif:memif",
"@com_github_profile//:go_default_library",
],
)
go_binary(
name = "icmp_responder_cb",
srcs = ["icmp_responder_cb.go"],
visibility = ["//visibility:public",],
deps = [
"//memif:memif",
"@com_github_profile//:go_default_library",
"@com_github_gopacket//layers:go_default_library",
"@com_github_gopacket//:go_default_library"
],
)

View File

@ -1,286 +0,0 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strings"
"sync"
"time"
"github.com/pkg/profile"
"memif"
)
func Disconnected(i *memif.Interface) error {
fmt.Println("Disconnected: ", i.GetName())
data, ok := i.GetPrivateData().(*interfaceData)
if !ok {
return fmt.Errorf("Invalid private data")
}
close(data.quitChan) // stop polling
close(data.errChan)
data.wg.Wait() // wait until polling stops, then continue disconnect
return nil
}
func Connected(i *memif.Interface) error {
fmt.Println("Connected: ", i.GetName())
data, ok := i.GetPrivateData().(*interfaceData)
if !ok {
return fmt.Errorf("Invalid private data")
}
data.errChan = make(chan error, 1)
data.quitChan = make(chan struct{}, 1)
data.wg.Add(1)
go func(errChan chan<- error, quitChan <-chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
// allocate packet buffer
pkt := make([]byte, 2048)
// get rx queue
rxq0, err := i.GetRxQueue(0)
if err != nil {
errChan <- err
return
}
// wait until both interfaces are connected
for !data.bri.IsConnected() {
time.Sleep(100 * time.Millisecond)
}
// get bridged interfaces tx queue
txq0, err := data.bri.GetTxQueue(0)
if err != nil {
errChan <- err
return
}
for {
select {
case <-quitChan: // channel closed
return
default:
// read packet from shared memory
pktLen, err := rxq0.ReadPacket(pkt)
if pktLen > 0 {
// FIXME: prevent packet write if interface is disconencted
// write packet to shared memory
txq0.WritePacket(pkt[:pktLen])
} else if err != nil {
errChan <- err
return
}
}
}
}(data.errChan, data.quitChan, &data.wg)
return nil
}
type interfaceData struct {
errChan chan error
quitChan chan struct{}
wg sync.WaitGroup
// bridged interface
bri *memif.Interface
}
func interractiveHelp() {
fmt.Println("help - print this help")
fmt.Println("start - start connecting loop")
fmt.Println("show - print interface details")
fmt.Println("exit - exit the application")
}
func newMemifInterface(socket *memif.Socket, id uint32, isMaster bool, name string) (*memif.Interface, *interfaceData, error) {
data := &interfaceData{}
args := &memif.Arguments{
Id: id,
IsMaster: isMaster,
ConnectedFunc: Connected,
DisconnectedFunc: Disconnected,
PrivateData: data,
Name: name,
}
i, err := socket.NewInterface(args)
if err != nil {
return nil, nil, fmt.Errorf("Failed to create interface on socket %s: %s", socket.GetFilename(), err)
}
// slave attempts to connect to control socket
// to handle control communication call socket.StartPolling()
if !i.IsMaster() {
fmt.Println(args.Name, ": Connecting to control socket...")
for !i.IsConnecting() {
err = i.RequestConnection()
if err != nil {
/* TODO: check for ECONNREFUSED errno
* if error is ECONNREFUSED it may simply mean that master
* interface is not up yet, use i.RequestConnection()
*/
return nil, nil, fmt.Errorf("Faild to connect: ", err)
}
}
}
return i, data, nil
}
func printMemifInterfaceDetails(i *memif.Interface) {
fmt.Println(i.GetName(), ":")
fmt.Println("\trole: ", memif.RoleToString(i.IsMaster()))
fmt.Println("\tid: ", i.GetId())
link := "down"
if i.IsConnected() {
link = "up"
}
fmt.Println("\tlink: ", link)
fmt.Println("\tremote: ", i.GetRemoteName())
fmt.Println("\tpeer: ", i.GetPeerName())
if i.IsConnected() {
mc := i.GetMemoryConfig()
fmt.Println("queue pairs: ", mc.NumQueuePairs)
fmt.Println("ring size: ", (1 << mc.Log2RingSize))
fmt.Println("buffer size: ", mc.PacketBufferSize)
}
}
func main() {
memifErrChan := make(chan error)
exitChan := make(chan struct{})
var i0, i1 *memif.Interface
var d0, d1 *interfaceData
cpuprof := flag.String("cpuprof", "", "cpu profiling output file")
memprof := flag.String("memprof", "", "mem profiling output file")
role := flag.String("role", "slave", "interface role")
name := flag.String("name", "gomemif", "interface name")
socketName := flag.String("socket", "", "control socket filename")
flag.Parse()
// profiling options
if *cpuprof != "" {
defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop()
}
if *memprof != "" {
defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop()
}
// memif options
var isMaster bool
switch *role {
case "slave":
isMaster = false
case "master":
isMaster = true
default:
fmt.Println("Invalid role")
return
}
// create memif socket
socket, err := memif.NewSocket("gomemif_example", *socketName)
if err != nil {
fmt.Println("Failed to create socket: ", err)
return
}
i0, d0, err = newMemifInterface(socket, 0, isMaster, *name)
if err != nil {
fmt.Println(err)
goto exit
}
// TODO: update name
i1, d1, err = newMemifInterface(socket, 1, isMaster, *name)
if err != nil {
fmt.Println(err)
goto exit
}
// set up bridge
d0.bri = i1
d1.bri = i0
// user input goroutine
go func(exitChan chan<- struct{}) {
reader := bufio.NewReader(os.Stdin)
fmt.Println("GoMemif: Responder")
fmt.Println("-----------------------")
for {
fmt.Print("gomemif# ")
text, _ := reader.ReadString('\n')
// convert CRLF to LF
text = strings.Replace(text, "\n", "", -1)
switch text {
case "help":
interractiveHelp()
case "start":
// start polling for events on this socket
socket.StartPolling(memifErrChan)
case "show":
printMemifInterfaceDetails(i0)
printMemifInterfaceDetails(i1)
case "exit":
err = socket.StopPolling()
if err != nil {
fmt.Println("Failed to stop polling: ", err)
}
close(exitChan)
return
default:
fmt.Println("Unknown input")
}
}
}(exitChan)
// main loop
for {
select {
case <-exitChan:
goto exit
case err, ok := <-memifErrChan:
if ok {
fmt.Println(err)
}
case err, ok := <-d0.errChan:
if ok {
fmt.Println(err)
}
case err, ok := <-d1.errChan:
if ok {
fmt.Println(err)
}
default:
continue
}
}
exit:
socket.Delete()
close(memifErrChan)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "memif",
srcs = [
"interface.go",
"interface_unsafe.go",
"control_channel.go",
"control_channel_unsafe.go",
"memif.go",
"memif_unsafe.go",
"packet_writer.go",
"packet_reader.go",
],
importpath = "memif",
visibility = ["//visibility:public",],
)

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package memif
import (
"fmt"
"os"
"syscall"
"unsafe"
)
// sendMsg sends a control message from contorl channels message queue
func (cc *controlChannel) sendMsg() (err error) {
if len(cc.msgQueue) < 1 {
return nil
}
// Get message buffer
msg := cc.msgQueue[0]
// Dequeue
cc.msgQueue = cc.msgQueue[1:]
iov := &syscall.Iovec{
Base: &msg.Buffer.Bytes()[0],
Len: msgSize,
}
msgh := syscall.Msghdr{
Iov: iov,
Iovlen: 1,
}
if msg.Fd > 0 {
oob := syscall.UnixRights(msg.Fd)
msgh.Control = &oob[0]
msgh.Controllen = uint64(syscall.CmsgSpace(4))
}
_, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, uintptr(cc.event.Fd), uintptr(unsafe.Pointer(&msgh)), uintptr(0))
if errno != 0 {
err = os.NewSyscallError("sendmsg", errno)
return fmt.Errorf("SYS_SENDMSG: %s", errno)
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package memif
import (
"fmt"
"os"
"syscall"
"unsafe"
)
// memfdCreate returns memory file file descriptor (memif.sys_memfd_create)
func memfdCreate() (mfd int, err error) {
p0, err := syscall.BytePtrFromString("memif_region_0")
if err != nil {
return -1, fmt.Errorf("memfdCreate: %s", err)
}
u_mfd, _, errno := syscall.Syscall(sys_memfd_create, uintptr(unsafe.Pointer(p0)), uintptr(mfd_allow_sealing), uintptr(0))
if errno != 0 {
return -1, fmt.Errorf("memfdCreate: %s", os.NewSyscallError("memfd_create", errno))
}
return int(u_mfd), nil
}

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +0,0 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package memif
import (
"unsafe"
)
// readHead reads ring head directly form the shared memory
func (q *Queue) readHead() (head int) {
return (int)(*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringHeadOffset])))
// return atomicload16(&q.i.regions[q.region].data[q.offset + descHeadOffset])
}
// readTail reads ring tail directly form the shared memory
func (q *Queue) readTail() (tail int) {
return (int)(*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringTailOffset])))
// return atomicload16(&q.i.regions[q.region].data[q.offset + descTailOffset])
}
// writeHead writes ring head directly to the shared memory
func (q *Queue) writeHead(value int) {
*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringHeadOffset])) = *(*uint16)(unsafe.Pointer(&value))
//atomicstore16(&q.i.regions[q.region].data[q.offset + descHeadOffset], value)
}
// writeTail writes ring tail directly to the shared memory
func (q *Queue) writeTail(value int) {
*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringTailOffset])) = *(*uint16)(unsafe.Pointer(&value))
//atomicstore16(&q.i.regions[q.region].data[q.offset + descTailOffset], value)
}
func (q *Queue) setDescLength(slot int, length int) {
*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize+descLengthOffset])) = *(*uint16)(unsafe.Pointer(&length))
}
// getFlags reads ring flags directly from the shared memory
func (q *Queue) getFlags() int {
return (int)(*(*uint16)(unsafe.Pointer(&q.i.regions[q.ring.region].data[q.ring.offset+ringFlagsOffset])))
}

View File

@ -1,91 +0,0 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package memif
import "fmt"
// ReadPacket reads one packet form the shared memory and
// returns the number of bytes read
func (q *Queue) ReadPacket(pkt []byte) (int, error) {
var mask int = q.ring.size - 1
var slot int
var lastSlot int
var length int
var offset int
var pktOffset int = 0
var nSlots uint16
var desc descBuf = newDescBuf()
if q.i.args.IsMaster {
slot = int(q.lastHead)
lastSlot = q.readHead()
} else {
slot = int(q.lastTail)
lastSlot = q.readTail()
}
nSlots = uint16(lastSlot - slot)
if nSlots == 0 {
goto refill
}
// copy descriptor from shm
q.getDescBuf(slot&mask, desc)
length = desc.getLength()
offset = desc.getOffset()
copy(pkt[:], q.i.regions[desc.getRegion()].data[offset:offset+length])
pktOffset += length
slot++
nSlots--
for (desc.getFlags() & descFlagNext) == descFlagNext {
if nSlots == 0 {
return 0, fmt.Errorf("Incomplete chained buffer, may suggest peer error.")
}
q.getDescBuf(slot&mask, desc)
length = desc.getLength()
offset = desc.getOffset()
copy(pkt[pktOffset:], q.i.regions[desc.getRegion()].data[offset:offset+length])
pktOffset += length
slot++
nSlots--
}
refill:
if q.i.args.IsMaster {
q.lastHead = uint16(slot)
q.writeTail(slot)
} else {
q.lastTail = uint16(slot)
head := q.readHead()
for nSlots := uint16(q.ring.size - head + int(q.lastTail)); nSlots > 0; nSlots-- {
q.setDescLength(head&mask, int(q.i.run.PacketBufferSize))
head++
}
q.writeHead(head)
}
return pktOffset, nil
}

View File

@ -1,95 +0,0 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package memif
// WritePacket writes one packet to the shared memory and
// returns the number of bytes written
func (q *Queue) WritePacket(pkt []byte) int {
var mask int = q.ring.size - 1
var slot int
var nFree uint16
var packetBufferSize int = int(q.i.run.PacketBufferSize)
if q.i.args.IsMaster {
slot = q.readTail()
nFree = uint16(q.readHead() - slot)
} else {
slot = q.readHead()
nFree = uint16(q.ring.size - slot + q.readTail())
}
if nFree == 0 {
q.interrupt()
return 0
}
// copy descriptor from shm
desc := newDescBuf()
q.getDescBuf(slot&mask, desc)
// reset flags
desc.setFlags(0)
// reset length
if q.i.args.IsMaster {
packetBufferSize = desc.getLength()
}
desc.setLength(0)
offset := desc.getOffset()
// write packet into memif buffer
n := copy(q.i.regions[desc.getRegion()].data[offset:offset+packetBufferSize], pkt[:])
desc.setLength(n)
for n < len(pkt) {
nFree--
if nFree == 0 {
q.interrupt()
return 0
}
desc.setFlags(descFlagNext)
q.putDescBuf(slot&mask, desc)
slot++
// copy descriptor from shm
q.getDescBuf(slot&mask, desc)
// reset flags
desc.setFlags(0)
// reset length
if q.i.args.IsMaster {
packetBufferSize = desc.getLength()
}
desc.setLength(0)
offset := desc.getOffset()
tmp := copy(q.i.regions[desc.getRegion()].data[offset:offset+packetBufferSize], pkt[:])
desc.setLength(tmp)
n += tmp
}
// copy descriptor to shm
q.putDescBuf(slot&mask, desc)
slot++
if q.i.args.IsMaster {
q.writeTail(slot)
} else {
q.writeHead(slot)
}
q.interrupt()
return n
}

View File

@ -0,0 +1 @@
Gomemif has been migrated to GoVPP repository https://github.com/FDio/govpp