| /* |
| *------------------------------------------------------------------ |
| * 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 |
| } |