gomemif: Add mode support
[vpp.git] / extras / gomemif / memif / memif.go
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2020 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 package memif
19
20 import (
21         "encoding/binary"
22         "fmt"
23         "syscall"
24 )
25
26 const cookie = 0x3E31F20
27
28 // VersionMajor is memif protocols major version
29 const VersionMajor = 2
30
31 // VersionMinor is memif protocols minor version
32 const VersionMinor = 0
33
34 // Version is memif protocols version as uint16
35 // (M-Major m-minor: MMMMMMMMmmmmmmmm)
36 const Version = ((VersionMajor << 8) | VersionMinor)
37
38 type msgType uint16
39
40 const (
41         msgTypeNone msgType = iota
42         msgTypeAck
43         msgTypeHello
44         msgTypeInit
45         msgTypeAddRegion
46         msgTypeAddRing
47         msgTypeConnect
48         msgTypeConnected
49         msgTypeDisconnect
50 )
51
52 type interfaceMode uint8
53
54 const (
55         InterfaceModeEthernet interfaceMode = iota
56         InterfaceModeIp
57         InterfaceModePuntInject
58 )
59
60 const msgSize = 128
61 const msgTypeSize = 2
62
63 const msgAddRingFlagS2M = (1 << 0)
64
65 // Descriptor flags
66 //
67 // next buffer present
68 const descFlagNext = (1 << 0)
69
70 // Ring flags
71 //
72 // Interrupt
73 const ringFlagInterrupt = 1
74
75 func min16(a uint16, b uint16) uint16 {
76         if a < b {
77                 return a
78         }
79         return b
80 }
81
82 func min8(a uint8, b uint8) uint8 {
83         if a < b {
84                 return a
85         }
86         return b
87 }
88
89 type MsgHello struct {
90         // app name
91         Name            [32]byte
92         VersionMin      uint16
93         VersionMax      uint16
94         MaxRegion       uint16
95         MaxRingM2S      uint16
96         MaxRingS2M      uint16
97         MaxLog2RingSize uint8
98 }
99
100 type MsgInit struct {
101         Version uint16
102         Id      uint32
103         Mode    interfaceMode
104         Secret  [24]byte
105         // app name
106         Name [32]byte
107 }
108
109 type MsgAddRegion struct {
110         Index uint16
111         Size  uint64
112 }
113
114 type MsgAddRing struct {
115         Flags          uint16
116         Index          uint16
117         Region         uint16
118         Offset         uint32
119         RingSizeLog2   uint8
120         PrivateHdrSize uint16
121 }
122
123 type MsgConnect struct {
124         // interface name
125         Name [32]byte
126 }
127
128 type MsgConnected struct {
129         // interface name
130         Name [32]byte
131 }
132
133 type MsgDisconnect struct {
134         Code   uint32
135         String [96]byte
136 }
137
138 /* DESCRIPTOR BEGIN */
139
140 const descSize = 16
141
142 // desc field offsets
143 const descFlagsOffset = 0
144 const descRegionOffset = 2
145 const descLengthOffset = 4
146 const descOffsetOffset = 8
147 const descMetadataOffset = 12
148
149 // descBuf represents a memif descriptor as array of bytes
150 type descBuf []byte
151
152 // newDescBuf returns new descriptor buffer
153 func newDescBuf() descBuf {
154         return make(descBuf, descSize)
155 }
156
157 // getDescBuff copies descriptor from shared memory to descBuf
158 func (q *Queue) getDescBuf(slot int, db descBuf) {
159         copy(db, q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize:])
160 }
161
162 // putDescBuf copies contents of descriptor buffer into shared memory
163 func (q *Queue) putDescBuf(slot int, db descBuf) {
164         copy(q.i.regions[q.ring.region].data[q.ring.offset+ringSize+slot*descSize:], db)
165 }
166
167 func (db descBuf) getFlags() int {
168         return (int)(binary.LittleEndian.Uint16((db)[descFlagsOffset:]))
169 }
170
171 func (db descBuf) getRegion() int {
172         return (int)(binary.LittleEndian.Uint16((db)[descRegionOffset:]))
173 }
174
175 func (db descBuf) getLength() int {
176         return (int)(binary.LittleEndian.Uint32((db)[descLengthOffset:]))
177 }
178
179 func (db descBuf) getOffset() int {
180         return (int)(binary.LittleEndian.Uint32((db)[descOffsetOffset:]))
181 }
182
183 func (db descBuf) getMetadata() int {
184         return (int)(binary.LittleEndian.Uint32((db)[descMetadataOffset:]))
185 }
186
187 func (db descBuf) setFlags(val int) {
188         binary.LittleEndian.PutUint16((db)[descFlagsOffset:], uint16(val))
189 }
190
191 func (db descBuf) setRegion(val int) {
192         binary.LittleEndian.PutUint16((db)[descRegionOffset:], uint16(val))
193 }
194
195 func (db descBuf) setLength(val int) {
196         binary.LittleEndian.PutUint32((db)[descLengthOffset:], uint32(val))
197 }
198
199 func (db descBuf) setOffset(val int) {
200         binary.LittleEndian.PutUint32((db)[descOffsetOffset:], uint32(val))
201 }
202
203 func (db descBuf) setMetadata(val int) {
204         binary.LittleEndian.PutUint32((db)[descMetadataOffset:], uint32(val))
205 }
206
207 /* DESCRIPTOR END */
208
209 /* RING BEGIN */
210
211 type ringType uint8
212
213 const (
214         ringTypeS2M ringType = iota
215         ringTypeM2S
216 )
217
218 const ringSize = 128
219
220 // ring field offsets
221 const ringCookieOffset = 0
222 const ringFlagsOffset = 4
223 const ringHeadOffset = 6
224 const ringTailOffset = 64
225
226 // ringBuf represents a memif ring as array of bytes
227 type ringBuf []byte
228
229 type ring struct {
230         ringType ringType
231         size     int
232         log2Size int
233         region   int
234         rb       ringBuf
235         offset   int
236 }
237
238 // newRing returns new memif ring based on data received in msgAddRing (master only)
239 func newRing(regionIndex int, ringType ringType, ringOffset int, log2RingSize int) *ring {
240         r := &ring{
241                 ringType: ringType,
242                 size:     (1 << log2RingSize),
243                 log2Size: log2RingSize,
244                 rb:       make(ringBuf, ringSize),
245                 offset:   ringOffset,
246         }
247
248         return r
249 }
250
251 // newRing returns a new memif ring
252 func (i *Interface) newRing(regionIndex int, ringType ringType, ringIndex int) *ring {
253         r := &ring{
254                 ringType: ringType,
255                 size:     (1 << i.run.Log2RingSize),
256                 log2Size: int(i.run.Log2RingSize),
257                 rb:       make(ringBuf, ringSize),
258         }
259
260         rSize := ringSize + descSize*r.size
261         if r.ringType == ringTypeS2M {
262                 r.offset = 0
263         } else {
264                 r.offset = int(i.run.NumQueuePairs) * rSize
265         }
266         r.offset += ringIndex * rSize
267
268         return r
269 }
270
271 // putRing put the ring to the shared memory
272 func (q *Queue) putRing() {
273         copy(q.i.regions[q.ring.region].data[q.ring.offset:], q.ring.rb)
274 }
275
276 // updateRing updates ring with data from shared memory
277 func (q *Queue) updateRing() {
278         copy(q.ring.rb, q.i.regions[q.ring.region].data[q.ring.offset:])
279 }
280
281 func (r *ring) getCookie() int {
282         return (int)(binary.LittleEndian.Uint32((r.rb)[ringCookieOffset:]))
283 }
284
285 // getFlags returns the flags value from ring buffer
286 // Use Queue.getFlags in fast-path to avoid updating the whole ring.
287 func (r *ring) getFlags() int {
288         return (int)(binary.LittleEndian.Uint16((r.rb)[ringFlagsOffset:]))
289 }
290
291 // getHead returns the head pointer value from ring buffer.
292 // Use readHead in fast-path to avoid updating the whole ring.
293 func (r *ring) getHead() int {
294         return (int)(binary.LittleEndian.Uint16((r.rb)[ringHeadOffset:]))
295 }
296
297 // getTail returns the tail pointer value from ring buffer.
298 // Use readTail in fast-path to avoid updating the whole ring.
299 func (r *ring) getTail() int {
300         return (int)(binary.LittleEndian.Uint16((r.rb)[ringTailOffset:]))
301 }
302
303 func (r *ring) setCookie(val int) {
304         binary.LittleEndian.PutUint32((r.rb)[ringCookieOffset:], uint32(val))
305 }
306
307 func (r *ring) setFlags(val int) {
308         binary.LittleEndian.PutUint16((r.rb)[ringFlagsOffset:], uint16(val))
309 }
310
311 // setHead set the head pointer value int the ring buffer.
312 // Use writeHead in fast-path to avoid putting the whole ring into shared memory.
313 func (r *ring) setHead(val int) {
314         binary.LittleEndian.PutUint16((r.rb)[ringHeadOffset:], uint16(val))
315 }
316
317 // setTail set the tail pointer value int the ring buffer.
318 // Use writeTail in fast-path to avoid putting the whole ring into shared memory.
319 func (r *ring) setTail(val int) {
320         binary.LittleEndian.PutUint16((r.rb)[ringTailOffset:], uint16(val))
321 }
322
323 /* RING END */
324
325 // isInterrupt returns true if the queue is in interrupt mode
326 func (q *Queue) isInterrupt() bool {
327         return (q.getFlags() & ringFlagInterrupt) == 0
328 }
329
330 // interrupt performs an interrupt if the queue is in interrupt mode
331 func (q *Queue) interrupt() error {
332         if q.isInterrupt() {
333                 buf := make([]byte, 8)
334                 binary.PutUvarint(buf, 1)
335                 n, err := syscall.Write(q.interruptFd, buf[:])
336                 if err != nil {
337                         return err
338                 }
339                 if n != 8 {
340                         return fmt.Errorf("Faild to write to eventfd")
341                 }
342         }
343
344         return nil
345 }