vcl: basic support for epoll level-triggered evts
[vpp.git] / src / vcl / vppcom.c
index 330d590..f96ceea 100644 (file)
@@ -807,7 +807,7 @@ vcl_session_disconnected_handler (vcl_worker_t * wrk,
 }
 
 int
-vppcom_session_shutdown (uint32_t session_handle)
+vppcom_session_shutdown (uint32_t session_handle, int how)
 {
   vcl_worker_t *wrk = vcl_worker_get_current ();
   vcl_session_t *session;
@@ -830,13 +830,20 @@ vppcom_session_shutdown (uint32_t session_handle)
       return VPPCOM_EBADFD;
     }
 
+  if (how == SHUT_RD || how == SHUT_RDWR)
+    {
+      session->flags |= VCL_SESSION_F_RD_SHUTDOWN;
+      if (how == SHUT_RD)
+       return VPPCOM_OK;
+    }
+  session->flags |= VCL_SESSION_F_WR_SHUTDOWN;
+
   if (PREDICT_TRUE (state == VCL_STATE_READY))
     {
       VDBG (1, "session %u [0x%llx]: sending shutdown...",
            session->session_index, vpp_handle);
 
       vcl_send_session_shutdown (wrk, session);
-      session->flags |= VCL_SESSION_F_SHUTDOWN;
     }
 
   return VPPCOM_OK;
@@ -1438,6 +1445,18 @@ vppcom_session_create (u8 proto, u8 is_nonblocking)
   return vcl_session_handle (session);
 }
 
+static void
+vcl_epoll_wait_clean_lt (vcl_worker_t *wrk, u32 sid)
+{
+  int i;
+
+  for (i = vec_len (wrk->ep_level_evts) - 1; i >= 0; i--)
+    {
+      if (wrk->ep_level_evts[i] == sid)
+       vec_del1 (wrk->ep_level_evts, i);
+    }
+}
+
 int
 vcl_session_cleanup (vcl_worker_t * wrk, vcl_session_t * s,
                     vcl_session_handle_t sh, u8 do_disconnect)
@@ -1468,6 +1487,8 @@ vcl_session_cleanup (vcl_worker_t * wrk, vcl_session_t * s,
        VDBG (0, "session %u [0x%llx]: EPOLL_CTL_DEL vep_idx %u "
              "failed! rv %d (%s)", s->session_index, s->vpp_handle,
              s->vep.vep_sh, rv, vppcom_retval_str (rv));
+      if (PREDICT_FALSE (vec_len (wrk->ep_level_evts)))
+       vcl_epoll_wait_clean_lt (wrk, s->session_index);
     }
 
   if (!do_disconnect)
@@ -1948,6 +1969,18 @@ vppcom_session_read_internal (uint32_t session_handle, void *buf, int n,
       return vcl_session_closed_error (s);
     }
 
+  if (PREDICT_FALSE (s->flags & VCL_SESSION_F_RD_SHUTDOWN))
+    {
+      /* Vpp would ack the incoming data and enqueue it for reading.
+       * So even if SHUT_RD is set, we can still read() the data if
+       * the session is ready.
+       */
+      if (!vcl_session_read_ready (s))
+       {
+         return 0;
+       }
+    }
+
   is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
   is_ct = vcl_session_is_ct (s);
   mq = wrk->app_event_queue;
@@ -2166,8 +2199,7 @@ vppcom_session_write_inline (vcl_worker_t * wrk, vcl_session_t * s, void *buf,
       return VPPCOM_EBADFD;
     }
 
-  if (PREDICT_FALSE (!vcl_session_is_open (s) ||
-                    (s->flags & VCL_SESSION_F_SHUTDOWN)))
+  if (PREDICT_FALSE (!vcl_session_is_open (s)))
     {
       VDBG (1, "session %u [0x%llx]: is not open! state 0x%x (%s)",
            s->session_index, s->vpp_handle, s->session_state,
@@ -2175,6 +2207,14 @@ vppcom_session_write_inline (vcl_worker_t * wrk, vcl_session_t * s, void *buf,
       return vcl_session_closed_error (s);;
     }
 
+  if (PREDICT_FALSE (s->flags & VCL_SESSION_F_WR_SHUTDOWN))
+    {
+      VDBG (1, "session %u [0x%llx]: is shutdown! state 0x%x (%s)",
+           s->session_index, s->vpp_handle, s->session_state,
+           vppcom_session_state_str (s->session_state));
+      return VPPCOM_EPIPE;
+    }
+
   is_ct = vcl_session_is_ct (s);
   tx_fifo = is_ct ? s->ct_tx_fifo : s->tx_fifo;
   is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
@@ -3037,6 +3077,10 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
          s = vcl_session_get (wrk, sid);
          s->vep.ev.events = 0;
        }
+      if (!(EPOLLET & session_events))
+       {
+         vec_add1 (wrk->ep_level_evts, sid);
+       }
       *num_ev += 1;
     }
 }
