session: api to add new transport types
[vpp.git] / src / vcl / vppcom.c
index 4224a08..c509f93 100644 (file)
@@ -60,8 +60,8 @@ vppcom_session_state_str (vcl_session_state_t state)
 
   switch (state)
     {
-    case STATE_START:
-      st = "STATE_START";
+    case STATE_CLOSED:
+      st = "STATE_CLOSED";
       break;
 
     case STATE_CONNECT:
@@ -84,8 +84,8 @@ vppcom_session_state_str (vcl_session_state_t state)
       st = "STATE_DISCONNECT";
       break;
 
-    case STATE_FAILED:
-      st = "STATE_FAILED";
+    case STATE_DETACHED:
+      st = "STATE_DETACHED";
       break;
 
     case STATE_UPDATED:
@@ -443,7 +443,7 @@ vcl_session_connected_handler (vcl_worker_t * wrk,
     {
       VDBG (0, "ERROR: session index %u: connect failed! %U",
            session_index, format_api_error, ntohl (mp->retval));
-      session->session_state = STATE_FAILED | STATE_DISCONNECT;
+      session->session_state = STATE_DETACHED | STATE_DISCONNECT;
       session->vpp_handle = mp->handle;
       return session_index;
     }
@@ -457,7 +457,7 @@ vcl_session_connected_handler (vcl_worker_t * wrk,
     {
       VDBG (0, "segment for session %u is not mounted!",
            session->session_index);
-      session->session_state = STATE_FAILED | STATE_DISCONNECT;
+      session->session_state = STATE_DETACHED | STATE_DISCONNECT;
       vcl_send_session_disconnect (wrk, session);
       return session_index;
     }
@@ -479,7 +479,7 @@ vcl_session_connected_handler (vcl_worker_t * wrk,
        {
          VDBG (0, "ct segment for session %u is not mounted!",
                session->session_index);
-         session->session_state = STATE_FAILED | STATE_DISCONNECT;
+         session->session_state = STATE_DETACHED | STATE_DISCONNECT;
          vcl_send_session_disconnect (wrk, session);
          return session_index;
        }
@@ -565,7 +565,7 @@ vcl_session_bound_handler (vcl_worker_t * wrk, session_bound_msg_t * mp)
            format_api_error, mp->retval);
       if (session)
        {
-         session->session_state = STATE_FAILED;
+         session->session_state = STATE_DETACHED;
          session->vpp_handle = mp->handle;
          return sid;
        }
@@ -589,7 +589,7 @@ vcl_session_bound_handler (vcl_worker_t * wrk, session_bound_msg_t * mp)
   vec_validate (wrk->vpp_event_queues, 0);
   wrk->vpp_event_queues[0] = session->vpp_evt_q;
 
-  if (session->is_dgram)
+  if (vcl_session_is_cl (session))
     {
       svm_fifo_t *rx_fifo, *tx_fifo;
       session->vpp_evt_q = uword_to_pointer (mp->vpp_evt_q, svm_msg_q_t *);
@@ -629,6 +629,36 @@ vcl_session_unlisten_reply_handler (vcl_worker_t * wrk, void *data)
   vcl_session_free (wrk, s);
 }
 
+static void
+vcl_session_migrated_handler (vcl_worker_t * wrk, void *data)
+{
+  session_migrated_msg_t *mp = (session_migrated_msg_t *) data;
+  vcl_session_t *s;
+
+  s = vcl_session_get_w_vpp_handle (wrk, mp->handle);
+  if (!s)
+    {
+      VDBG (0, "Migrated notification with wrong handle %llx", mp->handle);
+      return;
+    }
+
+  s->vpp_thread_index = mp->vpp_thread_index;
+  s->vpp_evt_q = uword_to_pointer (mp->vpp_evt_q, svm_msg_q_t *);
+
+  vec_validate (wrk->vpp_event_queues, s->vpp_thread_index);
+  wrk->vpp_event_queues[s->vpp_thread_index] = s->vpp_evt_q;
+
+  vcl_session_table_del_vpp_handle (wrk, mp->handle);
+  vcl_session_table_add_vpp_handle (wrk, mp->new_handle, s->session_index);
+
+  /* Generate new tx event if we have outstanding data */
+  if (svm_fifo_has_event (s->tx_fifo))
+    app_send_io_evt_to_vpp (s->vpp_evt_q, s->tx_fifo->master_session_index,
+                           SESSION_IO_EVT_TX, SVM_Q_WAIT);
+
+  VDBG (0, "Migrated 0x%x to thread %u", mp->handle, s->vpp_thread_index);
+}
+
 static vcl_session_t *
 vcl_session_accepted (vcl_worker_t * wrk, session_accepted_msg_t * msg)
 {
@@ -649,6 +679,7 @@ vcl_session_accepted (vcl_worker_t * wrk, session_accepted_msg_t * msg)
     }
 
   clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
+  vcl_msg->flags = 0;
   vcl_msg->accepted_msg = *msg;
   /* Session handle points to listener until fully accepted by app */
   vcl_session_table_add_vpp_handle (wrk, msg->handle, session->session_index);
@@ -669,6 +700,10 @@ vcl_session_disconnected_handler (vcl_worker_t * wrk,
       return 0;
     }
 
+  /* Late disconnect notification on a session that has been closed */
+  if (session->session_state == STATE_CLOSED)
+    return 0;
+
   /* Caught a disconnect before actually accepting the session */
   if (session->session_state == STATE_LISTEN)
     {
@@ -678,10 +713,39 @@ vcl_session_disconnected_handler (vcl_worker_t * wrk,
       return 0;
     }
 
-  session->session_state = STATE_VPP_CLOSING;
+  /* If not already reset change state */
+  if (session->session_state != STATE_DISCONNECT)
+    session->session_state = STATE_VPP_CLOSING;
+
   return session;
 }
 
+static void
+vcl_session_cleanup_handler (vcl_worker_t * wrk, void *data)
+{
+  session_cleanup_msg_t *msg;
+  vcl_session_t *session;
+
+  msg = (session_cleanup_msg_t *) data;
+  session = vcl_session_get_w_vpp_handle (wrk, msg->handle);
+  if (!session)
+    {
+      VDBG (0, "disconnect confirmed for unknown handle 0x%llx", msg->handle);
+      return;
+    }
+
+  vcl_session_table_del_vpp_handle (wrk, msg->handle);
+  /* Should not happen. App did not close the connection so don't free it. */
+  if (session->session_state != STATE_CLOSED)
+    {
+      VDBG (0, "app did not close session %d", session->session_index);
+      session->session_state = STATE_DETACHED;
+      session->vpp_handle = VCL_INVALID_SESSION_HANDLE;
+      return;
+    }
+  vcl_session_free (wrk, session);
+}
+
 static void
 vcl_session_req_worker_update_handler (vcl_worker_t * wrk, void *data)
 {
@@ -812,6 +876,12 @@ vcl_handle_mq_event (vcl_worker_t * wrk, session_event_t * e)
     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
       vcl_session_unlisten_reply_handler (wrk, e->data);
       break;
+    case SESSION_CTRL_EVT_MIGRATED:
+      vcl_session_migrated_handler (wrk, e->data);
+      break;
+    case SESSION_CTRL_EVT_CLEANUP:
+      vcl_session_cleanup_handler (wrk, e->data);
+      break;
     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
       vcl_session_req_worker_update_handler (wrk, e->data);
       break;
@@ -852,7 +922,7 @@ vppcom_wait_for_session_state_change (u32 session_index,
        {
          return VPPCOM_OK;
        }
-      if (session->session_state & STATE_FAILED)
+      if (session->session_state & STATE_DETACHED)
        {
          return VPPCOM_ECONNREFUSED;
        }
@@ -1166,7 +1236,7 @@ vppcom_session_create (u8 proto, u8 is_nonblocking)
   session = vcl_session_alloc (wrk);
 
   session->session_type = proto;
-  session->session_state = STATE_START;
+  session->session_state = STATE_CLOSED;
   session->vpp_handle = ~0;
   session->is_dgram = vcl_proto_is_dgram (proto);
 
@@ -1211,55 +1281,64 @@ vcl_session_cleanup (vcl_worker_t * wrk, vcl_session_t * session,
 
          next_sh = session->vep.next_sh;
        }
+      goto cleanup;
     }
-  else
+
+  if (session->is_vep_session)
     {
-      if (session->is_vep_session)
-       {
-         rv = vppcom_epoll_ctl (vep_sh, EPOLL_CTL_DEL, sh, 0);
-         if (rv < 0)
-           VDBG (0, "session %u [0x%llx]: EPOLL_CTL_DEL vep_idx %u "
-                 "failed! rv %d (%s)", session->session_index, vpp_handle,
-                 vep_sh, rv, vppcom_retval_str (rv));
-       }
+      rv = vppcom_epoll_ctl (vep_sh, EPOLL_CTL_DEL, sh, 0);
+      if (rv < 0)
+       VDBG (0, "session %u [0x%llx]: EPOLL_CTL_DEL vep_idx %u "
+             "failed! rv %d (%s)", session->session_index, vpp_handle,
+             vep_sh, rv, vppcom_retval_str (rv));
+    }
 
-      if (!do_disconnect)
-       {
-         VDBG (1, "session %u [0x%llx] disconnect skipped",
-               session->session_index, vpp_handle);
-         goto cleanup;
-       }
+  if (!do_disconnect)
+    {
+      VDBG (1, "session %u [0x%llx] disconnect skipped",
+           session->session_index, vpp_handle);
+      goto cleanup;
+    }
 
-      if (state & STATE_LISTEN)
-       {
-         rv = vppcom_session_unbind (sh);
-         if (PREDICT_FALSE (rv < 0))
-           VDBG (0, "session %u [0x%llx]: listener unbind failed! "
-                 "rv %d (%s)", session->session_index, vpp_handle, rv,
-                 vppcom_retval_str (rv));
-         return rv;
-       }
-      else if ((state & STATE_OPEN)
-              || (vcl_session_is_connectable_listener (wrk, session)))
-       {
-         rv = vppcom_session_disconnect (sh);
-         if (PREDICT_FALSE (rv < 0))
-           VDBG (0, "ERROR: session %u [0x%llx]: disconnect failed!"
-                 " rv %d (%s)", session->session_index, vpp_handle,
-                 rv, vppcom_retval_str (rv));
-       }
-      else if (state == STATE_DISCONNECT)
-       {
-         svm_msg_q_t *mq = vcl_session_vpp_evt_q (wrk, session);
-         vcl_send_session_reset_reply (mq, wrk->my_client_index,
-                                       session->vpp_handle, 0);
-       }
+  if (state & STATE_LISTEN)
+    {
+      rv = vppcom_session_unbind (sh);
+      if (PREDICT_FALSE (rv < 0))
+       VDBG (0, "session %u [0x%llx]: listener unbind failed! "
+             "rv %d (%s)", session->session_index, vpp_handle, rv,
+             vppcom_retval_str (rv));
+      return rv;
+    }
+  else if ((state & STATE_OPEN)
+          || (vcl_session_is_connectable_listener (wrk, session)))
+    {
+      rv = vppcom_session_disconnect (sh);
+      if (PREDICT_FALSE (rv < 0))
+       VDBG (0, "ERROR: session %u [0x%llx]: disconnect failed!"
+             " rv %d (%s)", session->session_index, vpp_handle,
+             rv, vppcom_retval_str (rv));
+    }
+  else if (state == STATE_DISCONNECT)
+    {
+      svm_msg_q_t *mq = vcl_session_vpp_evt_q (wrk, session);
+      vcl_send_session_reset_reply (mq, wrk->my_client_index,
+                                   session->vpp_handle, 0);
+    }
+  else if (state == STATE_DETACHED)
+    {
+      /* Should not happen. VPP cleaned up before app confirmed close */
+      VDBG (0, "vpp freed session %d before close", session->session_index);
+      goto free_session;
     }
 
-  VDBG (0, "session %u [0x%llx] removed", session->session_index, vpp_handle);
+  session->session_state = STATE_CLOSED;
+
+  /* Session is removed only after vpp confirms the disconnect */
+  return rv;
 
 cleanup:
   vcl_session_table_del_vpp_handle (wrk, vpp_handle);
+free_session:
   vcl_session_free (wrk, session);
   vcl_evt (VCL_EVT_CLOSE, session, rv);
 
@@ -1451,10 +1530,6 @@ vppcom_unformat_proto (uint8_t * proto, char *proto_str)
     *proto = VPPCOM_PROTO_UDPC;
   else if (!strcmp (proto_str, "udpc"))
     *proto = VPPCOM_PROTO_UDPC;
-  else if (!strcmp (proto_str, "SCTP"))
-    *proto = VPPCOM_PROTO_SCTP;
-  else if (!strcmp (proto_str, "sctp"))
-    *proto = VPPCOM_PROTO_SCTP;
   else if (!strcmp (proto_str, "TLS"))
     *proto = VPPCOM_PROTO_TLS;
   else if (!strcmp (proto_str, "tls"))
@@ -2127,6 +2202,12 @@ vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
       vcl_session_unlisten_reply_handler (wrk, e->data);
       break;
+    case SESSION_CTRL_EVT_MIGRATED:
+      vcl_session_migrated_handler (wrk, e->data);
+      break;
+    case SESSION_CTRL_EVT_CLEANUP:
+      vcl_session_cleanup_handler (wrk, e->data);
+      break;
     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
       vcl_session_worker_update_reply_handler (wrk, e->data);
       break;
@@ -2518,6 +2599,23 @@ vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
        svm_fifo_add_want_deq_ntf (session->tx_fifo,
                                   SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
 
+      /* Generate EPOLLOUT if tx fifo not full */
+      if ((event->events & EPOLLOUT) &&
+         (vcl_session_write_ready (session) > 0))
+       {
+         session_event_t e = { 0 };
+         e.event_type = SESSION_IO_EVT_TX;
+         e.session_index = session->session_index;
+         vec_add1 (wrk->unhandled_evts_vector, e);
+       }
+      /* Generate EPOLLIN if rx fifo has data */
+      if ((event->events & EPOLLIN) && (vcl_session_read_ready (session) > 0))
+       {
+         session_event_t e = { 0 };
+         e.event_type = SESSION_IO_EVT_RX;
+         e.session_index = session->session_index;
+         vec_add1 (wrk->unhandled_evts_vector, e);
+       }
       VDBG (1, "EPOLL_CTL_ADD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
            vep_handle, session_handle, event->events, event->data.u64);
       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
@@ -2543,6 +2641,17 @@ vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
          rv = VPPCOM_EINVAL;
          goto done;
        }
+
+      /* Generate EPOLLOUT when tx_fifo/ct_tx_fifo not full */
+      if ((event->events & EPOLLOUT) &&
+         !(session->vep.ev.events & EPOLLOUT) &&
+         (vcl_session_write_ready (session) > 0))
+       {
+         session_event_t e = { 0 };
+         e.event_type = SESSION_IO_EVT_TX;
+         e.session_index = session->session_index;
+         vec_add1 (wrk->unhandled_evts_vector, e);
+       }
       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
       session->vep.ev = *event;
       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
@@ -2682,7 +2791,7 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       add_event = 1;
       events[*num_ev].events |= EPOLLOUT;
       session_evt_data = session->vep.ev.data.u64;
-      if (session->session_state & STATE_FAILED)
+      if (session->session_state & STATE_DETACHED)
        events[*num_ev].events |= EPOLLHUP;
       break;
     case SESSION_CTRL_EVT_DISCONNECTED:
@@ -2691,8 +2800,6 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       if (!session)
        break;
       session_events = session->vep.ev.events;
-      if (!((EPOLLHUP | EPOLLRDHUP) & session_events))
-       break;
       add_event = 1;
       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
       session_evt_data = session->vep.ev.data.u64;
@@ -2702,8 +2809,6 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       if (!(session = vcl_session_get (wrk, sid)))
        break;
       session_events = session->vep.ev.events;
-      if (!((EPOLLHUP | EPOLLRDHUP) & session_events))
-       break;
       add_event = 1;
       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
       session_evt_data = session->vep.ev.data.u64;
@@ -2711,6 +2816,12 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
       vcl_session_unlisten_reply_handler (wrk, e->data);
       break;
+    case SESSION_CTRL_EVT_MIGRATED:
+      vcl_session_migrated_handler (wrk, e->data);
+      break;
+    case SESSION_CTRL_EVT_CLEANUP:
+      vcl_session_cleanup_handler (wrk, e->data);
+      break;
     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
       vcl_session_req_worker_update_handler (wrk, e->data);
       break;
@@ -3082,7 +3193,8 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
 
          /* VPP-TBD */
          *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
-                               session->tx_fifo ? session->tx_fifo->nitems :
+                               session->tx_fifo ?
+                               svm_fifo_size (session->tx_fifo) :
                                vcm->cfg.tx_fifo_size);
          *buflen = sizeof (u32);
 
@@ -3113,7 +3225,8 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
 
          /* VPP-TBD */
          *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
-                               session->rx_fifo ? session->rx_fifo->nitems :
+                               session->rx_fifo ?
+                               svm_fifo_size (session->rx_fifo) :
                                vcm->cfg.rx_fifo_size);
          *buflen = sizeof (u32);