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