blob: 15a8e87453cdd82dd5ff5707c6a9fd9a4b8549e2 [file] [log] [blame]
Jakub Grajciar07363a42020-04-02 10:02:17 +02001/*
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 provides the implementation of shared memory interface (memif).
19//
20// Memif network interfaces communicate using UNIX domain socket. This socket
21// must be first created using NewSocket(). Then interfaces can be added
22// to this socket using NewInterface(). To start communication on each socket
23// socket.StartPolling() must be called. socket.StopPolling() will stop
24// the communication. When the interface changes link status Connected and
25// Disconencted callbacks set in Arguments for each interface are called
26// respectively. Once the interface is connected rx and tx queues can be
27// aquired using interface.GetRxQueue() and interface.GetTxQueue().
28// Packets can be transmitted by calling queue.ReadPacket() on rx queues and
29// queue.WritePacket() on tx queues. If the interface is disconnected
30// queue.ReadPacket() and queue.WritePacket() MUST not be called.
31//
32// Data transmission is backed by shared memory. The driver works in
33// promiscuous mode only.
34package memif
35
36import (
37 "container/list"
38 "fmt"
39 "os"
40 "syscall"
41)
42
43const (
44 DefaultSocketFilename = "/run/vpp/memif.sock"
45 DefaultNumQueuePairs = 1
46 DefaultLog2RingSize = 10
47 DefaultPacketBufferSize = 2048
48)
49
50const mfd_allow_sealing = 2
51const sys_memfd_create = 319
52const f_add_seals = 1033
53const f_seal_shrink = 0x0002
54
55const efd_nonblock = 04000
56
57// ConnectedFunc is a callback called when an interface is connected
58type ConnectedFunc func(i *Interface) error
59
60// DisconnectedFunc is a callback called when an interface is disconnected
61type DisconnectedFunc func(i *Interface) error
62
63// MemoryConfig represents shared memory configuration
64type MemoryConfig struct {
65 NumQueuePairs uint16 // number of queue pairs
66 Log2RingSize uint8 // ring size as log2
67 PacketBufferSize uint32 // size of single packet buffer
68}
69
70// Arguments represent interface configuration
71type Arguments struct {
72 Id uint32 // Interface identifier unique across socket. Used to identify peer interface when connecting
73 IsMaster bool // Interface role master/slave
Nathan Skrzypczak176373c2021-05-07 19:39:07 +020074 Mode interfaceMode
Jakub Grajciar07363a42020-04-02 10:02:17 +020075 Name string
76 Secret [24]byte // optional parameter, secrets of the interfaces must match if they are to connect
77 MemoryConfig MemoryConfig
78 ConnectedFunc ConnectedFunc // callback called when interface changes status to connected
79 DisconnectedFunc DisconnectedFunc // callback called when interface changes status to disconnected
80 PrivateData interface{} // private data used by client program
81}
82
83// memoryRegion represents a shared memory mapped file
84type memoryRegion struct {
85 data []byte
86 size uint64
87 fd int
88 packetBufferOffset uint32
89}
90
91// Queue represents rx or tx queue
92type Queue struct {
93 ring *ring
94 i *Interface
95 lastHead uint16
96 lastTail uint16
97 interruptFd int
98}
99
100// Interface represents memif network interface
101type Interface struct {
102 args Arguments
103 run MemoryConfig
104 privateData interface{}
105 listRef *list.Element
106 socket *Socket
107 cc *controlChannel
108 remoteName string
109 peerName string
110 regions []memoryRegion
111 txQueues []Queue
112 rxQueues []Queue
113}
114
115// IsMaster returns true if the interfaces role is master, else returns false
116func (i *Interface) IsMaster() bool {
117 return i.args.IsMaster
118}
119
120// GetRemoteName returns the name of the application on which the peer
121// interface exists
122func (i *Interface) GetRemoteName() string {
123 return i.remoteName
124}
125
126// GetPeerName returns peer interfaces name
127func (i *Interface) GetPeerName() string {
128 return i.peerName
129}
130
131// GetName returens interfaces name
132func (i *Interface) GetName() string {
133 return i.args.Name
134}
135
136// GetMemoryConfig returns interfaces active memory config.
137// If interface is not connected the config is invalid.
138func (i *Interface) GetMemoryConfig() MemoryConfig {
139 return i.run
140}
141
142// GetRxQueue returns an rx queue specified by queue index
143func (i *Interface) GetRxQueue(qid int) (*Queue, error) {
144 if qid >= len(i.rxQueues) {
145 return nil, fmt.Errorf("Invalid Queue index")
146 }
147 return &i.rxQueues[qid], nil
148}
149
150// GetRxQueue returns a tx queue specified by queue index
151func (i *Interface) GetTxQueue(qid int) (*Queue, error) {
152 if qid >= len(i.txQueues) {
153 return nil, fmt.Errorf("Invalid Queue index")
154 }
155 return &i.txQueues[qid], nil
156}
157
158// GetEventFd returns queues interrupt event fd
159func (q *Queue) GetEventFd() (int, error) {
160 return q.interruptFd, nil
161}
162
163// GetFilename returns sockets filename
164func (socket *Socket) GetFilename() string {
165 return socket.filename
166}
167
168// close closes the queue
169func (q *Queue) close() {
170 syscall.Close(q.interruptFd)
171}
172
173// IsConnecting returns true if the interface is connecting
174func (i *Interface) IsConnecting() bool {
175 if i.cc != nil {
176 return true
177 }
178 return false
179}
180
181// IsConnected returns true if the interface is connected
182func (i *Interface) IsConnected() bool {
183 if i.cc != nil && i.cc.isConnected {
184 return true
185 }
186 return false
187}
188
189// Disconnect disconnects the interface
190func (i *Interface) Disconnect() (err error) {
191 if i.cc != nil {
192 // close control and disconenct interface
193 return i.cc.close(true, "Interface disconnected")
194 }
195 return nil
196}
197
198// disconnect finalizes interface disconnection
199func (i *Interface) disconnect() (err error) {
200 if i.cc == nil { // disconnected
201 return nil
202 }
203
204 err = i.args.DisconnectedFunc(i)
205 if err != nil {
206 return fmt.Errorf("DisconnectedFunc: ", err)
207 }
208
209 for _, q := range i.txQueues {
210 q.close()
211 }
212 i.txQueues = []Queue{}
213
214 for _, q := range i.rxQueues {
215 q.close()
216 }
217 i.rxQueues = []Queue{}
218
219 // unmap regions
220 for _, r := range i.regions {
221 err = syscall.Munmap(r.data)
222 if err != nil {
223 return err
224 }
225 err = syscall.Close(r.fd)
226 if err != nil {
227 return err
228 }
229 }
230 i.regions = nil
231 i.cc = nil
232
233 i.peerName = ""
234 i.remoteName = ""
235
236 return nil
237}
238
239// Delete deletes the interface
240func (i *Interface) Delete() (err error) {
241 i.Disconnect()
242
243 // remove referance on socket
244 i.socket.interfaceList.Remove(i.listRef)
245 i = nil
246
247 return nil
248}
249
250// GetSocket returns the socket the interface belongs to
251func (i *Interface) GetSocket() *Socket {
252 return i.socket
253}
254
255// GetPrivateDate returns interfaces private data
256func (i *Interface) GetPrivateData() interface{} {
257 return i.args.PrivateData
258}
259
260// GetId returns interfaces id
261func (i *Interface) GetId() uint32 {
262 return i.args.Id
263}
264
265// RoleToString returns 'Master' if isMaster os true, else returns 'Slave'
266func RoleToString(isMaster bool) string {
267 if isMaster {
268 return "Master"
269 }
270 return "Slave"
271}
272
273// RequestConnection is used by slave interface to connect to a socket and
274// create a control channel
275func (i *Interface) RequestConnection() error {
276 if i.IsMaster() {
277 return fmt.Errorf("Only slave can request connection")
278 }
279 // create socket
280 fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
281 if err != nil {
282 return fmt.Errorf("Failed to create UNIX domain socket: %v", err)
283 }
284 usa := &syscall.SockaddrUnix{Name: i.socket.filename}
285
286 // Connect to listener socket
287 err = syscall.Connect(fd, usa)
288 if err != nil {
289 return fmt.Errorf("Failed to connect socket %s : %v", i.socket.filename, err)
290 }
291
292 // Create control channel
293 i.cc, err = i.socket.addControlChannel(fd, i)
294 if err != nil {
295 return fmt.Errorf("Failed to create control channel: %v", err)
296 }
297
298 return nil
299}
300
301// NewInterface returns a new memif network interface. When creating an interface
302// it's id must be unique across socket with the exception of loopback interface
303// in which case the id is the same but role differs
304func (socket *Socket) NewInterface(args *Arguments) (*Interface, error) {
305 var err error
306 // make sure the ID is unique on this socket
307 for elt := socket.interfaceList.Front(); elt != nil; elt = elt.Next() {
308 i, ok := elt.Value.(*Interface)
309 if ok {
310 if i.args.Id == args.Id && i.args.IsMaster == args.IsMaster {
311 return nil, fmt.Errorf("Interface with id %u role %s already exists on this socket", args.Id, RoleToString(args.IsMaster))
312 }
313 }
314 }
315
316 // copy interface configuration
317 i := Interface{
318 args: *args,
319 }
320 // set default values
321 if i.args.MemoryConfig.NumQueuePairs == 0 {
322 i.args.MemoryConfig.NumQueuePairs = DefaultNumQueuePairs
323 }
324 if i.args.MemoryConfig.Log2RingSize == 0 {
325 i.args.MemoryConfig.Log2RingSize = DefaultLog2RingSize
326 }
327 if i.args.MemoryConfig.PacketBufferSize == 0 {
328 i.args.MemoryConfig.PacketBufferSize = DefaultPacketBufferSize
329 }
330
331 i.socket = socket
332
333 // append interface to the list
334 i.listRef = socket.interfaceList.PushBack(&i)
335
336 if i.args.IsMaster {
337 if socket.listener == nil {
338 err = socket.addListener()
339 if err != nil {
340 return nil, fmt.Errorf("Failed to create listener channel: %s", err)
341 }
342 }
343 }
344
345 return &i, nil
346}
347
348// eventFd returns an eventfd (SYS_EVENTFD2)
349func eventFd() (efd int, err error) {
350 u_efd, _, errno := syscall.Syscall(syscall.SYS_EVENTFD2, uintptr(0), uintptr(efd_nonblock), 0)
351 if errno != 0 {
352 return -1, os.NewSyscallError("eventfd", errno)
353 }
354 return int(u_efd), nil
355}
356
357// addRegions creates and adds a new memory region to the interface (slave only)
358func (i *Interface) addRegion(hasPacketBuffers bool, hasRings bool) (err error) {
359 var r memoryRegion
360
361 if hasRings {
362 r.packetBufferOffset = uint32((i.run.NumQueuePairs + i.run.NumQueuePairs) * (ringSize + descSize*(1<<i.run.Log2RingSize)))
363 } else {
364 r.packetBufferOffset = 0
365 }
366
367 if hasPacketBuffers {
368 r.size = uint64(r.packetBufferOffset + i.run.PacketBufferSize*uint32(1<<i.run.Log2RingSize)*uint32(i.run.NumQueuePairs+i.run.NumQueuePairs))
369 } else {
370 r.size = uint64(r.packetBufferOffset)
371 }
372
373 r.fd, err = memfdCreate()
374 if err != nil {
375 return err
376 }
377
378 _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(r.fd), uintptr(f_add_seals), uintptr(f_seal_shrink))
379 if errno != 0 {
380 syscall.Close(r.fd)
381 return fmt.Errorf("memfdCreate: %s", os.NewSyscallError("fcntl", errno))
382 }
383
384 err = syscall.Ftruncate(r.fd, int64(r.size))
385 if err != nil {
386 syscall.Close(r.fd)
387 r.fd = -1
388 return fmt.Errorf("memfdCreate: %s", err)
389 }
390
391 r.data, err = syscall.Mmap(r.fd, 0, int(r.size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
392 if err != nil {
393 return fmt.Errorf("addRegion: %s", err)
394 }
395
396 i.regions = append(i.regions, r)
397
398 return nil
399}
400
401// initializeRegions initializes interfaces regions (slave only)
402func (i *Interface) initializeRegions() (err error) {
403
404 err = i.addRegion(true, true)
405 if err != nil {
406 return fmt.Errorf("initializeRegions: %s", err)
407 }
408
409 return nil
410}
411
412// initializeQueues initializes interfaces queues (slave only)
413func (i *Interface) initializeQueues() (err error) {
414 var q *Queue
415 var desc descBuf
416 var slot int
417
418 desc = newDescBuf()
419 desc.setFlags(0)
420 desc.setRegion(0)
421 desc.setLength(int(i.run.PacketBufferSize))
422
423 for qid := 0; qid < int(i.run.NumQueuePairs); qid++ {
424 /* TX */
425 q = &Queue{
426 ring: i.newRing(0, ringTypeS2M, qid),
427 lastHead: 0,
428 lastTail: 0,
429 i: i,
430 }
431 q.ring.setCookie(cookie)
432 q.ring.setFlags(1)
433 q.interruptFd, err = eventFd()
434 if err != nil {
435 return err
436 }
437 q.putRing()
438 i.txQueues = append(i.txQueues, *q)
439
440 for j := 0; j < q.ring.size; j++ {
441 slot = qid*q.ring.size + j
442 desc.setOffset(int(i.regions[0].packetBufferOffset + uint32(slot)*i.run.PacketBufferSize))
443 q.putDescBuf(slot, desc)
444 }
445 }
446 for qid := 0; qid < int(i.run.NumQueuePairs); qid++ {
447 /* RX */
448 q = &Queue{
449 ring: i.newRing(0, ringTypeM2S, qid),
450 lastHead: 0,
451 lastTail: 0,
452 i: i,
453 }
454 q.ring.setCookie(cookie)
455 q.ring.setFlags(1)
456 q.interruptFd, err = eventFd()
457 if err != nil {
458 return err
459 }
460 q.putRing()
461 i.rxQueues = append(i.rxQueues, *q)
462
463 for j := 0; j < q.ring.size; j++ {
464 slot = qid*q.ring.size + j
465 desc.setOffset(int(i.regions[0].packetBufferOffset + uint32(slot)*i.run.PacketBufferSize))
466 q.putDescBuf(slot, desc)
467 }
468 }
469
470 return nil
471}
472
473// connect finalizes interface connection
474func (i *Interface) connect() (err error) {
475 for rid, _ := range i.regions {
476 r := &i.regions[rid]
477 if r.data == nil {
478 r.data, err = syscall.Mmap(r.fd, 0, int(r.size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
479 if err != nil {
480 return fmt.Errorf("Mmap: %s", err)
481 }
482 }
483 }
484
485 for _, q := range i.txQueues {
486 q.updateRing()
487
488 if q.ring.getCookie() != cookie {
489 return fmt.Errorf("Wrong cookie")
490 }
491
492 q.lastHead = 0
493 q.lastTail = 0
494 }
495
496 for _, q := range i.rxQueues {
497 q.updateRing()
498
499 if q.ring.getCookie() != cookie {
500 return fmt.Errorf("Wrong cookie")
501 }
502
503 q.lastHead = 0
504 q.lastTail = 0
505 }
506
507 return i.args.ConnectedFunc(i)
508}