diff options
author | Jakub Grajciar <jgrajcia@cisco.com> | 2020-04-02 10:02:17 +0200 |
---|---|---|
committer | Damjan Marion <dmarion@me.com> | 2020-04-28 21:18:37 +0000 |
commit | 07363a45fe4a7fe693acf438f0b56f927bdd3fbd (patch) | |
tree | 6d53728ac594de1b86e85c7d4ea1d9f8d145a993 /extras/gomemif/examples | |
parent | c458c493667bde30c22760e3a1839f2cac6e6447 (diff) |
gomemif: introduce gomemif
golang native memif driver
Type: feature
Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
Change-Id: I693156a44011c80025245d25134f5bf5db6eba82
Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
Diffstat (limited to 'extras/gomemif/examples')
-rw-r--r-- | extras/gomemif/examples/BUILD.bazel | 21 | ||||
-rw-r--r-- | extras/gomemif/examples/bridge.go | 286 | ||||
-rw-r--r-- | extras/gomemif/examples/responder.go | 227 |
3 files changed, 534 insertions, 0 deletions
diff --git a/extras/gomemif/examples/BUILD.bazel b/extras/gomemif/examples/BUILD.bazel new file mode 100644 index 00000000000..a88b92e37f1 --- /dev/null +++ b/extras/gomemif/examples/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "responder", + srcs = ["responder.go"], + visibility = ["//visibility:public",], + deps = [ + "//memif:memif", + "@com_github_profile//:go_default_library", + ], +) + +go_binary( + name = "bridge", + srcs = ["bridge.go"], + visibility = ["//visibility:public",], + deps = [ + "//memif:memif", + "@com_github_profile//:go_default_library", + ], +) diff --git a/extras/gomemif/examples/bridge.go b/extras/gomemif/examples/bridge.go new file mode 100644 index 00000000000..a192034e3be --- /dev/null +++ b/extras/gomemif/examples/bridge.go @@ -0,0 +1,286 @@ +/* + *------------------------------------------------------------------ + * 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) +} diff --git a/extras/gomemif/examples/responder.go b/extras/gomemif/examples/responder.go new file mode 100644 index 00000000000..8fda6435bc5 --- /dev/null +++ b/extras/gomemif/examples/responder.go @@ -0,0 +1,227 @@ +/* + *------------------------------------------------------------------ + * 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" + + "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 + } + // get tx queue + txq0, err := i.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 { + // 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 +} + +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 main() { + 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() + + if *cpuprof != "" { + defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop() + } + if *memprof != "" { + defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop() + } + + memifErrChan := make(chan error) + exitChan := make(chan struct{}) + + var isMaster bool + switch *role { + case "slave": + isMaster = false + case "master": + isMaster = true + default: + fmt.Println("Invalid role") + return + } + + fmt.Println("GoMemif: Responder") + fmt.Println("-----------------------") + + socket, err := memif.NewSocket("gomemif_example", *socketName) + if err != nil { + fmt.Println("Failed to create socket: ", err) + return + } + + data := interfaceData{} + args := &memif.Arguments{ + IsMaster: isMaster, + ConnectedFunc: Connected, + DisconnectedFunc: Disconnected, + PrivateData: &data, + Name: *name, + } + + i, err := socket.NewInterface(args) + if err != nil { + fmt.Println("Failed to create interface on socket %s: %s", socket.GetFilename(), err) + goto exit + } + + // 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() + */ + fmt.Println("Faild to connect: ", err) + goto exit + } + } + } + + go func(exitChan chan<- struct{}) { + reader := bufio.NewReader(os.Stdin) + 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": + fmt.Println("remote: ", i.GetRemoteName()) + fmt.Println("peer: ", i.GetPeerName()) + case "exit": + err = socket.StopPolling() + if err != nil { + fmt.Println("Failed to stop polling: ", err) + } + close(exitChan) + return + default: + fmt.Println("Unknown input") + } + } + }(exitChan) + + for { + select { + case <-exitChan: + goto exit + case err, ok := <-memifErrChan: + if ok { + fmt.Println(err) + } + case err, ok := <-data.errChan: + if ok { + fmt.Println(err) + } + default: + continue + } + } + +exit: + socket.Delete() + close(memifErrChan) +} |