vcl: add api to retrieve num bytes for tx
[vpp.git] / src / vcl / vppcom.c
index f2166f8..a0bbae1 100644 (file)
@@ -351,6 +351,11 @@ vcl_session_accepted_handler (vcl_worker_t * wrk, session_accepted_msg_t * mp,
 
   session->vpp_handle = mp->handle;
   session->session_state = VCL_STATE_READY;
+  if (mp->rmt.is_ip4)
+    {
+      session->original_dst_ip4 = mp->original_dst_ip4;
+      session->original_dst_port = mp->original_dst_port;
+    }
   session->transport.rmt_port = mp->rmt.port;
   session->transport.is_ip4 = mp->rmt.is_ip4;
   clib_memcpy_fast (&session->transport.rmt_ip, &mp->rmt.ip,
@@ -508,9 +513,9 @@ vcl_session_reset_handler (vcl_worker_t * wrk,
     }
 
   /* Caught a reset before actually accepting the session */
-  if (session->session_state == VCL_STATE_LISTEN)
+  if (session->session_state == VCL_STATE_LISTEN ||
+      session->session_state == VCL_STATE_LISTEN_NO_MQ)
     {
-
       if (!vcl_flag_accepted_session (session, reset_msg->handle,
                                      VCL_ACCEPTED_F_RESET))
        VDBG (0, "session was not accepted!");
@@ -540,6 +545,7 @@ vcl_session_bound_handler (vcl_worker_t * wrk, session_bound_msg_t * mp)
        {
          session->session_state = VCL_STATE_DETACHED;
          session->vpp_handle = mp->handle;
+         session->vpp_error = mp->retval;
          return sid;
        }
       else
@@ -701,7 +707,8 @@ vcl_session_disconnected_handler (vcl_worker_t * wrk,
     return 0;
 
   /* Caught a disconnect before actually accepting the session */
-  if (session->session_state == VCL_STATE_LISTEN)
+  if (session->session_state == VCL_STATE_LISTEN ||
+      session->session_state == VCL_STATE_LISTEN_NO_MQ)
     {
       if (!vcl_flag_accepted_session (session, msg->handle,
                                      VCL_ACCEPTED_F_CLOSED))
@@ -969,7 +976,7 @@ vcl_session_app_del_segment_handler (vcl_worker_t * wrk, void *data)
 {
   session_app_del_segment_msg_t *msg = (session_app_del_segment_msg_t *) data;
   vcl_segment_detach (msg->segment_handle);
-  VDBG (1, "Unmapped segment: %d", msg->segment_handle);
+  VDBG (1, "Unmapped segment: %lx", msg->segment_handle);
 }
 
 static void
@@ -1048,7 +1055,15 @@ vcl_handle_mq_event (vcl_worker_t * wrk, session_event_t * e)
        break;
       if (s->session_state == VCL_STATE_CLOSED)
        break;
-      if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
+      /* We do not postpone for blocking sessions or listen sessions because:
+       * 1. Blocking sessions are not part of epoll instead they're used in a
+       *    synchronous manner, such as read/write and etc.
+       * 2. Listen sessions that have not yet been accepted can't change to
+       *    VPP_CLOSING state instead can been marked as ACCEPTED_F_CLOSED.
+       */
+      if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK) &&
+         !(s->session_state == VCL_STATE_LISTEN ||
+           s->session_state == VCL_STATE_LISTEN_NO_MQ))
        {
          s->session_state = VCL_STATE_VPP_CLOSING;
          s->flags |= VCL_SESSION_F_PENDING_DISCONNECT;
@@ -1069,7 +1084,15 @@ vcl_handle_mq_event (vcl_worker_t * wrk, session_event_t * e)
        break;
       if (s->session_state == VCL_STATE_CLOSED)
        break;
-      if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
+      /* We do not postpone for blocking sessions or listen sessions because:
+       * 1. Blocking sessions are not part of epoll instead they're used in a
+       *    synchronous manner, such as read/write and etc.
+       * 2. Listen sessions that have not yet been accepted can't change to
+       *    DISCONNECT state instead can been marked as ACCEPTED_F_RESET.
+       */
+      if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK) &&
+         !(s->session_state == VCL_STATE_LISTEN ||
+           s->session_state == VCL_STATE_LISTEN_NO_MQ))
        {
          s->flags |= VCL_SESSION_F_PENDING_DISCONNECT;
          s->session_state = VCL_STATE_DISCONNECT;
@@ -1139,7 +1162,10 @@ vppcom_wait_for_session_state_change (u32 session_index,
        }
       if (session->session_state == VCL_STATE_DETACHED)
        {
-         return VPPCOM_ECONNREFUSED;
+         if (session->vpp_error == SESSION_E_ALREADY_LISTENING)
+           return VPPCOM_EADDRINUSE;
+         else
+           return VPPCOM_ECONNREFUSED;
        }
 
       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0))
@@ -1414,12 +1440,10 @@ vppcom_app_destroy (void)
 
   current_wrk = vcl_worker_get_current ();
 
-  /* *INDENT-OFF* */
   pool_foreach (wrk, vcm->workers)  {
     if (current_wrk != wrk)
       vcl_worker_cleanup (wrk, 0 /* notify vpp */ );
   }
-  /* *INDENT-ON* */
 
   vcl_api_detach (current_wrk);
   vcl_worker_cleanup (current_wrk, 0 /* notify vpp */ );
@@ -1656,7 +1680,7 @@ vppcom_session_bind (uint32_t session_handle, vppcom_endpt_t * ep)
   vcl_evt (VCL_EVT_BIND, session);
 
   if (session->session_type == VPPCOM_PROTO_UDP)
-    vppcom_session_listen (session_handle, 10);
+    return vppcom_session_listen (session_handle, 10);
 
   return VPPCOM_OK;
 }
