vcl_private.c revision 458089bb
1/*
2 * Copyright (c) 2018-2019 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <vcl/vcl_private.h>
17
18static pthread_key_t vcl_worker_stop_key;
19
20static const char *
21vppcom_app_state_str (app_state_t state)
22{
23  char *st;
24
25  switch (state)
26    {
27    case STATE_APP_START:
28      st = "STATE_APP_START";
29      break;
30
31    case STATE_APP_CONN_VPP:
32      st = "STATE_APP_CONN_VPP";
33      break;
34
35    case STATE_APP_ENABLED:
36      st = "STATE_APP_ENABLED";
37      break;
38
39    case STATE_APP_ATTACHED:
40      st = "STATE_APP_ATTACHED";
41      break;
42
43    default:
44      st = "UNKNOWN_APP_STATE";
45      break;
46    }
47
48  return st;
49}
50
51int
52vcl_wait_for_app_state_change (app_state_t app_state)
53{
54  vcl_worker_t *wrk = vcl_worker_get_current ();
55  f64 timeout = clib_time_now (&wrk->clib_time) + vcm->cfg.app_timeout;
56
57  while (clib_time_now (&wrk->clib_time) < timeout)
58    {
59      if (vcm->app_state == app_state)
60	return VPPCOM_OK;
61      if (vcm->app_state == STATE_APP_FAILED)
62	return VPPCOM_ECONNABORTED;
63    }
64  VDBG (0, "timeout waiting for state %s (%d)",
65	vppcom_app_state_str (app_state), app_state);
66  vcl_evt (VCL_EVT_SESSION_TIMEOUT, vcm, app_state);
67
68  return VPPCOM_ETIMEDOUT;
69}
70
71vcl_mq_evt_conn_t *
72vcl_mq_evt_conn_alloc (vcl_worker_t * wrk)
73{
74  vcl_mq_evt_conn_t *mqc;
75  pool_get (wrk->mq_evt_conns, mqc);
76  memset (mqc, 0, sizeof (*mqc));
77  return mqc;
78}
79
80u32
81vcl_mq_evt_conn_index (vcl_worker_t * wrk, vcl_mq_evt_conn_t * mqc)
82{
83  return (mqc - wrk->mq_evt_conns);
84}
85
86vcl_mq_evt_conn_t *
87vcl_mq_evt_conn_get (vcl_worker_t * wrk, u32 mq_conn_idx)
88{
89  return pool_elt_at_index (wrk->mq_evt_conns, mq_conn_idx);
90}
91
92int
93vcl_mq_epoll_add_evfd (vcl_worker_t * wrk, svm_msg_q_t * mq)
94{
95  struct epoll_event e = { 0 };
96  vcl_mq_evt_conn_t *mqc;
97  u32 mqc_index;
98  int mq_fd;
99
100  mq_fd = svm_msg_q_get_consumer_eventfd (mq);
101
102  if (wrk->mqs_epfd < 0 || mq_fd == -1)
103    return -1;
104
105  mqc = vcl_mq_evt_conn_alloc (wrk);
106  mqc_index = vcl_mq_evt_conn_index (wrk, mqc);
107  mqc->mq_fd = mq_fd;
108  mqc->mq = mq;
109
110  e.events = EPOLLIN;
111  e.data.u32 = mqc_index;
112  if (epoll_ctl (wrk->mqs_epfd, EPOLL_CTL_ADD, mq_fd, &e) < 0)
113    {
114      VDBG (0, "failed to add mq eventfd to mq epoll fd");
115      return -1;
116    }
117
118  return mqc_index;
119}
120
121int
122vcl_mq_epoll_del_evfd (vcl_worker_t * wrk, u32 mqc_index)
123{
124  vcl_mq_evt_conn_t *mqc;
125
126  if (wrk->mqs_epfd || mqc_index == ~0)
127    return -1;
128
129  mqc = vcl_mq_evt_conn_get (wrk, mqc_index);
130  if (epoll_ctl (wrk->mqs_epfd, EPOLL_CTL_DEL, mqc->mq_fd, 0) < 0)
131    {
132      VDBG (0, "failed to del mq eventfd to mq epoll fd");
133      return -1;
134    }
135  return 0;
136}
137
138static vcl_worker_t *
139vcl_worker_alloc (void)
140{
141  vcl_worker_t *wrk;
142  pool_get (vcm->workers, wrk);
143  memset (wrk, 0, sizeof (*wrk));
144  wrk->wrk_index = wrk - vcm->workers;
145  wrk->forked_child = ~0;
146  return wrk;
147}
148
149static void
150vcl_worker_free (vcl_worker_t * wrk)
151{
152  pool_put (vcm->workers, wrk);
153}
154
155void
156vcl_worker_cleanup (vcl_worker_t * wrk, u8 notify_vpp)
157{
158  clib_spinlock_lock (&vcm->workers_lock);
159  if (notify_vpp)
160    {
161      if (wrk->wrk_index == vcl_get_worker_index ())
162	vcl_send_app_worker_add_del (0 /* is_add */ );
163      else
164	vcl_send_child_worker_del (wrk);
165    }
166  if (wrk->mqs_epfd > 0)
167    close (wrk->mqs_epfd);
168  hash_free (wrk->session_index_by_vpp_handles);
169  vec_free (wrk->mq_events);
170  vec_free (wrk->mq_msg_vector);
171  vcl_worker_free (wrk);
172  clib_spinlock_unlock (&vcm->workers_lock);
173}
174
175static void
176vcl_worker_cleanup_cb (void *arg)
177{
178  vcl_worker_t *wrk = vcl_worker_get_current ();
179  u32 wrk_index = wrk->wrk_index;
180  vcl_worker_cleanup (wrk, 1 /* notify vpp */ );
181  vcl_set_worker_index (~0);
182  VDBG (0, "cleaned up worker %u", wrk_index);
183}
184
185vcl_worker_t *
186vcl_worker_alloc_and_init ()
187{
188  vcl_worker_t *wrk;
189
190  /* This was initialized already */
191  if (vcl_get_worker_index () != ~0)
192    return 0;
193
194  /* Use separate heap map entry for worker */
195  clib_mem_set_thread_index ();
196
197  if (pool_elts (vcm->workers) == vcm->cfg.max_workers)
198    {
199      VDBG (0, "max-workers %u limit reached", vcm->cfg.max_workers);
200      return 0;
201    }
202
203  clib_spinlock_lock (&vcm->workers_lock);
204  wrk = vcl_worker_alloc ();
205  vcl_set_worker_index (wrk->wrk_index);
206  wrk->thread_id = pthread_self ();
207  wrk->current_pid = getpid ();
208
209  wrk->mqs_epfd = -1;
210  if (vcm->cfg.use_mq_eventfd)
211    {
212      wrk->mqs_epfd = epoll_create (1);
213      if (wrk->mqs_epfd < 0)
214	{
215	  clib_unix_warning ("epoll_create() returned");
216	  goto done;
217	}
218    }
219
220  wrk->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
221  clib_time_init (&wrk->clib_time);
222  vec_validate (wrk->mq_events, 64);
223  vec_validate (wrk->mq_msg_vector, 128);
224  vec_reset_length (wrk->mq_msg_vector);
225  vec_validate (wrk->unhandled_evts_vector, 128);
226  vec_reset_length (wrk->unhandled_evts_vector);
227  clib_spinlock_unlock (&vcm->workers_lock);
228
229done:
230  return wrk;
231}
232
233int
234vcl_worker_register_with_vpp (void)
235{
236  vcl_worker_t *wrk = vcl_worker_get_current ();
237
238  clib_spinlock_lock (&vcm->workers_lock);
239
240  vcm->app_state = STATE_APP_ADDING_WORKER;
241  vcl_send_app_worker_add_del (1 /* is_add */ );
242  if (vcl_wait_for_app_state_change (STATE_APP_READY))
243    {
244      VDBG (0, "failed to add worker to vpp");
245      return -1;
246    }
247  if (pthread_key_create (&vcl_worker_stop_key, vcl_worker_cleanup_cb))
248    VDBG (0, "failed to add pthread cleanup function");
249  if (pthread_setspecific (vcl_worker_stop_key, &wrk->thread_id))
250    VDBG (0, "failed to setup key value");
251
252  clib_spinlock_unlock (&vcm->workers_lock);
253
254  VDBG (0, "added worker %u", wrk->wrk_index);
255  return 0;
256}
257
258int
259vcl_worker_set_bapi (void)
260{
261  vcl_worker_t *wrk = vcl_worker_get_current ();
262  int i;
263
264  /* Find the first worker with the same pid */
265  for (i = 0; i < vec_len (vcm->workers); i++)
266    {
267      if (i == wrk->wrk_index)
268	continue;
269      if (vcm->workers[i].current_pid == wrk->current_pid)
270	{
271	  wrk->vl_input_queue = vcm->workers[i].vl_input_queue;
272	  wrk->my_client_index = vcm->workers[i].my_client_index;
273	  return 0;
274	}
275    }
276  return -1;
277}
278
279svm_msg_q_t *
280vcl_worker_ctrl_mq (vcl_worker_t * wrk)
281{
282  return wrk->ctrl_mq;
283}
284
285void
286vcl_segment_table_add (u64 segment_handle, u32 svm_segment_index)
287{
288  clib_rwlock_writer_lock (&vcm->segment_table_lock);
289  hash_set (vcm->segment_table, segment_handle, svm_segment_index);
290  clib_rwlock_writer_unlock (&vcm->segment_table_lock);
291}
292
293u32
294vcl_segment_table_lookup (u64 segment_handle)
295{
296  uword *seg_indexp;
297
298  clib_rwlock_reader_lock (&vcm->segment_table_lock);
299  seg_indexp = hash_get (vcm->segment_table, segment_handle);
300  clib_rwlock_reader_unlock (&vcm->segment_table_lock);
301
302  if (!seg_indexp)
303    return VCL_INVALID_SEGMENT_INDEX;
304  return ((u32) * seg_indexp);
305}
306
307void
308vcl_segment_table_del (u64 segment_handle)
309{
310  clib_rwlock_writer_lock (&vcm->segment_table_lock);
311  hash_unset (vcm->segment_table, segment_handle);
312  clib_rwlock_writer_unlock (&vcm->segment_table_lock);
313}
314
315void
316vcl_cleanup_bapi (void)
317{
318  socket_client_main_t *scm = &socket_client_main;
319  api_main_t *am = &api_main;
320
321  am->my_client_index = ~0;
322  am->my_registration = 0;
323  am->vl_input_queue = 0;
324  am->msg_index_by_name_and_crc = 0;
325  scm->socket_fd = 0;
326
327  vl_client_api_unmap ();
328}
329
330int
331vcl_session_read_ready (vcl_session_t * session)
332{
333  /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
334  if (PREDICT_FALSE (session->is_vep))
335    {
336      VDBG (0, "ERROR: session %u: cannot read from an epoll session!",
337	    session->session_index);
338      return VPPCOM_EBADFD;
339    }
340
341  if (PREDICT_FALSE (!(session->session_state & (STATE_OPEN | STATE_LISTEN))))
342    {
343      vcl_session_state_t state = session->session_state;
344      int rv;
345
346      rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
347
348      VDBG (1, "session %u [0x%llx]: not open! state 0x%x (%s), ret %d (%s)",
349	    session->session_index, session->vpp_handle, state,
350	    vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
351      return rv;
352    }
353
354  if (session->session_state & STATE_LISTEN)
355    return clib_fifo_elts (session->accept_evts_fifo);
356
357  if (vcl_session_is_ct (session))
358    return svm_fifo_max_dequeue_cons (session->ct_rx_fifo);
359
360  return svm_fifo_max_dequeue_cons (session->rx_fifo);
361}
362
363int
364vcl_session_write_ready (vcl_session_t * session)
365{
366  /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
367  if (PREDICT_FALSE (session->is_vep))
368    {
369      VDBG (0, "session %u [0x%llx]: cannot write to an epoll session!",
370	    session->session_index, session->vpp_handle);
371      return VPPCOM_EBADFD;
372    }
373
374  if (PREDICT_FALSE (session->session_state & STATE_LISTEN))
375    {
376      if (session->tx_fifo)
377	return svm_fifo_max_enqueue_prod (session->tx_fifo);
378      else
379	return VPPCOM_EBADFD;
380    }
381
382  if (PREDICT_FALSE (!(session->session_state & STATE_OPEN)))
383    {
384      vcl_session_state_t state = session->session_state;
385      int rv;
386
387      rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
388      VDBG (0, "session %u [0x%llx]: not open! state 0x%x (%s), ret %d (%s)",
389	    session->session_index, session->vpp_handle, state,
390	    vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
391      return rv;
392    }
393
394  if (vcl_session_is_ct (session))
395    return svm_fifo_max_enqueue_prod (session->ct_tx_fifo);
396
397  return svm_fifo_max_enqueue_prod (session->tx_fifo);
398}
399
400/*
401 * fd.io coding-style-patch-verification: ON
402 *
403 * Local Variables:
404 * eval: (c-set-style "gnu")
405 * End:
406 */
407