TAS
TCP Acceleration as an OS Service
tas.c
1 /*
2  * Copyright 2019 University of Washington, Max Planck Institute for
3  * Software Systems, and The University of Texas at Austin
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #define _GNU_SOURCE
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <assert.h>
31 #include <pthread.h>
32 
33 #include <rte_config.h>
34 #include <rte_eal.h>
35 #include <rte_lcore.h>
36 #include <rte_launch.h>
37 #include <rte_cycles.h>
38 #include <rte_malloc.h>
39 
40 #include <tas_memif.h>
41 #include <utils_timeout.h>
42 
43 #include <tas.h>
44 #include <fastpath.h>
45 #include "fast/internal.h"
46 
47 struct core_load {
48  uint64_t cyc_busy;
49 };
50 
51 struct configuration config;
52 
53 unsigned fp_cores_max;
54 volatile unsigned fp_cores_cur = 1;
55 volatile unsigned fp_scale_to = 0;
56 
57 static unsigned threads_launched = 0;
58 int exited;
59 
60 struct dataplane_context **ctxs = NULL;
61 struct core_load *core_loads = NULL;
62 
63 static int start_threads(void);
64 static void thread_error(void);
65 static int common_thread(void *arg);
66 
67 
68 static void *slowpath_thread(void)
69 {
70  slowpath_main();
71  return NULL;
72 }
73 
74 int main(int argc, char *argv[])
75 {
76  int res = EXIT_SUCCESS;
77 
78  /* parse command line options */
79  if (config_parse(&config, argc, argv) != 0) {
80  res = EXIT_FAILURE;
81  goto error_exit;
82  }
83  fp_cores_max = config.fp_cores_max;
84 
85  /* allocate shared memory before dpdk grabs all huge pages */
86  if (shm_preinit() != 0) {
87  res = EXIT_FAILURE;
88  goto error_exit;
89  }
90 
91  /* initialize dpdk */
92  rte_log_set_global_level(RTE_LOG_ERR);
93  if (rte_eal_init(config.dpdk_argc, config.dpdk_argv) < 0) {
94  fprintf(stderr, "dpdk init failed\n");
95  goto error_exit;
96  }
97 
98  if ((core_loads = calloc(fp_cores_max, sizeof(*core_loads))) == NULL) {
99  res = EXIT_FAILURE;
100  fprintf(stderr, "core loads alloc failed\n");
101  goto error_exit;
102  }
103 
104  if (shm_init(fp_cores_max) != 0) {
105  res = EXIT_FAILURE;
106  fprintf(stderr, "dma init failed\n");
107  goto error_exit;
108  }
109 
110  if (network_init(fp_cores_max) != 0) {
111  res = EXIT_FAILURE;
112  fprintf(stderr, "network init failed\n");
113  goto error_shm_cleanup;
114  }
115 
116  if (dataplane_init() != 0) {
117  res = EXIT_FAILURE;
118  fprintf(stderr, "dpinit failed\n");
119  goto error_network_cleanup;
120  }
121 
122  shm_set_ready();
123 
124  if (start_threads() != 0) {
125  res = EXIT_FAILURE;
126  goto error_dataplane_cleanup;
127  }
128 
129  /* Start kernel thread */
130  slowpath_thread();
131 
132 error_dataplane_cleanup:
133  /* TODO */
134 error_network_cleanup:
135  network_cleanup();
136 error_shm_cleanup:
137  shm_cleanup();
138 error_exit:
139  return res;
140 }
141 
142 static int common_thread(void *arg)
143 {
144  uint16_t id = (uintptr_t) arg;
145  struct dataplane_context *ctx;
146 
147  {
148  char name[17];
149  snprintf(name, sizeof(name), "stcp-fp-%u", id);
150  pthread_setname_np(pthread_self(), name);
151  }
152 
153  /* Allocate fastpath core context */
154  if ((ctx = rte_zmalloc("fastpath core context", sizeof(*ctx), 0)) == NULL) {
155  fprintf(stderr, "Allocating fastpath core context failed\n");
156  goto error_alloc;
157  }
158  ctxs[id] = ctx;
159  ctx->id = id;
160 
161 
162  /* initialize trace if enabled */
163 #ifdef FLEXNIC_TRACING
164  if (trace_thread_init(id) != 0) {
165  fprintf(stderr, "initializing trace failed\n");
166  goto error_trace;
167  }
168 #endif
169 
170  /* initialize data plane context */
171  if (dataplane_context_init(ctx) != 0) {
172  fprintf(stderr, "initializing data plane context\n");
173  goto error_dpctx;
174  }
175 
176  /* poll doorbells and network */
177  dataplane_loop(ctx);
178 
179  dataplane_context_destroy(ctx);
180  return 0;
181 
182 error_dpctx:
183 #ifdef FLEXNIC_TRACING
184 error_trace:
185 #endif
186  dataplane_context_destroy(ctx);
187 error_alloc:
188  thread_error();
189  return -1;
190 }
191 
192 static int start_threads(void)
193 {
194  unsigned cores_avail, cores_needed, core;
195  void *arg;
196 
197  cores_avail = rte_lcore_count();
198  /* fast path cores + one slow path core */
199  cores_needed = fp_cores_max + 1;
200 
201  if ((ctxs = rte_calloc("context list", fp_cores_max, sizeof(*ctxs), 64)) == NULL) {
202  perror("datplane_init: calloc failed");
203  return -1;
204  }
205 
206  /* check that we have enough cores */
207  if (cores_avail < cores_needed) {
208  fprintf(stderr, "Not enough cores: got %u need %u\n", cores_avail,
209  cores_needed);
210  return -1;
211  }
212 
213  /* start common threads */
214  RTE_LCORE_FOREACH_SLAVE(core) {
215  if (threads_launched < fp_cores_max) {
216  arg = (void *) (uintptr_t) threads_launched;
217  if (rte_eal_remote_launch(common_thread, arg, core) != 0) {
218  fprintf(stderr, "ERROR\n");
219  return -1;
220  }
221  threads_launched++;
222  }
223  }
224 
225  return 0;
226 }
227 
228 static void thread_error(void)
229 {
230  fprintf(stderr, "thread_error\n");
231  abort();
232 }
233 
234 int flexnic_scale_to(uint32_t cores)
235 {
236  if (fp_scale_to != 0) {
237  fprintf(stderr, "flexnic_scale_to: already scaling\n");
238  return -1;
239  }
240 
241  fp_scale_to = cores;
242 
243  notify_fastpath_core(0);
244  return 0;
245 }
246 
247 void flexnic_loadmon(uint32_t ts)
248 {
249  uint64_t cyc_busy = 0, x, tsc, cycles, id_cyc;
250  unsigned i, num_cores;
251  static uint64_t ewma_busy = 0, ewma_cycles = 0, last_tsc = 0, kdrops = 0;
252  static int waiting = 1, waiting_n = 0, count = 0;
253 
254  num_cores = fp_cores_cur;
255 
256  /* sum up busy cycles from all cores */
257  for (i = 0; i < num_cores; i++) {
258  if (ctxs[i] == NULL)
259  return;
260 
261  x = ctxs[i]->loadmon_cyc_busy;
262  cyc_busy += x - core_loads[i].cyc_busy;
263  core_loads[i].cyc_busy = x;
264 
265  kdrops += ctxs[i]->kernel_drop;
266  ctxs[i]->kernel_drop = 0;
267  }
268 
269  /* measure cpu cycles since last call */
270  tsc = rte_get_tsc_cycles();
271  if (last_tsc == 0) {
272  last_tsc = tsc;
273  return;
274  }
275  cycles = tsc - last_tsc;
276  last_tsc = tsc;
277 
278  /* ewma for busy cycles and total cycles */
279  ewma_busy = (7 * ewma_busy + cyc_busy) / 8;
280  ewma_cycles = (7 * ewma_cycles + cycles) / 8;
281 
282  /* periodically print out staticstics */
283  if (count++ % 100 == 0) {
284  if (!config.quiet)
285  fprintf(stderr, "flexnic_loadmon: status cores = %u busy = %lu "
286  "cycles =%lu kdrops=%lu\n", num_cores, ewma_busy, ewma_cycles,
287  kdrops);
288  kdrops = 0;
289  }
290 
291  /* waiting period after scaling decsions */
292  if (waiting && ++waiting_n < 10)
293  return;
294 
295  /* calculate idle cycles */
296  if (num_cores * ewma_cycles > ewma_busy) {
297  id_cyc = num_cores * ewma_cycles - ewma_busy;
298  } else {
299  id_cyc = 0;
300  }
301 
302  /* scale down if idle iterations more than 1.25 cores are idle */
303  if (num_cores > 1 && id_cyc > ewma_cycles * 5 / 4) {
304  if (!config.quiet)
305  fprintf(stderr, "flexnic_loadmon: down cores = %u idle_cyc = %lu "
306  "1.2 cores = %lu\n", num_cores, id_cyc, ewma_cycles * 5 / 4);
307  flexnic_scale_to(num_cores - 1);
308  waiting = 1;
309  waiting_n = 0;
310  return;
311  }
312 
313  /* scale up if idle iterations less than .2 of a core */
314  if (num_cores < fp_cores_max && id_cyc < ewma_cycles / 5) {
315  if (!config.quiet)
316  fprintf(stderr, "flexnic_loadmon: up cores = %u idle_cyc = %lu "
317  "0.2 cores = %lu\n", num_cores, id_cyc, ewma_cycles / 5);
318  flexnic_scale_to(num_cores + 1);
319  waiting = 1;
320  waiting_n = 0;
321  return;
322  }
323 }
Definition: tas.c:47
int dpdk_argc
Definition: config.h:139
uint32_t fp_cores_max
Definition: config.h:115
char ** dpdk_argv
Definition: config.h:137