@@ -2030,13 +2054,13 @@ vppcom_session_read_internal (uint32_t session_handle, void *buf, int n,
 
   if (svm_fifo_is_empty_cons (rx_fifo))
     {
+      if (is_ct)
+       svm_fifo_unset_event (s->rx_fifo);
+      svm_fifo_unset_event (rx_fifo);
       if (is_nonblocking)
        {
          if (vcl_session_is_closing (s))
            return vcl_session_closing_error (s);
-         if (is_ct)
-           svm_fifo_unset_event (s->rx_fifo);
-         svm_fifo_unset_event (rx_fifo);
          return VPPCOM_EWOULDBLOCK;
        }
       while (svm_fifo_is_empty_cons (rx_fifo))
@@ -2142,11 +2166,13 @@ vppcom_session_read_segments (uint32_t session_handle,
 
   if (svm_fifo_is_empty_cons (rx_fifo))
     {
+      if (is_ct)
+       svm_fifo_unset_event (s->rx_fifo);
+      svm_fifo_unset_event (rx_fifo);
       if (is_nonblocking)
        {
-         if (is_ct)
-           svm_fifo_unset_event (s->rx_fifo);
-         svm_fifo_unset_event (rx_fifo);
+         if (vcl_session_is_closing (s))
+           return vcl_session_closing_error (s);
          return VPPCOM_EWOULDBLOCK;
        }
       while (svm_fifo_is_empty_cons (rx_fifo))
@@ -2425,10 +2451,24 @@ vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
       break;
     case SESSION_CTRL_EVT_DISCONNECTED:
-      disconnected_msg = (session_disconnected_msg_t *) e->data;
-      s = vcl_session_disconnected_handler (wrk, disconnected_msg);
-      if (!s)
-       break;
+      if (!e->postponed)
+       {
+         disconnected_msg = (session_disconnected_msg_t *) e->data;
+         s = vcl_session_disconnected_handler (wrk, disconnected_msg);
+         if (!s)
+           break;
+       }
+      else
+       {
+         s = vcl_session_get (wrk, e->session_index);
+         s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
+       }
+      if (vcl_session_is_closed (s))
+       {
+         if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
+           vcl_session_free (wrk, s);
+         break;
+       }
       sid = s->session_index;
       if (sid < n_bits && except_map)
        {
@@ -2437,7 +2477,24 @@ vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
        }
       break;
     case SESSION_CTRL_EVT_RESET:
-      sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
+      if (!e->postponed)
+       {
+         sid =
+           vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
+         s = vcl_session_get (wrk, sid);
+       }
+      else
+       {
+         sid = e->session_index;
+         s = vcl_session_get (wrk, sid);
+         s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
+       }
+      if (vcl_session_is_closed (s))
+       {
+         if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
+           vcl_session_free (wrk, s);
+         break;
+       }
       if (sid < n_bits && except_map)
        {
          clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
@@ -2771,7 +2828,7 @@ vppcom_epoll_create (void)
 
 static void
 vcl_epoll_ctl_add_unhandled_event (vcl_worker_t *wrk, vcl_session_t *s,
-                                  u8 is_epollet, session_evt_type_t evt)
+                                  u32 is_epollet, session_evt_type_t evt)
 {
   if (!is_epollet)
     {
@@ -2864,6 +2921,7 @@ vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
       s->vep.lt_next = VCL_INVALID_SESSION_INDEX;
       s->vep.ev = *event;
+      s->vep.ev.events |= EPOLLHUP | EPOLLERR;
       s->flags &= ~VCL_SESSION_F_IS_VEP;
       s->flags |= VCL_SESSION_F_IS_VEP_SESSION;
       vep_session->vep.next_sh = session_handle;
@@ -2955,6 +3013,7 @@ vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
        }
       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
       s->vep.ev = *event;
+      s->vep.ev.events |= EPOLLHUP | EPOLLERR;
 
       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
            vep_handle, session_handle, event->events, event->data.u64);
@@ -3033,6 +3092,14 @@ done:
   return rv;
 }
 
+always_inline u8
+vcl_ep_session_needs_evt (vcl_session_t *s, u32 evt)
+{
+  /* No event if not epolled / events reset on hup or level-trigger on */
+  return ((s->vep.ev.events & evt) &&
+         s->vep.lt_next == VCL_INVALID_SESSION_INDEX);
+}
+
 static inline void
 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
                                struct epoll_event *events, u32 * num_ev)
@@ -3052,11 +3119,10 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       if (vcl_session_is_closed (s))
        break;
       vcl_fifo_rx_evt_valid_or_break (s);
-      session_events = s->vep.ev.events;
-      if (!(EPOLLIN & s->vep.ev.events) ||
-         (s->flags & VCL_SESSION_F_HAS_RX_EVT) ||
-         (s->vep.lt_next != VCL_INVALID_SESSION_INDEX))
+      if (!vcl_ep_session_needs_evt (s, EPOLLIN) ||
+         (s->flags & VCL_SESSION_F_HAS_RX_EVT))
        break;
+      session_events = s->vep.ev.events;
       add_event = 1;
       events[*num_ev].events = EPOLLIN;
       session_evt_data = s->vep.ev.data.u64;
@@ -3069,10 +3135,9 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
        break;
       svm_fifo_reset_has_deq_ntf (vcl_session_is_ct (s) ? s->ct_tx_fifo :
                                                                s->tx_fifo);
-      session_events = s->vep.ev.events;
-      if (!(EPOLLOUT & session_events) ||
-         (s->vep.lt_next != VCL_INVALID_SESSION_INDEX))
+      if (!vcl_ep_session_needs_evt (s, EPOLLOUT))
        break;
+      session_events = s->vep.ev.events;
       add_event = 1;
       events[*num_ev].events = EPOLLOUT;
       session_evt_data = s->vep.ev.data.u64;
@@ -3082,13 +3147,10 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
        s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
       else
        s = vcl_session_get (wrk, e->session_index);
-      if (!s)
+      if (!s || !vcl_ep_session_needs_evt (s, EPOLLIN))
        break;
-      session_events = s->vep.ev.events;
       sid = s->session_index;
-      if (!(EPOLLIN & session_events) ||
-         (s->vep.lt_next != VCL_INVALID_SESSION_INDEX))
-       break;
+      session_events = s->vep.ev.events;
       add_event = 1;
       events[*num_ev].events = EPOLLIN;
       session_evt_data = s->vep.ev.data.u64;
@@ -3102,19 +3164,20 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       else
        sid = e->session_index;
       s = vcl_session_get (wrk, sid);
-      if (vcl_session_is_closed (s))
-       break;
-      session_events = s->vep.ev.events;
-      /* Generate EPOLLOUT because there's no connected event */
-      if (!(EPOLLOUT & session_events))
+      if (vcl_session_is_closed (s) || !vcl_ep_session_needs_evt (s, EPOLLOUT))
        break;
       /* We didn't have a fifo when the event was added */
       vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
       add_event = 1;
+      session_events = s->vep.ev.events;
+      /* Generate EPOLLOUT because there's no connected event */
       events[*num_ev].events = EPOLLOUT;
       session_evt_data = s->vep.ev.data.u64;
       if (s->session_state == VCL_STATE_DETACHED)
-       events[*num_ev].events |= EPOLLHUP;
+       {
+         events[*num_ev].events |= EPOLLHUP;
+         s->vep.ev.events = 0;
+       }
       break;
     case SESSION_CTRL_EVT_DISCONNECTED:
       if (!e->postponed)
@@ -3127,8 +3190,7 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
          s = vcl_session_get (wrk, e->session_index);
          s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
        }
-      if (vcl_session_is_closed (s) ||
-         !(s->flags & VCL_SESSION_F_IS_VEP_SESSION))
+      if (vcl_session_is_closed (s) || !vcl_ep_session_needs_evt (s, EPOLLHUP))
        {
          if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
            vcl_session_free (wrk, s);
@@ -3152,7 +3214,7 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
          events[*num_ev].events = EPOLLHUP;
        }
       session_evt_data = s->vep.ev.data.u64;
-
+      s->vep.ev.events = 0;
       break;
     case SESSION_CTRL_EVT_BOUND:
       vcl_session_bound_handler (wrk, (session_bound_msg_t *) e->data);
@@ -3170,8 +3232,7 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
          s = vcl_session_get (wrk, sid);
          s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
        }
