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/memif/memif.go | |
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/memif/memif.go')
-rw-r--r-- | extras/gomemif/memif/memif.go | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/extras/gomemif/memif/memif.go b/extras/gomemif/memif/memif.go new file mode 100644 index 00000000000..1a7857de03e --- /dev/null +++ b/extras/gomemif/memif/memif.go @@ -0,0 +1,345 @@ +/* + *------------------------------------------------------------------ + * 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 ( + "encoding/binary" + "fmt" + "syscall" +) + +const cookie = 0x3E31F20 + +// VersionMajor is memif protocols major version +const VersionMajor = 2 + +// VersionMinor is memif protocols minor version +const VersionMinor = 0 + +// Version is memif protocols version as uint16 +// (M-Major m-minor: MMMMMMMMmmmmmmmm) +const Version = ((VersionMajor << 8) | VersionMinor) + +type msgType uint16 + +const ( + msgTypeNone msgType = iota + msgTypeAck + msgTypeHello + msgTypeInit + msgTypeAddRegion + msgTypeAddRing + msgTypeConnect + msgTypeConnected + msgTypeDisconnect +) + +type interfaceMode uint8 + +const ( + interfaceModeEthernet interfaceMode = iota + interfaceModeIp + interfaceModePuntInject +) + +const msgSize = 128 +const msgTypeSize = 2 + +const msgAddRingFlagS2M = (1 << 0) + +// Descriptor flags +// +// next buffer present +const descFlagNext = (1 << 0) + +// Ring flags +// +// Interrupt +const ringFlagInterrupt = 1 + +func min16(a uint16, b uint16) uint16 { + if a < b { + return a + } + return b +} + +func min8(a uint8, b uint8) uint8 { + if a < b { + return a + } + return b +} + +type MsgHello struct { + // app name + Name [32]byte + VersionMin uint16 + VersionMax uint16 + MaxRegion uint16 + MaxRingM2S uint16 + MaxRingS2M uint16 + MaxLog2RingSize uint8 +} + +type MsgInit struct { + Version uint16 + Id uint32 + Mode interfaceMode + Secret [24]byte + // app name + Name [32]byte +} + +type MsgAddRegion struct { + Index uint16 + Size uint64 +} + +type MsgAddRing struct { + Flags uint16 + Index uint16 + Region uint16 + Offset uint32 + RingSizeLog2 uint8 + PrivateHdrSize uint16 +} + +type MsgConnect struct { + // interface name + Name [32]byte +} + +type MsgConnected struct { + // interface name + Name [32]byte +} + +type MsgDisconnect struct { + Code uint32 + String [96]byte +} + +/* DESCRIPTOR BEGIN */ + +const descSize = 16 + +// desc field offsets +const descFlagsOffset = 0 +const descRegionOffset = 2 +const descLengthOffset = 4 +const descOffsetOffset = 8 +const descMetadataOffset = 12 + +// descBuf represents a memif descriptor as array of bytes +type descBuf []byte + +// newDescBuf returns new descriptor buffer +func newDescBuf() descBuf { + return make(descBuf, descSize) +} + +// getDescBuff copies descriptor from shared memory to descBuf +func (q *Queue) getDescBuf(slot int, db descBuf) { + copy(db, q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize:]) +} + +// putDescBuf copies contents of descriptor buffer into shared memory +func (q *Queue) putDescBuf(slot int, db descBuf) { + copy(q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize:], db) +} + +func (db descBuf) getFlags() int { + return (int)(binary.LittleEndian.Uint16((db)[descFlagsOffset:])) +} + +func (db descBuf) getRegion() int { + return (int)(binary.LittleEndian.Uint16((db)[descRegionOffset:])) +} + +func (db descBuf) getLength() int { + return (int)(binary.LittleEndian.Uint32((db)[descLengthOffset:])) +} + +func (db descBuf) getOffset() int { + return (int)(binary.LittleEndian.Uint32((db)[descOffsetOffset:])) +} + +func (db descBuf) getMetadata() int { + return (int)(binary.LittleEndian.Uint32((db)[descMetadataOffset:])) +} + +func (db descBuf) setFlags(val int) { + binary.LittleEndian.PutUint16((db)[descFlagsOffset:], uint16(val)) +} + +func (db descBuf) setRegion(val int) { + binary.LittleEndian.PutUint16((db)[descRegionOffset:], uint16(val)) +} + +func (db descBuf) setLength(val int) { + binary.LittleEndian.PutUint32((db)[descLengthOffset:], uint32(val)) +} + +func (db descBuf) setOffset(val int) { + binary.LittleEndian.PutUint32((db)[descOffsetOffset:], uint32(val)) +} + +func (db descBuf) setMetadata(val int) { + binary.LittleEndian.PutUint32((db)[descMetadataOffset:], uint32(val)) +} + +/* DESCRIPTOR END */ + +/* RING BEGIN */ + +type ringType uint8 + +const ( + ringTypeS2M ringType = iota + ringTypeM2S +) + +const ringSize = 128 + +// ring field offsets +const ringCookieOffset = 0 +const ringFlagsOffset = 4 +const ringHeadOffset = 6 +const ringTailOffset = 64 + +// ringBuf represents a memif ring as array of bytes +type ringBuf []byte + +type ring struct { + ringType ringType + size int + log2Size int + region int + rb ringBuf + offset int +} + +// newRing returns new memif ring based on data received in msgAddRing (master only) +func newRing(regionIndex int, ringType ringType, ringOffset int, log2RingSize int) *ring { + r := &ring{ + ringType: ringType, + size: (1 << log2RingSize), + log2Size: log2RingSize, + rb: make(ringBuf, ringSize), + offset: ringOffset, + } + + return r +} + +// newRing returns a new memif ring +func (i *Interface) newRing(regionIndex int, ringType ringType, ringIndex int) *ring { + r := &ring{ + ringType: ringType, + size: (1 << i.run.Log2RingSize), + log2Size: int(i.run.Log2RingSize), + rb: make(ringBuf, ringSize), + } + + rSize := ringSize + descSize*r.size + if r.ringType == ringTypeS2M { + r.offset = 0 + } else { + r.offset = int(i.run.NumQueuePairs) * rSize + } + r.offset += ringIndex * rSize + + return r +} + +// putRing put the ring to the shared memory +func (q *Queue) putRing() { + copy(q.i.regions[q.ring.region].data[q.ring.offset:], q.ring.rb) +} + +// updateRing updates ring with data from shared memory +func (q *Queue) updateRing() { + copy(q.ring.rb, q.i.regions[q.ring.region].data[q.ring.offset:]) +} + +func (r *ring) getCookie() int { + return (int)(binary.LittleEndian.Uint32((r.rb)[ringCookieOffset:])) +} + +// getFlags returns the flags value from ring buffer +// Use Queue.getFlags in fast-path to avoid updating the whole ring. +func (r *ring) getFlags() int { + return (int)(binary.LittleEndian.Uint16((r.rb)[ringFlagsOffset:])) +} + +// getHead returns the head pointer value from ring buffer. +// Use readHead in fast-path to avoid updating the whole ring. +func (r *ring) getHead() int { + return (int)(binary.LittleEndian.Uint16((r.rb)[ringHeadOffset:])) +} + +// getTail returns the tail pointer value from ring buffer. +// Use readTail in fast-path to avoid updating the whole ring. +func (r *ring) getTail() int { + return (int)(binary.LittleEndian.Uint16((r.rb)[ringTailOffset:])) +} + +func (r *ring) setCookie(val int) { + binary.LittleEndian.PutUint32((r.rb)[ringCookieOffset:], uint32(val)) +} + +func (r *ring) setFlags(val int) { + binary.LittleEndian.PutUint16((r.rb)[ringFlagsOffset:], uint16(val)) +} + +// setHead set the head pointer value int the ring buffer. +// Use writeHead in fast-path to avoid putting the whole ring into shared memory. +func (r *ring) setHead(val int) { + binary.LittleEndian.PutUint16((r.rb)[ringHeadOffset:], uint16(val)) +} + +// setTail set the tail pointer value int the ring buffer. +// Use writeTail in fast-path to avoid putting the whole ring into shared memory. +func (r *ring) setTail(val int) { + binary.LittleEndian.PutUint16((r.rb)[ringTailOffset:], uint16(val)) +} + +/* RING END */ + +// isInterrupt returns true if the queue is in interrupt mode +func (q *Queue) isInterrupt() bool { + return (q.getFlags() & ringFlagInterrupt) == 0 +} + +// interrupt performs an interrupt if the queue is in interrupt mode +func (q *Queue) interrupt() error { + if q.isInterrupt() { + buf := make([]byte, 8) + binary.PutUvarint(buf, 1) + n, err := syscall.Write(q.interruptFd, buf[:]) + if err != nil { + return err + } + if n != 8 { + return fmt.Errorf("Faild to write to eventfd") + } + } + + return nil +} |