blob: bc1c560862f72dac4aacc617eff5680c37c9f34f [file] [log] [blame]
/*
*------------------------------------------------------------------
* 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
}