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