346 lines
7.8 KiB
Go
346 lines
7.8 KiB
Go
|
/*
|
||
|
*------------------------------------------------------------------
|
||
|
* 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
|
||
|
}
|