vcl: nest vcl_mq_epfd to support epoll_wait without high CPU usage
[vpp.git] / src / vcl / ldp.c
index 70a4299..e3e6dfa 100644 (file)
@@ -80,6 +80,7 @@ typedef struct ldp_worker_ctx_
    * Epoll state
    */
   u8 epoll_wait_vcl;
+  u8 mq_epfd_added;
   int vcl_mq_epfd;
 
 } ldp_worker_ctx_t;
@@ -109,7 +110,11 @@ typedef struct
 
 #define LDBG(_lvl, _fmt, _args...)                                     \
   if (ldp->debug > _lvl)                                               \
-    clib_warning ("ldp<%d>: " _fmt, getpid(), ##_args)
+    {                                                                  \
+      int errno_saved = errno;                                         \
+      clib_warning ("ldp<%d>: " _fmt, getpid(), ##_args);              \
+      errno = errno_saved;                                             \
+    }
 
 static ldp_main_t ldp_main = {
   .vlsh_bit_val = (1 << LDP_SID_BIT_MIN),
@@ -132,11 +137,8 @@ ldp_worker_get_current (void)
 static inline void
 ldp_set_app_name (char *app_name)
 {
-  int rv = snprintf (ldp->app_name, LDP_APP_NAME_MAX,
-                    "ldp-%d-%s", getpid (), app_name);
-
-  if (rv >= LDP_APP_NAME_MAX)
-    app_name[LDP_APP_NAME_MAX - 1] = 0;
+  snprintf (ldp->app_name, LDP_APP_NAME_MAX,
+           "ldp-%d-%s", getpid (), app_name);
 }
 
 static inline char *
@@ -379,24 +381,19 @@ readv (int fd, const struct iovec * iov, int iovcnt)
   vlsh = ldp_fd_to_vlsh (fd);
   if (vlsh != VLS_INVALID_HANDLE)
     {
-      do
+      for (i = 0; i < iovcnt; ++i)
        {
-         for (i = 0; i < iovcnt; ++i)
+         rv = vls_read (vlsh, iov[i].iov_base, iov[i].iov_len);
+         if (rv <= 0)
+           break;
+         else
            {
-             rv = vls_read (vlsh, iov[i].iov_base, iov[i].iov_len);
-             if (rv < 0)
+             total += rv;
+             if (rv < iov[i].iov_len)
                break;
-             else
-               {
-                 total += rv;
-                 if (rv < iov[i].iov_len)
-                   break;
-               }
            }
        }
-      while ((rv >= 0) && (total == 0));
-
-      if (rv < 0)
+      if (rv < 0 && total == 0)
        {
          errno = -rv;
          size = -1;
@@ -452,24 +449,20 @@ writev (int fd, const struct iovec * iov, int iovcnt)
   vlsh = ldp_fd_to_vlsh (fd);
   if (vlsh != VLS_INVALID_HANDLE)
     {
-      do
+      for (i = 0; i < iovcnt; ++i)
        {
-         for (i = 0; i < iovcnt; ++i)
+         rv = vls_write_msg (vlsh, iov[i].iov_base, iov[i].iov_len);
+         if (rv < 0)
+           break;
+         else
            {
-             rv = vls_write_msg (vlsh, iov[i].iov_base, iov[i].iov_len);
-             if (rv < 0)
+             total += rv;
+             if (rv < iov[i].iov_len)
                break;
-             else
-               {
-                 total += rv;
-                 if (rv < iov[i].iov_len)
-                   break;
-               }
            }
        }
-      while ((rv >= 0) && (total == 0));
 
-      if (rv < 0)
+      if (rv < 0 && total == 0)
        {
          errno = -rv;
          size = -1;
@@ -534,7 +527,11 @@ fcntl (int fd, int cmd, ...)
     }
   else
     {
+#ifdef HAVE_FCNTL64
+      rv = libc_vfcntl64 (fd, cmd, ap);
+#else
       rv = libc_vfcntl (fd, cmd, ap);
+#endif
     }
 
   va_end (ap);
@@ -542,6 +539,18 @@ fcntl (int fd, int cmd, ...)
   return rv;
 }
 
+int
+fcntl64 (int fd, int cmd, ...)
+{
+  va_list ap;
+  int rv;
+
+  va_start (ap, cmd);
+  rv = fcntl (fd, cmd, ap);
+  va_end (ap);
+  return rv;
+}
+
 int
 ioctl (int fd, unsigned long int cmd, ...)
 {
@@ -619,12 +628,13 @@ ldp_select_init_maps (fd_set * __restrict original,
     if (vlsh == VLS_INVALID_HANDLE)
       clib_bitmap_set_no_check (*libcb, fd, 1);
     else
-      clib_bitmap_set_no_check (*vclb, vlsh_to_session_index (vlsh), 1);
+      *vclb = clib_bitmap_set (*vclb, vlsh_to_session_index (vlsh), 1);
   }));
   /* *INDENT-ON* */
 
   si_bits_set = clib_bitmap_last_set (*vclb) + 1;
   *si_bits = (si_bits_set > *si_bits) ? si_bits_set : *si_bits;
+  clib_bitmap_validate (*resultb, *si_bits);
 
   libc_bits_set = clib_bitmap_last_set (*libcb) + 1;
   *libc_bits = (libc_bits_set > *libc_bits) ? libc_bits_set : *libc_bits;
@@ -643,6 +653,7 @@ ldp_select_vcl_map_to_libc (clib_bitmap_t * vclb, fd_set * __restrict libcb)
   /* *INDENT-OFF* */
   clib_bitmap_foreach (si, vclb, ({
     vlsh = vls_session_index_to_vlsh (si);
+    ASSERT (vlsh != VLS_INVALID_HANDLE);
     fd = ldp_vlsh_to_fd (vlsh);
     if (PREDICT_FALSE (fd < 0))
       {
@@ -755,15 +766,15 @@ ldp_pselect (int nfds, fd_set * __restrict readfds,
        {
          if (readfds)
            clib_memcpy_fast (ldpw->rd_bitmap, ldpw->si_rd_bitmap,
-                             vec_len (ldpw->rd_bitmap) *
+                             vec_len (ldpw->si_rd_bitmap) *
                              sizeof (clib_bitmap_t));
          if (writefds)
            clib_memcpy_fast (ldpw->wr_bitmap, ldpw->si_wr_bitmap,
-                             vec_len (ldpw->wr_bitmap) *
+                             vec_len (ldpw->si_wr_bitmap) *
                              sizeof (clib_bitmap_t));
          if (exceptfds)
            clib_memcpy_fast (ldpw->ex_bitmap, ldpw->si_ex_bitmap,
-                             vec_len (ldpw->ex_bitmap) *
+                             vec_len (ldpw->si_ex_bitmap) *
                              sizeof (clib_bitmap_t));
 
          rv = vls_select (si_bits, readfds ? ldpw->rd_bitmap : NULL,
@@ -773,6 +784,7 @@ ldp_pselect (int nfds, fd_set * __restrict readfds,
            {
              errno = -rv;
              rv = -1;
+             goto done;
            }
          else if (rv > 0)
            {
@@ -1500,7 +1512,10 @@ recv (int fd, void *buf, size_t n, int flags)
     {
       size = vls_recvfrom (vlsh, buf, n, flags, NULL);
       if (size < 0)
-       errno = -size;
+       {
+         errno = -size;
+         size = -1;
+       }
     }
   else
     {
@@ -1809,8 +1824,8 @@ getsockopt (int fd, int level, int optname,
                rv = -EFAULT;
              break;
            case TCP_CONGESTION:
-             strcpy (optval, "cubic");
              *optlen = strlen ("cubic");
+             strncpy (optval, "cubic", *optlen + 1);
              rv = 0;
              break;
            default:
@@ -2330,17 +2345,132 @@ done:
   return rv;
 }
 
+static inline int
+ldp_epoll_pwait_eventfd (int epfd, struct epoll_event *events,
+                        int maxevents, int timeout, const sigset_t * sigmask)
+{
+  ldp_worker_ctx_t *ldpw = ldp_worker_get_current ();
+  int libc_epfd, rv = 0, num_ev;
+  vls_handle_t ep_vlsh;
+
+  if ((errno = -ldp_init ()))
+    return -1;
+
+  if (PREDICT_FALSE (!events || (timeout < -1)))
+    {
+      errno = EFAULT;
+      return -1;
+    }
+
+  if (epfd == ldpw->vcl_mq_epfd)
+    return libc_epoll_pwait (epfd, events, maxevents, timeout, sigmask);
+
+  ep_vlsh = ldp_fd_to_vlsh (epfd);
+  if (PREDICT_FALSE (ep_vlsh == VLS_INVALID_HANDLE))
+    {
+      LDBG (0, "epfd %d: bad ep_vlsh %d!", epfd, ep_vlsh);
+      errno = EBADFD;
+      return -1;
+    }
+
+  libc_epfd = vls_attr (ep_vlsh, VPPCOM_ATTR_GET_LIBC_EPFD, 0, 0);
+  if (PREDICT_FALSE (!libc_epfd))
+    {
+      u32 size = sizeof (epfd);
+
+      LDBG (1, "epfd %d, vep_vlsh %d calling libc_epoll_create1: "
+           "EPOLL_CLOEXEC", epfd, ep_vlsh);
+      libc_epfd = libc_epoll_create1 (EPOLL_CLOEXEC);
+      if (libc_epfd < 0)
+       {
+         rv = libc_epfd;
+         goto done;
+       }
+
+      rv = vls_attr (ep_vlsh, VPPCOM_ATTR_SET_LIBC_EPFD, &libc_epfd, &size);
+      if (rv < 0)
+       {
+         errno = -rv;
+         rv = -1;
+         goto done;
+       }
+    }
+  if (PREDICT_FALSE (libc_epfd <= 0))
+    {
+      errno = -libc_epfd;
+      rv = -1;
+      goto done;
+    }
+
+  if (PREDICT_FALSE (!ldpw->mq_epfd_added))
+    {
+      struct epoll_event e = { 0 };
+      e.events = EPOLLIN;
+      e.data.fd = ldpw->vcl_mq_epfd;
+      if (libc_epoll_ctl (libc_epfd, EPOLL_CTL_ADD, ldpw->vcl_mq_epfd, &e) <
+         0)
+       {
+         LDBG (0, "epfd %d, add libc mq epoll fd %d to libc epoll fd %d",
+               epfd, ldpw->vcl_mq_epfd, libc_epfd);
+         rv = -1;
+         goto done;
+       }
+      ldpw->mq_epfd_added = 1;
+    }
+
+  rv = vls_epoll_wait (ep_vlsh, events, maxevents, 0);
+  if (rv > 0)
+    goto done;
+  else if (rv < 0)
+    {
+      errno = -rv;
+      rv = -1;
+      goto done;
+    }
+
+  rv = libc_epoll_pwait (libc_epfd, events, maxevents, timeout, sigmask);
+  if (rv <= 0)
+    goto done;
+  for (int i = 0; i < rv; i++)
+    {
+      if (events[i].data.fd == ldpw->vcl_mq_epfd)
+       {
+         /* We should remove mq epoll fd from events. */
+         rv--;
+         if (i != rv)
+           {
+             events[i].events = events[rv].events;
+             events[i].data.u64 = events[rv].data.u64;
+           }
+         num_ev = vls_epoll_wait (ep_vlsh, &events[rv], maxevents - rv, 0);
+         if (PREDICT_TRUE (num_ev > 0))
+           rv += num_ev;
+         break;
+       }
+    }
+
+done:
+  return rv;
+}
+
 int
 epoll_pwait (int epfd, struct epoll_event *events,
             int maxevents, int timeout, const sigset_t * sigmask)
 {
-  return ldp_epoll_pwait (epfd, events, maxevents, timeout, sigmask);
+  if (vls_use_eventfd ())
+    return ldp_epoll_pwait_eventfd (epfd, events, maxevents, timeout,
+                                   sigmask);
+  else
+    return ldp_epoll_pwait (epfd, events, maxevents, timeout, sigmask);
 }
 
 int
 epoll_wait (int epfd, struct epoll_event *events, int maxevents, int timeout)
 {
-  return ldp_epoll_pwait (epfd, events, maxevents, timeout, NULL);
+  if (vls_use_eventfd ())
+    return ldp_epoll_pwait_eventfd (epfd, events, maxevents, timeout, NULL);
+  else
+    return ldp_epoll_pwait (epfd, events, maxevents, timeout, NULL);
 }
 
 int