memif: fix coverity warnings
[vpp.git] / src / plugins / memif / memif.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2016 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 #define _GNU_SOURCE
19 #include <stdint.h>
20 #include <net/if.h>
21 #include <sys/types.h>
22 #include <fcntl.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <sys/uio.h>
27 #include <sys/mman.h>
28 #include <sys/prctl.h>
29 #include <inttypes.h>
30
31 #include <vlib/vlib.h>
32 #include <vlib/unix/unix.h>
33 #include <vnet/plugin/plugin.h>
34 #include <vnet/ethernet/ethernet.h>
35 #include <vpp/app/version.h>
36 #include <memif/memif.h>
37
38 #define MEMIF_DEBUG 1
39
40 #if MEMIF_DEBUG == 1
41 #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
42 #define DEBUG_UNIX_LOG(...) clib_unix_warning(__VA_ARGS__)
43 #else
44 #define DEBUG_LOG(...)
45 #endif
46
47 memif_main_t memif_main;
48
49 static clib_error_t *memif_conn_fd_read_ready (unix_file_t * uf);
50 static clib_error_t *memif_int_fd_read_ready (unix_file_t * uf);
51
52 static u32
53 memif_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, u32 flags)
54 {
55   /* nothing for now */
56   return 0;
57 }
58
59 static void
60 memif_remove_pending_conn (memif_pending_conn_t * pending_conn)
61 {
62   memif_main_t *mm = &memif_main;
63
64   unix_file_del (&unix_main,
65                  unix_main.file_pool + pending_conn->connection.index);
66   pool_put (mm->pending_conns, pending_conn);
67 }
68
69 static void
70 memif_connect (vlib_main_t * vm, memif_if_t * mif)
71 {
72   vnet_main_t *vnm = vnet_get_main ();
73   int num_rings = mif->num_s2m_rings + mif->num_m2s_rings;
74   memif_ring_data_t *rd = NULL;
75   vnet_hw_interface_t *hw;
76   u8 rid, rx_queues;
77   int ret;
78
79   vec_validate_aligned (mif->ring_data, num_rings - 1, CLIB_CACHE_LINE_BYTES);
80   vec_foreach (rd, mif->ring_data)
81   {
82     rd->last_head = 0;
83   }
84
85   mif->flags &= ~MEMIF_IF_FLAG_CONNECTING;
86   mif->flags |= MEMIF_IF_FLAG_CONNECTED;
87   vnet_hw_interface_set_flags (vnm, mif->hw_if_index,
88                                VNET_HW_INTERFACE_FLAG_LINK_UP);
89
90   hw = vnet_get_hw_interface (vnm, mif->hw_if_index);
91   hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE;
92   vnet_hw_interface_set_input_node (vnm, mif->hw_if_index,
93                                     memif_input_node.index);
94   rx_queues = memif_get_rx_queues (mif);
95   for (rid = 0; rid < rx_queues; rid++)
96     {
97       vnet_hw_interface_assign_rx_thread (vnm, mif->hw_if_index, rid, ~0);
98       ret = vnet_hw_interface_set_rx_mode (vnm, mif->hw_if_index, rid,
99                                            VNET_HW_INTERFACE_RX_MODE_INTERRUPT);
100       if (ret)
101         DEBUG_LOG ("Warning: unable to set rx mode for interface %d "
102                    "queue %d: rc=%d", mif->hw_if_index, rid, ret);
103     }
104 }
105
106 static void
107 memif_disconnect_do (vlib_main_t * vm, memif_if_t * mif)
108 {
109   vnet_main_t *vnm = vnet_get_main ();
110   u8 rid, rx_queues;
111   int rv;
112   memif_shm_t **shm;
113
114   mif->flags &= ~(MEMIF_IF_FLAG_CONNECTED | MEMIF_IF_FLAG_CONNECTING);
115   if (mif->hw_if_index != ~0)
116     vnet_hw_interface_set_flags (vnm, mif->hw_if_index, 0);
117
118   if (mif->connection.index != ~0)
119     {
120       unix_file_del (&unix_main, unix_main.file_pool + mif->connection.index);
121       mif->connection.index = ~0;
122       mif->connection.fd = -1;  /* closed in unix_file_del */
123     }
124
125   rx_queues = memif_get_rx_queues (mif);
126   for (rid = 0; rid < rx_queues; rid++)
127     {
128       rv = vnet_hw_interface_unassign_rx_thread (vnm, mif->hw_if_index, rid);
129       if (rv)
130         DEBUG_LOG ("Warning: unable to unassign interface %d, "
131                    "queue %d: rc=%d", mif->hw_if_index, rid, rv);
132     }
133
134   shm = (memif_shm_t **) mif->regions;
135   rv = munmap ((void *) *shm, mif->shared_mem_size);
136   if (rv)
137     DEBUG_UNIX_LOG ("Error: failed munmap call");
138
139   vec_free (mif->regions);
140 }
141
142 void
143 memif_disconnect (vlib_main_t * vm, memif_if_t * mif)
144 {
145   if (mif->interrupt_line.index != ~0)
146     {
147       unix_file_del (&unix_main,
148                      unix_main.file_pool + mif->interrupt_line.index);
149       mif->interrupt_line.index = ~0;
150       mif->interrupt_line.fd = -1;      /* closed in unix_file_del */
151     }
152
153   memif_disconnect_do (vm, mif);
154 }
155
156 static clib_error_t *
157 memif_process_connect_req (memif_pending_conn_t * pending_conn,
158                            memif_msg_t * req, struct ucred *slave_cr,
159                            int shm_fd, int int_fd)
160 {
161   memif_main_t *mm = &memif_main;
162   vlib_main_t *vm = vlib_get_main ();
163   int fd = pending_conn->connection.fd;
164   unix_file_t *uf = 0;
165   memif_if_t *mif = 0;
166   memif_msg_t resp = { 0 };
167   unix_file_t template = { 0 };
168   void *shm;
169   uword *p;
170   u8 retval = 0;
171   static clib_error_t *error = 0;
172
173   if (shm_fd == -1)
174     {
175       DEBUG_LOG
176         ("Connection request is missing shared memory file descriptor");
177       retval = 1;
178       goto response;
179     }
180
181   if (int_fd == -1)
182     {
183       DEBUG_LOG
184         ("Connection request is missing interrupt line file descriptor");
185       retval = 2;
186       goto response;
187     }
188
189   if (slave_cr == NULL)
190     {
191       DEBUG_LOG ("Connection request is missing slave credentials");
192       retval = 3;
193       goto response;
194     }
195
196   p = mhash_get (&mm->if_index_by_key, &req->key);
197   if (!p)
198     {
199       DEBUG_LOG
200         ("Connection request with unmatched key (0x%" PRIx64 ")", req->key);
201       retval = 4;
202       goto response;
203     }
204
205   mif = vec_elt_at_index (mm->interfaces, *p);
206   if (mif->listener_index != pending_conn->listener_index)
207     {
208       DEBUG_LOG
209         ("Connection request with non-matching listener (%d vs. %d)",
210          pending_conn->listener_index, mif->listener_index);
211       retval = 5;
212       goto response;
213     }
214
215   if (mif->flags & MEMIF_IF_FLAG_IS_SLAVE)
216     {
217       DEBUG_LOG ("Memif slave does not accept connection requests");
218       retval = 6;
219       goto response;
220     }
221
222   if (mif->connection.fd != -1)
223     {
224       DEBUG_LOG
225         ("Memif with key 0x%" PRIx64 " is already connected", mif->key);
226       retval = 7;
227       goto response;
228     }
229
230   if ((mif->flags & MEMIF_IF_FLAG_ADMIN_UP) == 0)
231     {
232       /* just silently decline the request */
233       retval = 8;
234       goto response;
235     }
236
237   if (req->shared_mem_size < sizeof (memif_shm_t))
238     {
239       DEBUG_LOG
240         ("Unexpectedly small shared memory segment received from slave.");
241       retval = 9;
242       goto response;
243     }
244
245   if ((shm =
246        mmap (NULL, req->shared_mem_size, PROT_READ | PROT_WRITE, MAP_SHARED,
247              shm_fd, 0)) == MAP_FAILED)
248     {
249       DEBUG_UNIX_LOG
250         ("Failed to map shared memory segment received from slave memif");
251       error = clib_error_return_unix (0, "mmap fd %d", shm_fd);
252       retval = 10;
253       goto response;
254     }
255
256   if (((memif_shm_t *) shm)->cookie != 0xdeadbeef)
257     {
258       DEBUG_LOG
259         ("Possibly corrupted shared memory segment received from slave memif");
260       munmap (shm, req->shared_mem_size);
261       retval = 11;
262       goto response;
263     }
264
265   mif->shared_mem_size = req->shared_mem_size;
266   mif->log2_ring_size = req->log2_ring_size;
267   mif->num_s2m_rings = req->num_s2m_rings;
268   mif->num_m2s_rings = req->num_m2s_rings;
269   mif->buffer_size = req->buffer_size;
270   mif->remote_pid = slave_cr->pid;
271   mif->remote_uid = slave_cr->uid;
272   vec_add1 (mif->regions, shm);
273
274   /* register interrupt line */
275   mif->interrupt_line.fd = int_fd;
276   template.read_function = memif_int_fd_read_ready;
277   template.file_descriptor = int_fd;
278   template.private_data = mif->if_index;
279   mif->interrupt_line.index = unix_file_add (&unix_main, &template);
280
281   /* change context for future messages */
282   uf = vec_elt_at_index (unix_main.file_pool, pending_conn->connection.index);
283   uf->private_data = mif->if_index << 1;
284   mif->connection = pending_conn->connection;
285   pool_put (mm->pending_conns, pending_conn);
286   pending_conn = 0;
287
288   memif_connect (vm, mif);
289
290 response:
291   resp.version = MEMIF_VERSION;
292   resp.type = MEMIF_MSG_TYPE_CONNECT_RESP;
293   resp.retval = retval;
294   if (send (fd, &resp, sizeof (resp), 0) < 0)
295     {
296       DEBUG_UNIX_LOG ("Failed to send connection response");
297       error = clib_error_return_unix (0, "send fd %d", fd);
298       if (pending_conn)
299         memif_remove_pending_conn (pending_conn);
300       else
301         memif_disconnect (vm, mif);
302     }
303   if (retval > 0)
304     {
305       if (shm_fd >= 0)
306         close (shm_fd);
307       if (int_fd >= 0)
308         close (int_fd);
309     }
310   return error;
311 }
312
313 static clib_error_t *
314 memif_process_connect_resp (memif_if_t * mif, memif_msg_t * resp)
315 {
316   vlib_main_t *vm = vlib_get_main ();
317
318   if ((mif->flags & MEMIF_IF_FLAG_IS_SLAVE) == 0)
319     {
320       DEBUG_LOG ("Memif master does not accept connection responses");
321       return 0;
322     }
323
324   if ((mif->flags & MEMIF_IF_FLAG_CONNECTING) == 0)
325     {
326       DEBUG_LOG ("Unexpected connection response");
327       return 0;
328     }
329
330   if (resp->retval == 0)
331     memif_connect (vm, mif);
332   else
333     memif_disconnect (vm, mif);
334
335   return 0;
336 }
337
338 static clib_error_t *
339 memif_conn_fd_read_ready (unix_file_t * uf)
340 {
341   memif_main_t *mm = &memif_main;
342   vlib_main_t *vm = vlib_get_main ();
343   memif_if_t *mif = 0;
344   memif_pending_conn_t *pending_conn = 0;
345   int fd_array[2] = { -1, -1 };
346   char ctl[CMSG_SPACE (sizeof (fd_array)) +
347            CMSG_SPACE (sizeof (struct ucred))] = { 0 };
348   struct msghdr mh = { 0 };
349   struct iovec iov[1];
350   struct ucred *cr = 0;
351   memif_msg_t msg = { 0 };
352   struct cmsghdr *cmsg;
353   ssize_t size;
354   static clib_error_t *error = 0;
355
356   iov[0].iov_base = (void *) &msg;
357   iov[0].iov_len = sizeof (memif_msg_t);
358   mh.msg_iov = iov;
359   mh.msg_iovlen = 1;
360   mh.msg_control = ctl;
361   mh.msg_controllen = sizeof (ctl);
362
363   /* grab the appropriate context */
364   if (uf->private_data & 1)
365     pending_conn = vec_elt_at_index (mm->pending_conns,
366                                      uf->private_data >> 1);
367   else
368     mif = vec_elt_at_index (mm->interfaces, uf->private_data >> 1);
369
370   /* Stop workers to avoid end of the world */
371   vlib_worker_thread_barrier_sync (vlib_get_main ());
372
373   /* receive the incoming message */
374   size = recvmsg (uf->file_descriptor, &mh, 0);
375   if (size != sizeof (memif_msg_t))
376     {
377       if (size == 0)
378         {
379           if (pending_conn)
380             memif_remove_pending_conn (pending_conn);
381           else
382             memif_disconnect_do (vm, mif);
383           goto return_ok;
384         }
385
386       DEBUG_UNIX_LOG ("Malformed message received on fd %d",
387                       uf->file_descriptor);
388       error = clib_error_return_unix (0, "recvmsg fd %d",
389                                       uf->file_descriptor);
390       goto disconnect;
391     }
392
393   /* check version of the sender's memif plugin */
394   if (msg.version != MEMIF_VERSION)
395     {
396       DEBUG_LOG ("Memif version mismatch");
397       goto disconnect;
398     }
399
400   /* process the message based on its type */
401   switch (msg.type)
402     {
403     case MEMIF_MSG_TYPE_CONNECT_REQ:
404       if (pending_conn == 0)
405         DEBUG_LOG ("Received unexpected connection request");
406       else
407         {
408           /* Read anciliary data */
409           cmsg = CMSG_FIRSTHDR (&mh);
410           while (cmsg)
411             {
412               if (cmsg->cmsg_level == SOL_SOCKET
413                   && cmsg->cmsg_type == SCM_CREDENTIALS)
414                 {
415                   cr = (struct ucred *) CMSG_DATA (cmsg);
416                 }
417               else if (cmsg->cmsg_level == SOL_SOCKET
418                        && cmsg->cmsg_type == SCM_RIGHTS)
419                 {
420                   memcpy (fd_array, CMSG_DATA (cmsg), sizeof (fd_array));
421                 }
422               cmsg = CMSG_NXTHDR (&mh, cmsg);
423             }
424           error = memif_process_connect_req (pending_conn, &msg, cr,
425                                              fd_array[0], fd_array[1]);
426         }
427       break;
428
429     case MEMIF_MSG_TYPE_CONNECT_RESP:
430       if (mif == 0)
431         DEBUG_LOG ("Received unexpected connection response");
432       else
433         error = memif_process_connect_resp (mif, &msg);
434       break;
435
436     case MEMIF_MSG_TYPE_DISCONNECT:
437       goto disconnect;
438
439     default:
440       DEBUG_LOG ("Received unknown message type");
441       goto disconnect;
442     }
443
444 return_ok:
445   vlib_worker_thread_barrier_release (vlib_get_main ());
446   return error;
447
448 disconnect:
449   if (pending_conn)
450     memif_remove_pending_conn (pending_conn);
451   else
452     memif_disconnect (vm, mif);
453   vlib_worker_thread_barrier_release (vlib_get_main ());
454   return error;
455 }
456
457 static clib_error_t *
458 memif_int_fd_read_ready (unix_file_t * uf)
459 {
460   memif_main_t *mm = &memif_main;
461   vnet_main_t *vnm = vnet_get_main ();
462   memif_if_t *mif = vec_elt_at_index (mm->interfaces, uf->private_data);
463   u8 b;
464   ssize_t size;
465
466   size = read (uf->file_descriptor, &b, sizeof (b));
467   if (0 == size)
468     {
469       /* interrupt line was disconnected */
470       unix_file_del (&unix_main,
471                      unix_main.file_pool + mif->interrupt_line.index);
472       mif->interrupt_line.index = ~0;
473       mif->interrupt_line.fd = -1;
474     }
475   else if (size < 0)
476     DEBUG_UNIX_LOG ("Failed to read from socket");
477   else
478     vnet_device_input_set_interrupt_pending (vnm, mif->hw_if_index, b);
479
480   return 0;
481 }
482
483 static clib_error_t *
484 memif_conn_fd_accept_ready (unix_file_t * uf)
485 {
486   memif_main_t *mm = &memif_main;
487   memif_listener_t *listener = 0;
488   memif_pending_conn_t *pending_conn = 0;
489   int addr_len;
490   struct sockaddr_un client;
491   int conn_fd;
492   unix_file_t template = { 0 };
493
494   listener = pool_elt_at_index (mm->listeners, uf->private_data);
495
496   addr_len = sizeof (client);
497   conn_fd = accept (uf->file_descriptor,
498                     (struct sockaddr *) &client, (socklen_t *) & addr_len);
499
500   if (conn_fd < 0)
501     return clib_error_return_unix (0, "accept fd %d", uf->file_descriptor);
502
503   pool_get (mm->pending_conns, pending_conn);
504   pending_conn->index = pending_conn - mm->pending_conns;
505   pending_conn->listener_index = listener->index;
506   pending_conn->connection.fd = conn_fd;
507
508   template.read_function = memif_conn_fd_read_ready;
509   template.file_descriptor = conn_fd;
510   template.private_data = (pending_conn->index << 1) | 1;
511   pending_conn->connection.index = unix_file_add (&unix_main, &template);
512
513   return 0;
514 }
515
516 static void
517 memif_connect_master (vlib_main_t * vm, memif_if_t * mif)
518 {
519   memif_msg_t msg;
520   struct msghdr mh = { 0 };
521   struct iovec iov[1];
522   struct cmsghdr *cmsg;
523   int mfd = -1;
524   int rv;
525   int fd_array[2] = { -1, -1 };
526   char ctl[CMSG_SPACE (sizeof (fd_array))];
527   memif_ring_t *ring = NULL;
528   int i, j;
529   void *shm = 0;
530   u64 buffer_offset;
531   unix_file_t template = { 0 };
532
533   msg.version = MEMIF_VERSION;
534   msg.type = MEMIF_MSG_TYPE_CONNECT_REQ;
535   msg.key = mif->key;
536   msg.log2_ring_size = mif->log2_ring_size;
537   msg.num_s2m_rings = mif->num_s2m_rings;
538   msg.num_m2s_rings = mif->num_m2s_rings;
539   msg.buffer_size = mif->buffer_size;
540
541   buffer_offset = sizeof (memif_shm_t) +
542     (mif->num_s2m_rings + mif->num_m2s_rings) *
543     (sizeof (memif_ring_t) +
544      sizeof (memif_desc_t) * (1 << mif->log2_ring_size));
545
546   msg.shared_mem_size = buffer_offset +
547     mif->buffer_size * (1 << mif->log2_ring_size) * (mif->num_s2m_rings +
548                                                      mif->num_m2s_rings);
549
550   if ((mfd = memfd_create ("shared mem", MFD_ALLOW_SEALING)) == -1)
551     {
552       DEBUG_LOG ("Failed to create anonymous file");
553       goto error;
554     }
555
556   if ((fcntl (mfd, F_ADD_SEALS, F_SEAL_SHRINK)) == -1)
557     {
558       DEBUG_UNIX_LOG ("Failed to seal an anonymous file off from truncating");
559       goto error;
560     }
561
562   if ((ftruncate (mfd, msg.shared_mem_size)) == -1)
563     {
564       DEBUG_UNIX_LOG ("Failed to extend the size of an anonymous file");
565       goto error;
566     }
567
568   if ((shm = mmap (NULL, msg.shared_mem_size, PROT_READ | PROT_WRITE,
569                    MAP_SHARED, mfd, 0)) == MAP_FAILED)
570     {
571       DEBUG_UNIX_LOG ("Failed to map anonymous file into memory");
572       goto error;
573     }
574
575   mif->shared_mem_size = msg.shared_mem_size;
576   vec_add1 (mif->regions, shm);
577   ((memif_shm_t *) mif->regions[0])->cookie = 0xdeadbeef;
578
579   for (i = 0; i < mif->num_s2m_rings; i++)
580     {
581       ring = memif_get_ring (mif, MEMIF_RING_S2M, i);
582       ring->head = ring->tail = 0;
583       for (j = 0; j < (1 << mif->log2_ring_size); j++)
584         {
585           u16 slot = i * (1 << mif->log2_ring_size) + j;
586           ring->desc[j].region = 0;
587           ring->desc[j].offset =
588             buffer_offset + (u32) (slot * mif->buffer_size);
589           ring->desc[j].buffer_length = mif->buffer_size;
590         }
591     }
592   for (i = 0; i < mif->num_m2s_rings; i++)
593     {
594       ring = memif_get_ring (mif, MEMIF_RING_M2S, i);
595       ring->head = ring->tail = 0;
596       for (j = 0; j < (1 << mif->log2_ring_size); j++)
597         {
598           u16 slot =
599             (i + mif->num_s2m_rings) * (1 << mif->log2_ring_size) + j;
600           ring->desc[j].region = 0;
601           ring->desc[j].offset =
602             buffer_offset + (u32) (slot * mif->buffer_size);
603           ring->desc[j].buffer_length = mif->buffer_size;
604         }
605     }
606
607   iov[0].iov_base = (void *) &msg;
608   iov[0].iov_len = sizeof (memif_msg_t);
609   mh.msg_iov = iov;
610   mh.msg_iovlen = 1;
611
612   /* create interrupt socket */
613   if (socketpair (AF_UNIX, SOCK_STREAM, 0, fd_array) < 0)
614     {
615       DEBUG_UNIX_LOG ("Failed to create a pair of connected sockets");
616       goto error;
617     }
618
619   mif->interrupt_line.fd = fd_array[0];
620   template.read_function = memif_int_fd_read_ready;
621   template.file_descriptor = mif->interrupt_line.fd;
622   template.private_data = mif->if_index;
623   mif->interrupt_line.index = unix_file_add (&unix_main, &template);
624
625   memset (&ctl, 0, sizeof (ctl));
626   mh.msg_control = ctl;
627   mh.msg_controllen = sizeof (ctl);
628   cmsg = CMSG_FIRSTHDR (&mh);
629   cmsg->cmsg_len = CMSG_LEN (sizeof (fd_array));
630   cmsg->cmsg_level = SOL_SOCKET;
631   cmsg->cmsg_type = SCM_RIGHTS;
632   fd_array[0] = mfd;
633   memcpy (CMSG_DATA (cmsg), fd_array, sizeof (fd_array));
634
635   mif->flags |= MEMIF_IF_FLAG_CONNECTING;
636   rv = sendmsg (mif->connection.fd, &mh, 0);
637   if (rv < 0)
638     {
639       DEBUG_UNIX_LOG ("Failed to send memif connection request");
640       goto error;
641     }
642
643   /* No need to keep the descriptor open,
644    * mmap creates an extra reference to the underlying file */
645   close (mfd);
646   mfd = -1;
647   /* This FD is given to peer, so we can close it */
648   close (fd_array[1]);
649   fd_array[1] = -1;
650   return;
651
652 error:
653   if (mfd > -1)
654     close (mfd);
655   if (fd_array[1] > -1)
656     close (fd_array[1]);
657   memif_disconnect (vm, mif);
658 }
659
660 static uword
661 memif_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
662 {
663   memif_main_t *mm = &memif_main;
664   memif_if_t *mif;
665   struct sockaddr_un sun;
666   int sockfd;
667   uword *event_data = 0, event_type;
668   unix_file_t template = { 0 };
669   u8 enabled = 0;
670   f64 start_time, last_run_duration = 0, now;
671
672   sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
673   if (sockfd < 0)
674     {
675       DEBUG_UNIX_LOG ("socket AF_UNIX");
676       return 0;
677     }
678   sun.sun_family = AF_UNIX;
679   template.read_function = memif_conn_fd_read_ready;
680
681   while (1)
682     {
683       if (enabled)
684         vlib_process_wait_for_event_or_clock (vm,
685                                               (f64) 3 - last_run_duration);
686       else
687         vlib_process_wait_for_event (vm);
688
689       event_type = vlib_process_get_events (vm, &event_data);
690       vec_reset_length (event_data);
691
692       switch (event_type)
693         {
694         case ~0:
695           break;
696         case MEMIF_PROCESS_EVENT_START:
697           enabled = 1;
698           break;
699         case MEMIF_PROCESS_EVENT_STOP:
700           enabled = 0;
701           continue;
702         default:
703           ASSERT (0);
704         }
705
706       last_run_duration = start_time = vlib_time_now (vm);
707       /* *INDENT-OFF* */
708       pool_foreach (mif, mm->interfaces,
709         ({
710           /* Allow no more than 10us without a pause */
711           now = vlib_time_now (vm);
712           if (now > start_time + 10e-6)
713             {
714               vlib_process_suspend (vm, 100e-6);        /* suspend for 100 us */
715               start_time = vlib_time_now (vm);
716             }
717
718           if ((mif->flags & MEMIF_IF_FLAG_ADMIN_UP) == 0)
719             continue;
720
721           if (mif->flags & MEMIF_IF_FLAG_CONNECTING)
722             continue;
723
724           if (mif->flags & MEMIF_IF_FLAG_CONNECTED)
725             continue;
726
727           if (mif->flags & MEMIF_IF_FLAG_IS_SLAVE)
728             {
729               strncpy (sun.sun_path, (char *) mif->socket_filename,
730                        sizeof (sun.sun_path) - 1);
731
732               if (connect
733                   (sockfd, (struct sockaddr *) &sun,
734                    sizeof (struct sockaddr_un)) == 0)
735                 {
736                   mif->connection.fd = sockfd;
737                   template.file_descriptor = sockfd;
738                   template.private_data = mif->if_index << 1;
739                   mif->connection.index = unix_file_add (&unix_main, &template);
740                   memif_connect_master (vm, mif);
741
742                   /* grab another fd */
743                   sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
744                   if (sockfd < 0)
745                     {
746                       DEBUG_UNIX_LOG ("socket AF_UNIX");
747                       return 0;
748                     }
749                 }
750             }
751         }));
752       /* *INDENT-ON* */
753       last_run_duration = vlib_time_now (vm) - last_run_duration;
754     }
755   return 0;
756 }
757
758 /* *INDENT-OFF* */
759 VLIB_REGISTER_NODE (memif_process_node,static) = {
760   .function = memif_process,
761   .type = VLIB_NODE_TYPE_PROCESS,
762   .name = "memif-process",
763 };
764 /* *INDENT-ON* */
765
766 static void
767 memif_close_if (memif_main_t * mm, memif_if_t * mif)
768 {
769   vlib_main_t *vm = vlib_get_main ();
770   memif_listener_t *listener = 0;
771   memif_pending_conn_t *pending_conn = 0;
772
773   memif_disconnect (vm, mif);
774
775   if (mif->listener_index != (uword) ~ 0)
776     {
777       listener = pool_elt_at_index (mm->listeners, mif->listener_index);
778       if (--listener->usage_counter == 0)
779         {
780           /* not used anymore -> remove the socket and pending connections */
781
782           /* *INDENT-OFF* */
783           pool_foreach (pending_conn, mm->pending_conns,
784             ({
785                if (pending_conn->listener_index == mif->listener_index)
786                  {
787                    memif_remove_pending_conn (pending_conn);
788                  }
789              }));
790           /* *INDENT-ON* */
791
792           unix_file_del (&unix_main,
793                          unix_main.file_pool + listener->socket.index);
794           pool_put (mm->listeners, listener);
795           unlink ((char *) mif->socket_filename);
796         }
797     }
798
799   clib_spinlock_free (&mif->lockp);
800
801   mhash_unset (&mm->if_index_by_key, &mif->key, &mif->if_index);
802   vec_free (mif->socket_filename);
803   vec_free (mif->ring_data);
804
805   memset (mif, 0, sizeof (*mif));
806   pool_put (mm->interfaces, mif);
807 }
808
809 int
810 memif_worker_thread_enable ()
811 {
812   /* if worker threads are enabled, switch to polling mode */
813   /* *INDENT-OFF* */
814   foreach_vlib_main ((
815                        {
816                        vlib_node_set_state (this_vlib_main,
817                                             memif_input_node.index,
818                                             VLIB_NODE_STATE_POLLING);
819                        }));
820   /* *INDENT-ON* */
821   return 0;
822 }
823
824 int
825 memif_worker_thread_disable ()
826 {
827   /* *INDENT-OFF* */
828   foreach_vlib_main ((
829                        {
830                        vlib_node_set_state (this_vlib_main,
831                                             memif_input_node.index,
832                                             VLIB_NODE_STATE_INTERRUPT);
833                        }));
834   /* *INDENT-ON* */
835   return 0;
836 }
837
838 int
839 memif_create_if (vlib_main_t * vm, memif_create_if_args_t * args)
840 {
841   memif_main_t *mm = &memif_main;
842   vlib_thread_main_t *tm = vlib_get_thread_main ();
843   vnet_main_t *vnm = vnet_get_main ();
844   memif_if_t *mif = 0;
845   vnet_sw_interface_t *sw;
846   clib_error_t *error = 0;
847   int ret = 0;
848   uword *p;
849
850   p = mhash_get (&mm->if_index_by_key, &args->key);
851   if (p)
852     return VNET_API_ERROR_SUBIF_ALREADY_EXISTS;
853
854   pool_get (mm->interfaces, mif);
855   memset (mif, 0, sizeof (*mif));
856   mif->key = args->key;
857   mif->if_index = mif - mm->interfaces;
858   mif->sw_if_index = mif->hw_if_index = mif->per_interface_next_index = ~0;
859   mif->listener_index = ~0;
860   mif->connection.index = mif->interrupt_line.index = ~0;
861   mif->connection.fd = mif->interrupt_line.fd = -1;
862
863   if (tm->n_vlib_mains > 1)
864     clib_spinlock_init (&mif->lockp);
865
866   if (!args->hw_addr_set)
867     {
868       f64 now = vlib_time_now (vm);
869       u32 rnd;
870       rnd = (u32) (now * 1e6);
871       rnd = random_u32 (&rnd);
872
873       memcpy (args->hw_addr + 2, &rnd, sizeof (rnd));
874       args->hw_addr[0] = 2;
875       args->hw_addr[1] = 0xfe;
876     }
877
878   error = ethernet_register_interface (vnm, memif_device_class.index,
879                                        mif->if_index, args->hw_addr,
880                                        &mif->hw_if_index,
881                                        memif_eth_flag_change);
882
883   if (error)
884     {
885       clib_error_report (error);
886       ret = VNET_API_ERROR_SYSCALL_ERROR_1;
887       goto error;
888     }
889
890   sw = vnet_get_hw_sw_interface (vnm, mif->hw_if_index);
891   mif->sw_if_index = sw->sw_if_index;
892
893   mif->log2_ring_size = args->log2_ring_size;
894   mif->buffer_size = args->buffer_size;
895
896   mif->num_s2m_rings = args->rx_queues;
897   mif->num_m2s_rings = args->tx_queues;
898
899   mhash_set_mem (&mm->if_index_by_key, &args->key, &mif->if_index, 0);
900
901   if (args->socket_filename != 0)
902     mif->socket_filename = args->socket_filename;
903   else
904     mif->socket_filename = vec_dup (mm->default_socket_filename);
905
906   args->sw_if_index = mif->sw_if_index;
907
908   if (args->is_master)
909     {
910       struct sockaddr_un un = { 0 };
911       struct stat file_stat;
912       int on = 1;
913       memif_listener_t *listener = 0;
914
915       if (stat ((char *) mif->socket_filename, &file_stat) == 0)
916         {
917           if (!S_ISSOCK (file_stat.st_mode))
918             {
919               errno = ENOTSOCK;
920               ret = VNET_API_ERROR_SYSCALL_ERROR_2;
921               goto error;
922             }
923           /* *INDENT-OFF* */
924           pool_foreach (listener, mm->listeners,
925             ({
926                if (listener->sock_dev == file_stat.st_dev &&
927                    listener->sock_ino == file_stat.st_ino)
928                  {
929                    /* attach memif to the existing listener */
930                    mif->listener_index = listener->index;
931                    ++listener->usage_counter;
932                    goto signal;
933                  }
934              }));
935           /* *INDENT-ON* */
936           unlink ((char *) mif->socket_filename);
937         }
938
939       pool_get (mm->listeners, listener);
940       memset (listener, 0, sizeof (*listener));
941       listener->socket.fd = -1;
942       listener->socket.index = ~0;
943       listener->index = listener - mm->listeners;
944       listener->usage_counter = 1;
945
946       if ((listener->socket.fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
947         {
948           ret = VNET_API_ERROR_SYSCALL_ERROR_3;
949           goto error;
950         }
951
952       un.sun_family = AF_UNIX;
953       strncpy ((char *) un.sun_path, (char *) mif->socket_filename,
954                sizeof (un.sun_path) - 1);
955
956       if (setsockopt (listener->socket.fd, SOL_SOCKET, SO_PASSCRED,
957                       &on, sizeof (on)) < 0)
958         {
959           ret = VNET_API_ERROR_SYSCALL_ERROR_4;
960           goto error;
961         }
962       if (bind (listener->socket.fd, (struct sockaddr *) &un,
963                 sizeof (un)) == -1)
964         {
965           ret = VNET_API_ERROR_SYSCALL_ERROR_5;
966           goto error;
967         }
968       if (listen (listener->socket.fd, 1) == -1)
969         {
970           ret = VNET_API_ERROR_SYSCALL_ERROR_6;
971           goto error;
972         }
973
974       if (stat ((char *) mif->socket_filename, &file_stat) == -1)
975         {
976           ret = VNET_API_ERROR_SYSCALL_ERROR_7;
977           goto error;
978         }
979
980       listener->sock_dev = file_stat.st_dev;
981       listener->sock_ino = file_stat.st_ino;
982
983       unix_file_t template = { 0 };
984       template.read_function = memif_conn_fd_accept_ready;
985       template.file_descriptor = listener->socket.fd;
986       template.private_data = listener->index;
987       listener->socket.index = unix_file_add (&unix_main, &template);
988
989       mif->listener_index = listener->index;
990     }
991   else
992     {
993       mif->flags |= MEMIF_IF_FLAG_IS_SLAVE;
994     }
995
996 #if 0
997   /* use configured or generate random MAC address */
998   if (!args->hw_addr_set &&
999       tm->n_vlib_mains > 1 && pool_elts (mm->interfaces) == 1)
1000     memif_worker_thread_enable ();
1001 #endif
1002
1003 signal:
1004   if (pool_elts (mm->interfaces) == 1)
1005     {
1006       vlib_process_signal_event (vm, memif_process_node.index,
1007                                  MEMIF_PROCESS_EVENT_START, 0);
1008     }
1009   return 0;
1010
1011 error:
1012   if (mif->hw_if_index != ~0)
1013     {
1014       ethernet_delete_interface (vnm, mif->hw_if_index);
1015       mif->hw_if_index = ~0;
1016     }
1017   memif_close_if (mm, mif);
1018   return ret;
1019 }
1020
1021 int
1022 memif_delete_if (vlib_main_t * vm, u64 key)
1023 {
1024   vnet_main_t *vnm = vnet_get_main ();
1025   memif_main_t *mm = &memif_main;
1026   memif_if_t *mif;
1027   uword *p;
1028   u32 hw_if_index;
1029
1030   p = mhash_get (&mm->if_index_by_key, &key);
1031   if (p == NULL)
1032     {
1033       DEBUG_LOG ("Memory interface with key 0x%" PRIx64 " does not exist",
1034                  key);
1035       return VNET_API_ERROR_SYSCALL_ERROR_1;
1036     }
1037   mif = pool_elt_at_index (mm->interfaces, p[0]);
1038   mif->flags |= MEMIF_IF_FLAG_DELETING;
1039
1040   /* bring down the interface */
1041   vnet_sw_interface_set_flags (vnm, mif->sw_if_index, 0);
1042
1043   hw_if_index = mif->hw_if_index;
1044   memif_close_if (mm, mif);
1045
1046   /* remove the interface */
1047   ethernet_delete_interface (vnm, hw_if_index);
1048   if (pool_elts (mm->interfaces) == 0)
1049     {
1050       vlib_process_signal_event (vm, memif_process_node.index,
1051                                  MEMIF_PROCESS_EVENT_STOP, 0);
1052     }
1053
1054 #if 0
1055   if (tm->n_vlib_mains > 1 && pool_elts (mm->interfaces) == 0)
1056     memif_worker_thread_disable ();
1057 #endif
1058
1059   return 0;
1060 }
1061
1062 static clib_error_t *
1063 memif_init (vlib_main_t * vm)
1064 {
1065   memif_main_t *mm = &memif_main;
1066   vlib_thread_main_t *tm = vlib_get_thread_main ();
1067   vlib_thread_registration_t *tr;
1068   uword *p;
1069
1070   memset (mm, 0, sizeof (memif_main_t));
1071
1072   mm->input_cpu_first_index = 0;
1073   mm->input_cpu_count = 1;
1074
1075   /* initialize binary API */
1076   memif_plugin_api_hookup (vm);
1077
1078   /* find out which cpus will be used for input */
1079   p = hash_get_mem (tm->thread_registrations_by_name, "workers");
1080   tr = p ? (vlib_thread_registration_t *) p[0] : 0;
1081
1082   if (tr && tr->count > 0)
1083     {
1084       mm->input_cpu_first_index = tr->first_index;
1085       mm->input_cpu_count = tr->count;
1086     }
1087
1088   mhash_init (&mm->if_index_by_key, sizeof (uword), sizeof (u64));
1089
1090   vec_validate_aligned (mm->rx_buffers, tm->n_vlib_mains - 1,
1091                         CLIB_CACHE_LINE_BYTES);
1092
1093   /* set default socket filename */
1094   vec_validate (mm->default_socket_filename,
1095                 strlen (MEMIF_DEFAULT_SOCKET_FILENAME));
1096   strncpy ((char *) mm->default_socket_filename,
1097            MEMIF_DEFAULT_SOCKET_FILENAME,
1098            vec_len (mm->default_socket_filename) - 1);
1099
1100   return 0;
1101 }
1102
1103 VLIB_INIT_FUNCTION (memif_init);
1104
1105 /* *INDENT-OFF* */
1106 VLIB_PLUGIN_REGISTER () = {
1107     .version = VPP_BUILD_VER,
1108     .description = "Packet Memory Interface (experimetal)",
1109 };
1110 /* *INDENT-ON* */
1111
1112 /*
1113  * fd.io coding-style-patch-verification: ON
1114  *
1115  * Local Variables:
1116  * eval: (c-set-style "gnu")
1117  * End:
1118  */