-      if (vcl_session_is_closed (s) ||
-         !(s->flags & VCL_SESSION_F_IS_VEP_SESSION))
+      if (vcl_session_is_closed (s) || !vcl_ep_session_needs_evt (s, EPOLLHUP))
        {
          if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
            vcl_session_free (wrk, s);
@@ -3190,6 +3251,7 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
          events[*num_ev].events |= EPOLLIN;
        }
       session_evt_data = s->vep.ev.data.u64;
+      s->vep.ev.events = 0;
       break;
     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
       vcl_session_unlisten_reply_handler (wrk, e->data);
@@ -3222,11 +3284,13 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
 
   if (add_event)
     {
+      ASSERT (s->flags & VCL_SESSION_F_IS_VEP_SESSION);
       events[*num_ev].data.u64 = session_evt_data;
       if (EPOLLONESHOT & session_events)
        {
          s = vcl_session_get (wrk, sid);
-         s->vep.ev.events = 0;
+         if (!(events[*num_ev].events & EPOLLHUP))
+           s->vep.ev.events = EPOLLHUP | EPOLLERR;
        }
       else if (!(EPOLLET & session_events))
        {
@@ -3380,6 +3444,11 @@ vcl_epoll_wait_handle_lt (vcl_worker_t *wrk, struct epoll_event *events,
       s = vcl_session_get (wrk, next);
       next = s->vep.lt_next;
 
+      if (s->vep.ev.events == 0)
+       {
+         vec_add1 (to_remove, s->session_index);
+         continue;
+       }
       if ((s->vep.ev.events & EPOLLIN) && (rv = vcl_session_read_ready (s)))
        {
          add_event = 1;
@@ -3402,11 +3471,13 @@ vcl_epoll_wait_handle_lt (vcl_worker_t *wrk, struct epoll_event *events,
        {
          events[*n_evts].events = evt_flags;
          events[*n_evts].data.u64 = evt_data;
+         if (EPOLLONESHOT & s->vep.ev.events)
+           s->vep.ev.events = EPOLLHUP | EPOLLERR;
+         if (evt_flags & EPOLLHUP)
+           s->vep.ev.events = 0;
          *n_evts += 1;
          add_event = 0;
          evt_flags = 0;
-         if (EPOLLONESHOT & s->vep.ev.events)
-           s->vep.ev.events = 0;
          if (*n_evts == maxevents)
            {
              wrk->ep_lt_current = next;
@@ -3514,7 +3585,19 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
       VDBG (2, "VPPCOM_ATTR_GET_NWRITE: sh %u, nwrite = %d", session_handle,
            rv);
       break;
-
+    case VPPCOM_ATTR_GET_NWRITEQ:
+      if (PREDICT_FALSE (!buffer || !buflen || *buflen != sizeof (int)))
+       {
+         rv = VPPCOM_EINVAL;
+         break;
+       }
+      if (!session->tx_fifo || session->session_state == VCL_STATE_DETACHED)
+       {
+         rv = VPPCOM_EINVAL;
+         break;
+       }
+      *(int *) buffer = svm_fifo_max_dequeue (session->tx_fifo);
+      break;
     case VPPCOM_ATTR_GET_FLAGS:
       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
        {
@@ -3597,6 +3680,33 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
        rv = VPPCOM_EINVAL;
       break;
 
+    case VPPCOM_ATTR_GET_ORIGINAL_DST:
+      if (!session->transport.is_ip4)
+       {
+         /* now original dst only support ipv4*/
+         rv = VPPCOM_EAFNOSUPPORT;
+         break;
+       }
+      if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*ep)) &&
+                       ep->ip))
+       {
+         ep->is_ip4 = session->transport.is_ip4;
+         ep->port = session->original_dst_port;
+         clib_memcpy_fast (ep->ip, &session->original_dst_ip4,
+                           sizeof (ip4_address_t));
+         *buflen = sizeof (*ep);
+         VDBG (1,
+               "VPPCOM_ATTR_GET_ORIGINAL_DST: sh %u, is_ip4 = %u, addr = %U"
+               " port %d",
+               session_handle, ep->is_ip4, vcl_format_ip4_address,
+               (ip4_address_t *) (&session->original_dst_ip4),
+               ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
+               clib_net_to_host_u16 (ep->port));
+       }
+      else
+       rv = VPPCOM_EINVAL;
+      break;
+
     case VPPCOM_ATTR_SET_LCL_ADDR:
       if (PREDICT_TRUE (buffer && buflen &&
                        (*buflen >= sizeof (*ep)) && ep->ip))