Motr  M0
sim.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011-2020 Seagate Technology LLC and/or its Affiliates
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * For any questions about this software or licensing,
17  * please email opensource@seagate.com or cortx-questions@seagate.com.
18  *
19  */
20 
21 
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <err.h>
26 #include <signal.h> /* MINSIGSTKSZ */
27 #include <sysexits.h>
28 
29 #include <execinfo.h>
30 
31 #include "motr/magic.h"
32 #include "lib/assert.h"
33 #include "desim/sim.h"
34 
40 M0_TL_DESCR_DEFINE(ca, "call-outs", static, struct sim_callout,
41  sc_linkage, sc_magic, M0_DESIM_SIM_CALLOUT_MAGIC,
43 M0_TL_DEFINE(ca, static, struct sim_callout);
44 
45 M0_TL_DESCR_DEFINE(sim_thr, "threads", static, struct sim_thread,
46  st_block, st_magic, M0_DESIM_SIM_THREAD_MAGIC,
48 M0_TL_DEFINE(sim_thr, static, struct sim_thread);
49 
50 int vasprintf(char **strp, const char *fmt, va_list ap);
51 
52 #if 0
53 static int workload_debug(struct sim_callout *call)
54 {
55  long n = (long)call->sc_datum;
56 
57  sim_log(call->sc_sim,
58  SLL_DEBUG, "here: %p %llu\n", call, call->sc_sim->ss_bolt);
59  if (n < 1000000) {
60  sim_timer_rearm(call, 1, workload_debug, (void *)(n + 1));
61  return 0;
62  } else
63  return 1;
64 }
65 #endif
66 
71 static struct sim_thread *sim_current = NULL;
72 
80 static ucontext_t sim_idle_ctx;
81 
85 M0_INTERNAL void *sim_alloc(size_t size)
86 {
87  void *area;
88 
89  area = malloc(size);
90  M0_ASSERT(area != NULL);
91  memset(area, 0, size);
92  return area;
93 }
94 
98 M0_INTERNAL void sim_free(void *ptr)
99 {
100  free(ptr);
101 }
102 
106 M0_INTERNAL void sim_init(struct sim *state)
107 {
108  state->ss_bolt = 0;
109  ca_tlist_init(&state->ss_future);
110  getcontext(&sim_idle_ctx);
111 }
112 
116 M0_INTERNAL void sim_fini(struct sim *state)
117 {
118  ca_tlist_fini(&state->ss_future);
119 }
120 
129 M0_INTERNAL void sim_run(struct sim *state)
130 {
131  struct sim_callout *call;
132 
133  while (!ca_tlist_is_empty(&state->ss_future)) {
135  call = ca_tlist_pop(&state->ss_future);
136  M0_ASSERT(call->sc_time >= state->ss_bolt);
137  /* jump to the future */
138  state->ss_bolt = call->sc_time;
139  if (call->sc_call(call))
140  /*
141  * timer wasn't rearmed.
142  */
143  sim_free(call);
144  }
145 }
146 
150 static void sim_call_place(struct sim *sim, struct sim_callout *call)
151 {
152  struct sim_callout *found;
153 
154  /*
155  * This is the most time consuming function for long simulations. To
156  * speed it up, sim::ss_future list can be replaced with a more advanced
157  * data structure, like a tree or a skip-list of some sort.
158  */
159 
160  found = m0_tl_find(ca, scan, &sim->ss_future,
161  scan->sc_time > call->sc_time);
162 
163  if (found != NULL)
164  ca_tlist_add_before(found, call);
165  else
166  ca_tlist_add_tail(&sim->ss_future, call);
167 }
168 
172 static void sim_timer_init(struct sim *state, struct sim_callout *call,
173  sim_time_t delta, sim_call_t *cfunc, void *datum)
174 {
175  call->sc_time = state->ss_bolt + delta;
176  M0_ASSERT(call->sc_time >= state->ss_bolt); /* overflow */
177  call->sc_call = cfunc;
178  call->sc_datum = datum;
179  call->sc_sim = state;
180  ca_tlink_init(call);
181  sim_call_place(state, call);
182 }
183 
189 M0_INTERNAL void sim_timer_add(struct sim *state, sim_time_t delta,
190  sim_call_t * cfunc, void *datum)
191 {
192  struct sim_callout *call;
193 
194  call = sim_alloc(sizeof *call);
195  sim_timer_init(state, call, delta, cfunc, datum);
196 }
197 
202 M0_INTERNAL void sim_timer_rearm(struct sim_callout *call, sim_time_t delta,
203  sim_call_t * cfunc, void *datum)
204 {
205  sim_timer_init(call->sc_sim, call, delta, cfunc, datum);
206 }
207 
212 static void sim_thread_fix(struct sim_thread *thread)
213 {
214  thread->st_ctx.uc_stack.ss_size = thread->st_size;
215 }
216 
220 static void sim_thread_resume(struct sim_thread *thread)
221 {
222  int rc;
223 
226  rc = swapcontext(&sim_idle_ctx, &thread->st_ctx);
227  if (rc != 0)
228  err(EX_UNAVAILABLE, "resume: swapcontext");
229 }
230 
236 {
237  int rc;
238 
240  sim_current = NULL;
241  rc = swapcontext(&thread->st_ctx, &sim_idle_ctx);
242  if (rc != 0)
243  err(EX_UNAVAILABLE, "suspend: swapcontext");
245 }
246 
247 /*
248  * The sim{en,de}code*() and sim_trampoline() functions below are for
249  * portability: makecontext(3) creates a context to execute a supplied function
250  * with a given number of integer (int) arguments. To pass non-integer
251  * parameters (pointers), they have to be encoded as integers.
252  *
253  * Very simple encoding scheme is used, where each pointer is encoded as a
254  * couple of integers.
255  *
256  * Note, that this is not an idle experiment in obfuscation: in some
257  * configurations alignments of integer and pointer types are different.
258  */
259 
260 static void *sim_decode(int p0, int p1)
261 {
262  return (void *)((((uint64_t)p0) << 32) | (((uint64_t)p1) & 0xffffffff));
263 }
264 
265 static int sim_encode0(void *p)
266 {
267  return ((uint64_t)p) >> 32;
268 }
269 
270 static int sim_encode1(void *p)
271 {
272  return ((uint64_t)p) & 0xffffffff;
273 }
274 
275 static void sim_trampoline(int func0, int func1,
276  int state0, int state1, int thread0, int thread1,
277  int datum0, int datum1)
278 {
279  sim_func_t *func;
280 
281  func = sim_decode(func0, func1);
282  func(sim_decode(state0, state1), sim_decode(thread0, thread1),
283  sim_decode(datum0, datum1));
284 }
285 
293 M0_INTERNAL void sim_thread_init(struct sim *state, struct sim_thread *thread,
294  unsigned stacksize, sim_func_t func, void *arg)
295 {
296  int rc;
297 
298  if (stacksize == 0)
299  /*
300  * In the kernel land people coded distrbuted file system
301  * servers with a 4K stack.
302  *
303  * Unfortunately, in user land, glibc vfprintf alone eats 1.3KB
304  * of stack space.
305  */
306  stacksize = 4 * 1024 * sizeof(int);
307  if (stacksize < MINSIGSTKSZ)
308  stacksize = MINSIGSTKSZ;
309 
310  rc = getcontext(&thread->st_ctx);
311  if (rc != 0)
312  err(EX_UNAVAILABLE, "getcontext");
313  thread->st_sim = state;
314  thread->st_ctx.uc_link = &sim_idle_ctx;
315  thread->st_ctx.uc_stack.ss_sp = thread->st_stack = valloc(stacksize);
316  thread->st_ctx.uc_stack.ss_size = thread->st_size = stacksize;
317  thread->st_ctx.uc_stack.ss_flags = 0;
318  sim_thr_tlink_init(thread);
319  if (thread->st_stack == NULL)
320  err(EX_TEMPFAIL, "malloc(%d) of a stack", stacksize);
321  makecontext(&thread->st_ctx, (void (*)())sim_trampoline,
322  8,
323  sim_encode0(func), sim_encode1(func),
324  sim_encode0(state), sim_encode1(state),
326  sim_encode0(arg), sim_encode1(arg));
328 }
329 
333 M0_INTERNAL void sim_thread_fini(struct sim_thread *thread)
334 {
336  M0_PRE(!ca_tlink_is_in(&thread->st_wake));
337  sim_thr_tlink_fini(thread);
338  if (thread->st_stack != NULL)
339  sim_free(thread->st_stack);
340 }
341 
346 M0_INTERNAL void sim_thread_exit(struct sim_thread *thread)
347 {
349 }
350 
354 static int sim_wakeup(struct sim_callout *call)
355 {
357  return 0;
358 }
359 
363 static void sim_wakeup_post(struct sim *sim,
364  struct sim_thread *thread, sim_time_t nap)
365 {
366  sim_timer_init(sim, &thread->st_wake, nap, sim_wakeup, thread);
367 }
368 
372 M0_INTERNAL void sim_sleep(struct sim_thread *thread, sim_time_t nap)
373 {
375  sim_wakeup_post(thread->st_sim, thread, nap);
377 }
378 
385 M0_INTERNAL void sim_chan_init(struct sim_chan *chan, char *format, ...)
386 {
387  sim_thr_tlist_init(&chan->ch_threads);
388  cnt_init(&chan->ch_cnt_sleep, NULL, "chan#%p", chan);
389  if (format != NULL) {
390  va_list varg;
391  va_start(varg, format);
392  sim_name_vaset(&chan->ch_cnt_sleep.c_name, format, varg);
393  va_end(varg);
394  }
395 }
396 
400 M0_INTERNAL void sim_chan_fini(struct sim_chan *chan)
401 {
402  sim_thr_tlist_fini(&chan->ch_threads);
403  cnt_fini(&chan->ch_cnt_sleep);
404 }
405 
411 M0_INTERNAL void sim_chan_wait(struct sim_chan *chan, struct sim_thread *thread)
412 {
414  sim_thr_tlist_add_tail(&chan->ch_threads, thread);
415  /*
416  * The simplest way to measure the time threads are blocked on a channel
417  * is to modify sim_chan::ch_cmt_sleep in this function, right after
418  * return from sim_thread_suspend(). Unfortunately, chan might be
419  * deallocated by that time.
420  *
421  * Instead, the time is stored in sim_thread::st_blocked, and
422  * sim_chan::ch_cnt_sleep is updated by sim_chan_wake_head().
423  */
424  thread->st_blocked = thread->st_sim->ss_bolt;
426  /*
427  * No access to chan after this point.
428  */
429 }
430 
435 static void sim_chan_wake_head(struct sim_chan *chan)
436 {
437  struct sim_thread *t;
438 
439  t = sim_thr_tlist_pop(&chan->ch_threads);
440  cnt_mod(&chan->ch_cnt_sleep, t->st_sim->ss_bolt - t->st_blocked);
441  sim_wakeup_post(t->st_sim, t, 0);
442 }
443 
447 M0_INTERNAL void sim_chan_signal(struct sim_chan *chan)
448 {
449  if (!sim_thr_tlist_is_empty(&chan->ch_threads))
451 }
452 
456 M0_INTERNAL void sim_chan_broadcast(struct sim_chan *chan)
457 {
458  while (!sim_thr_tlist_is_empty(&chan->ch_threads))
460 }
461 
465 M0_INTERNAL struct sim_thread *sim_thread_current(void)
466 {
467  return sim_current;
468 }
469 
471 M0_INTERNAL unsigned long long sim_rnd(unsigned long long a,
472  unsigned long long b)
473 {
474  /*
475  * PRNG is a time critical piece of DES. Use very simple and fast linear
476  * congruential generator for now.
477  */
478 
479  unsigned long long scaled;
480 #if 0
481 
482  M0_ASSERT(a <= b);
483 
484  scaled = ((random()*(b - a + 1)) >> 31) + a;
485 #else /* glibc */
486  static unsigned long seed = 1;
487 
488  seed = (1103515245 * seed + 12345) & 0xffffffff;
489 
490  scaled = ((seed * (b - a + 1)) >> 32) + a;
491 #endif
492  M0_ASSERT(a <= scaled && scaled <= b);
493  return scaled;
494 }
495 
500 M0_INTERNAL void sim_name_set(char **name, const char *format, ...)
501 {
502  va_list valist;
503 
504  va_start(valist, format);
505  sim_name_vaset(name, format, valist);
506  va_end(valist);
507 }
508 
513 M0_INTERNAL void sim_name_vaset(char **name, const char *format, va_list valist)
514 {
515  if (*name != NULL)
516  free(*name);
517 
518  if (vasprintf(name, format, valist) == -1)
519  err(EX_SOFTWARE, "vasprintf");
520 }
521 
523 
527 M0_INTERNAL void sim_log(struct sim *s, enum sim_log_level level,
528  const char *format, ...)
529 {
530  if (level <= sim_log_level) {
531  va_list valist;
532 
533  va_start(valist, format);
534  if (s != NULL)
535  printf("[%15.9f] ", s->ss_bolt / 1000000000.);
536  else
537  printf("[---------------] ");
538  vprintf(format, valist);
539  va_end(valist);
540  }
541 }
542 
543 M0_INTERNAL int sim_global_init(void)
544 {
545  cnt_global_init();
546  return 0;
547 }
548 
549 M0_INTERNAL void sim_global_fini(void)
550 {
551  cnt_global_fini();
552 }
553 
556 /*
557  * Local variables:
558  * c-indentation-style: "K&R"
559  * c-basic-offset: 8
560  * tab-width: 8
561  * fill-column: 80
562  * scroll-step: 1
563  * End:
564  */
static void sim_thread_fix(struct sim_thread *thread)
Definition: sim.c:212
int sim_call_t(struct sim_callout *)
Definition: sim.h:114
static void ptr(struct m0_addb2__context *ctx, const uint64_t *v, char *buf)
Definition: dump.c:440
static struct m0_addb2_philter p
Definition: consumer.c:40
static struct sim_thread * sim_current
Definition: sim.c:71
M0_INTERNAL void sim_thread_init(struct sim *state, struct sim_thread *thread, unsigned stacksize, sim_func_t func, void *arg)
Definition: sim.c:293
#define M0_PRE(cond)
Definition: sim.h:152
M0_INTERNAL void sim_init(struct sim *state)
Definition: sim.c:106
#define NULL
Definition: misc.h:38
M0_INTERNAL void sim_chan_fini(struct sim_chan *chan)
Definition: sim.c:400
M0_INTERNAL void sim_free(void *ptr)
Definition: sim.c:98
M0_INTERNAL struct sim_thread * sim_thread_current(void)
Definition: sim.c:465
enum m0_trace_level level
Definition: trace.c:111
void sim_func_t(struct sim *, struct sim_thread *, void *)
Definition: sim.h:115
M0_INTERNAL void sim_log(struct sim *s, enum sim_log_level level, const char *format,...)
Definition: sim.c:527
static int sim_encode1(void *p)
Definition: sim.c:270
M0_INTERNAL void sim_chan_broadcast(struct sim_chan *chan)
Definition: sim.c:456
static int sim_wakeup(struct sim_callout *call)
Definition: sim.c:354
M0_INTERNAL void cnt_init(struct cnt *cnt, struct cnt *parent, const char *format,...)
Definition: cnt.c:44
M0_INTERNAL void cnt_global_fini(void)
Definition: cnt.c:110
M0_INTERNAL void sim_chan_init(struct sim_chan *chan, char *format,...)
Definition: sim.c:385
M0_INTERNAL void sim_thread_exit(struct sim_thread *thread)
Definition: sim.c:346
static void sim_wakeup_post(struct sim *sim, struct sim_thread *thread, sim_time_t nap)
Definition: sim.c:363
static void sim_thread_suspend(struct sim_thread *thread)
Definition: sim.c:235
M0_INTERNAL void sim_run(struct sim *state)
Definition: sim.c:129
M0_INTERNAL void sim_global_fini(void)
Definition: sim.c:549
M0_INTERNAL void cnt_mod(struct cnt *cnt, cnt_t val)
Definition: cnt.c:92
M0_INTERNAL void sim_thread_fini(struct sim_thread *thread)
Definition: sim.c:333
const char * name
Definition: trace.c:110
M0_TL_DEFINE(ca, static, struct sim_callout)
#define M0_ASSERT(cond)
struct m0_thread thread
Definition: note.c:104
static struct m0_thread t[8]
Definition: service_ut.c:1230
M0_INTERNAL void sim_timer_add(struct sim *state, sim_time_t delta, sim_call_t *cfunc, void *datum)
Definition: sim.c:189
char * fmt(const char *format,...) __attribute__((format(printf
M0_INTERNAL void sim_sleep(struct sim_thread *thread, sim_time_t nap)
Definition: sim.c:372
sim_time_t ss_bolt
Definition: sim.h:158
M0_INTERNAL void sim_chan_wait(struct sim_chan *chan, struct sim_thread *thread)
Definition: sim.c:411
unsigned long long sim_time_t
Definition: sim.h:111
static void sim_thread_resume(struct sim_thread *thread)
Definition: sim.c:220
M0_INTERNAL void sim_chan_signal(struct sim_chan *chan)
Definition: sim.c:447
M0_INTERNAL unsigned long long sim_rnd(unsigned long long a, unsigned long long b)
Definition: sim.c:471
Definition: sim.h:237
M0_INTERNAL void * sim_alloc(size_t size)
Definition: sim.c:85
M0_TL_DESCR_DEFINE(ca, "call-outs", static, struct sim_callout, sc_linkage, sc_magic, M0_DESIM_SIM_CALLOUT_MAGIC, M0_DESIM_SIM_CALLOUT_HEAD_MAGIC)
sim_call_t * sc_call
Definition: sim.h:134
static int sim_encode0(void *p)
Definition: sim.c:265
format
Definition: hist.py:128
M0_INTERNAL void sim_name_vaset(char **name, const char *format, va_list valist)
Definition: sim.c:513
void * sc_datum
Definition: sim.h:138
struct sim * sc_sim
Definition: sim.h:142
int vasprintf(char **strp, const char *fmt, va_list ap)
uint64_t n
Definition: fops.h:107
sim_time_t sc_time
Definition: sim.h:128
Definition: sim.h:288
static struct m0_chan chan[RDWR_REQUEST_MAX]
M0_INTERNAL void cnt_global_init(void)
Definition: cnt.c:105
m0_bcount_t size
Definition: di.c:39
static void sim_trampoline(int func0, int func1, int state0, int state1, int thread0, int thread1, int datum0, int datum1)
Definition: sim.c:275
M0_INTERNAL void sim_timer_rearm(struct sim_callout *call, sim_time_t delta, sim_call_t *cfunc, void *datum)
Definition: sim.c:202
sim_log_level
Definition: sim.h:284
static uint64_t found
Definition: base.c:376
Definition: sim.h:286
static void sim_call_place(struct sim *sim, struct sim_callout *call)
Definition: sim.c:150
static void sim_timer_init(struct sim *state, struct sim_callout *call, sim_time_t delta, sim_call_t *cfunc, void *datum)
Definition: sim.c:172
M0_INTERNAL void sim_name_set(char **name, const char *format,...)
Definition: sim.c:500
static void sim_chan_wake_head(struct sim_chan *chan)
Definition: sim.c:435
M0_INTERNAL void sim_fini(struct sim *state)
Definition: sim.c:116
static int scan(struct scanner *s)
Definition: beck.c:963
#define m0_tl_find(name, var, head,...)
Definition: tlist.h:757
static struct m0_addb2_source * s
Definition: consumer.c:39
static ucontext_t sim_idle_ctx
Definition: sim.c:80
struct m0_tl ss_future
Definition: sim.h:165
int32_t rc
Definition: trigger_fop.h:47
static void * sim_decode(int p0, int p1)
Definition: sim.c:260
M0_INTERNAL int sim_global_init(void)
Definition: sim.c:543
M0_INTERNAL void cnt_fini(struct cnt *cnt)
Definition: cnt.c:83