@@ -3151,13 +3195,73 @@ vppcom_epoll_wait_eventfd (vcl_worker_t *wrk, struct epoll_event *events,
   return 0;
 }
 
+static void
+vcl_epoll_swap_lt_lists (vcl_worker_t *wrk)
+{
+  u32 *le;
+
+  le = wrk->ep_level_evts;
+  wrk->ep_level_evts = wrk->ep_level_evts_fl;
+  wrk->ep_level_evts_fl = le;
+}
+
+static void
+vcl_epoll_wait_handle_lt (vcl_worker_t *wrk, struct epoll_event *events,
+                         int maxevents, u32 *n_evts)
+{
+  u32 *sid, add_event = 0, *le = wrk->ep_level_evts_fl;
+  vcl_session_t *s;
+  u64 evt_data;
+
+  if (*n_evts >= maxevents)
+    {
+      vec_add (wrk->ep_level_evts, le, vec_len (le));
+      vec_reset_length (wrk->ep_level_evts_fl);
+      return;
+    }
+
+  vec_foreach (sid, le)
+    {
+      s = vcl_session_get (wrk, sid[0]);
+      if (!s)
+       continue;
+      if ((s->vep.ev.events & EPOLLIN) && vcl_session_read_ready (s))
+       {
+         add_event = 1;
+         events[*n_evts].events |= EPOLLIN;
+         evt_data = s->vep.ev.data.u64;
+       }
+      if ((s->vep.ev.events & EPOLLOUT) && vcl_session_write_ready (s))
+       {
+         add_event = 1;
+         events[*n_evts].events |= EPOLLOUT;
+         evt_data = s->vep.ev.data.u64;
+       }
+      if (add_event)
+       {
+         events[*n_evts].data.u64 = evt_data;
+         *n_evts += 1;
+         add_event = 0;
+         vec_add1 (wrk->ep_level_evts, sid[0]);
+         if (*n_evts == maxevents)
+           {
+             u32 pos = (sid - le) + 1;
+             vec_add (wrk->ep_level_evts, &le[pos], vec_len (le) - pos);
+             break;
+           }
+       }
+    }
+
+  vec_reset_length (wrk->ep_level_evts_fl);
+}
+
 int
 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
                   int maxevents, double wait_for_time)
 {
   vcl_worker_t *wrk = vcl_worker_get_current ();
   vcl_session_t *vep_session;
-  u32 n_evts = 0;
+  u32 n_evts = 0, do_lt = 0;
   int i;
 
   if (PREDICT_FALSE (maxevents <= 0))
@@ -3196,12 +3300,23 @@ vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
   if ((int) wait_for_time == -2)
     return n_evts;
 
+  if (PREDICT_FALSE (vec_len (wrk->ep_level_evts)))
+    {
+      vcl_epoll_swap_lt_lists (wrk);
+      do_lt = 1;
+    }
+
   if (vcm->cfg.use_mq_eventfd)
-    return vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
-                                     wait_for_time);
+    n_evts = vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
+                                       wait_for_time);
+  else
+    n_evts = vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
+                                       wait_for_time);
 
-  return vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
-                                   wait_for_time);
+  if (PREDICT_FALSE (do_lt))
+    vcl_epoll_wait_handle_lt (wrk, events, maxevents, &n_evts);
+
+  return n_evts;
 }
 
 int
@@ -3209,7 +3324,7 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
                     void *buffer, uint32_t * buflen)
 {
   vcl_worker_t *wrk = vcl_worker_get_current ();
-  u32 *flags = buffer, tmp_flags = 0;
+  u32 *flags = buffer;
   vppcom_endpt_t *ep = buffer;
   transport_endpt_attr_t tea;
   vcl_session_t *session;
@@ -3747,27 +3862,6 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
            *buflen);
       break;
 
-    case VPPCOM_ATTR_SET_SHUT:
-      if (*flags == SHUT_RD || *flags == SHUT_RDWR)
-       vcl_session_set_attr (session, VCL_SESS_ATTR_SHUT_RD);
-      if (*flags == SHUT_WR || *flags == SHUT_RDWR)
-       vcl_session_set_attr (session, VCL_SESS_ATTR_SHUT_WR);
-      break;
-
-    case VPPCOM_ATTR_GET_SHUT:
-      if (vcl_session_has_attr (session, VCL_SESS_ATTR_SHUT_RD))
-       tmp_flags = 1;
-      if (vcl_session_has_attr (session, VCL_SESS_ATTR_SHUT_WR))
-       tmp_flags |= 2;
-      if (tmp_flags == 1)
-       *(int *) buffer = SHUT_RD;
-      else if (tmp_flags == 2)
-       *(int *) buffer = SHUT_WR;
-      else if (tmp_flags == 3)
-       *(int *) buffer = SHUT_RDWR;
-      *buflen = sizeof (int);
-      break;
-
     case VPPCOM_ATTR_SET_CONNECTED:
       session->flags |= VCL_SESSION_F_CONNECTED;
       break;