Motr  M0
st_assert.c
Go to the documentation of this file.
1 /* -*- C -*- */
2 /*
3  * Copyright (c) 2020 Seagate Technology LLC and/or its Affiliates
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * For any questions about this software or licensing,
18  * please email opensource@seagate.com or cortx-questions@seagate.com.
19  *
20  */
21 
22 #ifndef __KERNEL__
23 #include <unistd.h>
24 #include <errno.h>
25 #endif
26 
27 #include "motr/client.h"
28 #include "motr/st/st.h"
29 #include "motr/st/st_assert.h"
30 #include "motr/st/st_misc.h"
31 
32 /*XXX juan: add doxygen to static functions */
33 
34 /*
35  * Why a cleaner instead of a similar mechanism to cunit using setjmp/longjmp?
36  * (1) Kernel doesn't support setjmp/longjmp
37  * (2) It is preferred that a test developer focuses more on the logic (as
38  * a Client application developper) instead of worrying about how to clean
39  * the mess when an assert fails(and have to know things in Client and motr).
40  * (3) In previous assert implementation, Tests share almost the same cleanup
41  * steps.
42  *
43  * A simple cleaner is triggered at the end of each test to clean up
44  * things like unfinished operations, unfreed memory etc and recover
45  * the test to where it starts when a ST_ASSERT fails.
46  * (1) We take advantage of the fact that the things required to be cleaned
47  * are limited in a Client test.
48  * - Pending Client operations ()
49  * - Client entities
50  * - Allocated memory
51  * (2) To remember what to clean, a test is required to:
52  * - use client api wrapper (xxx) instead of m0_xxx
53  * - use mem_alloc/free instead of M0_XXX
54  * In this way, those resources have a chance to be registered
55  * (3) We take advantage of the fact that only one test is executed at any
56  * point of time in a worker thread to remember those resources needed to
57  * be cleaned.
58  * (4) test_func --> aux_func --> assert: make sure to call ST_ASSERT
59  * right after aux_func in test_func so that the test terminates if
60  * any assert in aux_func fails and returns to run_test (TODO).
61  */
62 
64 
65 /*
66  * Resources that are used by a test (thread-wise)
67  * To keep it simple arraies are used as the number of resources used
68  * in a test normally is limited.
69  */
71  /* Client ops*/
72  int sg_op_cnt;
73  struct m0_op **sg_ops;
74 
75  /* Motr*/
78 
79  /* Allocated memory address */
81  void **sg_ptrs;
82 };
83 
85 
86 enum {
87  ST_MAX_OP_NUM = 4096,
90 };
91 
92 /* ST_ASSERT|_FATAL Implementation */
93 bool st_assertimpl(bool c, const char *str_c, const char *file,
94  int lno, const char *func)
95 {
96  static char buf[1024];
97  int idx;
98  struct st_worker_stat *st;
99 
100  idx = st_get_worker_idx(get_tid());
101  st = st_get_worker_stat(idx);
102  if (st == NULL)
103  goto EXIT;
104  st->sws_nr_asserts++;
105 
106  if (!c) {
107  snprintf(buf, sizeof buf,
108  "Client ST assertion failed "
109  "[%s: line %d] %s on: %s\n", file, lno, func, str_c);
110  console_printf("%s", buf);
111 
112  st->sws_nr_failed_asserts++;
113  }
114 
115 EXIT:
116  return c;
117 }
118 
119 
120 static inline struct st_cleaning_bin *get_bin(void)
121 {
122  int widx;
123 
124  widx = st_get_worker_idx(get_tid());
125  if (widx < 0)
126  return NULL;
127 
128  return &worker_bins[widx];
129 }
130 
131 void st_mark_op(struct m0_op *op)
132 {
133  int i;
134  struct st_cleaning_bin *bin;
135 
136  bin = get_bin();
137  if (bin == NULL || bin->sg_ops == NULL
138  || bin->sg_op_cnt >= ST_MAX_OP_NUM)
139  return;
140 
141  if (bin->sg_ops == NULL
142  || bin->sg_op_cnt >= ST_MAX_OP_NUM)
143  return;
144 
145  for (i = 0; i < bin->sg_op_cnt; i++) {
146  if (bin->sg_ops[i] == op)
147  break;
148  }
149 
150  if (i == bin->sg_op_cnt) {
151  bin->sg_ops[i] = op;
152  bin->sg_op_cnt++;
153  }
154 }
155 
156 void st_unmark_op(struct m0_op *op)
157 
158 {
159  int i;
160  struct st_cleaning_bin *bin;
161 
162  bin = get_bin();
163  if (bin == NULL || bin->sg_ops == NULL
164  || bin->sg_op_cnt >= ST_MAX_OP_NUM)
165  return;
166 
167  for (i = 0; i < bin->sg_op_cnt; i++) {
168  if (bin->sg_ops[i] == op)
169  break;
170  }
171 
172  /* to keep thing simple, we don't decrease the op_cnt*/
173  if (i < bin->sg_op_cnt)
174  bin->sg_ops[i] = NULL;
175 }
176 
177 void st_mark_entity(struct m0_entity *entity)
178 {
179  int i;
180  struct st_cleaning_bin *bin;
181 
182  bin = get_bin();
183  if (bin == NULL || bin->sg_entities == NULL
184  || bin->sg_entity_cnt >= ST_MAX_ENTITY_NUM)
185  return;
186 
187  for (i = 0; i < bin->sg_entity_cnt; i++) {
188  if (bin->sg_entities[i] == entity)
189  break;
190  }
191 
192  if (i == bin->sg_entity_cnt) {
193  bin->sg_entities[i] = entity;
194  bin->sg_entity_cnt++;
195  }
196 }
197 
198 void st_unmark_entity(struct m0_entity *entity)
199 {
200  int i;
201  struct st_cleaning_bin *bin;
202 
203  bin = get_bin();
204  if (bin == NULL || bin->sg_entities == NULL
205  || bin->sg_entity_cnt >= ST_MAX_ENTITY_NUM)
206  return;
207 
208  for (i = 0; i < bin->sg_entity_cnt; i++) {
209  if (bin->sg_entities[i] == entity)
210  break;
211  }
212 
213  /* to keep thing simple, we don't decrease the cnt*/
214  if (i < bin->sg_entity_cnt)
215  bin->sg_entities[i] = NULL;
216 }
217 
218 void st_mark_ptr(void *ptr)
219 {
220  int i;
221  struct st_cleaning_bin *bin;
222 
223  bin = get_bin();
224  if (bin == NULL || bin->sg_ptrs == NULL
225  || bin->sg_ptr_cnt >= ST_MAX_PTR_NUM)
226  return;
227 
228  for (i = 0; i < bin->sg_ptr_cnt; i++) {
229  if (bin->sg_ptrs[i] == ptr)
230  break;
231  }
232 
233  if (i == bin->sg_ptr_cnt) {
234  bin->sg_ptrs[i] = ptr;
235  bin->sg_ptr_cnt++;
236  }
237 }
238 
239 void st_unmark_ptr(void *ptr)
240 {
241  int i;
242  struct st_cleaning_bin *bin;
243 
244  bin = get_bin();
245  if (bin == NULL || bin->sg_ptrs == NULL
246  || bin->sg_ptr_cnt >= ST_MAX_PTR_NUM)
247  return;
248 
249  for (i = 0; i < bin->sg_ptr_cnt; i++) {
250  if (bin->sg_ptrs[i] == ptr)
251  break;
252  }
253 
254  /* to keep thing simple, we don't decrease the cnt*/
255  if (i < bin->sg_ptr_cnt)
256  bin->sg_ptrs[i] = NULL;
257 }
258 
260 {
261  int i;
262  int nr_workers;
263  struct st_cleaning_bin *bin;
264 
265  nr_workers = ST_MAX_WORKER_NUM;
266 
267  for (i = 0; i < nr_workers; i++) {
268  st_cleaner_up_flags[i] = false;
269 
270  /* allocate memory */
271  bin = &worker_bins[i];
272 
273  bin->sg_op_cnt = 0;
275  if (bin->sg_ops == NULL)
276  goto ERR_EXIT;
277 
278  bin->sg_entity_cnt = 0;
280  if (bin->sg_entities == NULL)
281  goto ERR_EXIT;
282 
283  bin->sg_ptr_cnt = 0;
285  if (bin->sg_ptrs == NULL){
286  goto ERR_EXIT;
287  }
288  }
289 
290  return 0;
291 
292 ERR_EXIT:
293  console_printf("No memory for cleaner\n");
294  for (i = 0; i < nr_workers; i++) {
295  bin = &worker_bins[i];
296  if (bin->sg_ops)
297  mem_free(bin->sg_ops);
298  if (bin->sg_entities)
299  mem_free(bin->sg_entities);
300  if (bin->sg_ptrs)
301  mem_free(bin->sg_ptrs);
302  }
303 
304  return -ENOMEM;
305 }
306 
308 {
309  int i;
310  int nr_workers;
311  struct st_cleaning_bin *bin;
312 
313  nr_workers = ST_MAX_WORKER_NUM;
314  for (i = 0; i < nr_workers; i++) {
315  st_cleaner_up_flags[i] = false;
316  bin = &worker_bins[i];
317  if (bin->sg_ops)
318  mem_free(bin->sg_ops);
319  if (bin->sg_entities)
320  mem_free(bin->sg_entities);
321  if (bin->sg_ptrs)
322  mem_free(bin->sg_ptrs);
323  }
324 }
325 
327 {
328  int widx;
329 
330  widx = st_get_worker_idx(get_tid());
331  if (widx < 0)
332  return false;
333 
334  return st_cleaner_up_flags[widx];
335 }
336 
338 {
339  int widx;
340 
341  widx = st_get_worker_idx(get_tid());
342  if (widx < 0)
343  return;
344 
345  st_cleaner_up_flags[widx] = true;
346 }
347 
349 {
350  int widx;
351 
352  widx = st_get_worker_idx(get_tid());
353  if (widx < 0)
354  return;
355 
356  st_cleaner_up_flags[widx] = false;
357 }
358 
359 static void release_op(struct m0_op *op)
360 {
361  int max_try;
362  int try_cnt;
363  m0_time_t sleep_time;
364 
365  /*
366  * There is a risk here to wait for state change for
367  * long long time(may be for ever?). op_cancel is needed
368  *
369  * Sining: [comments on op_wait]
370  * 1. op_wait requires APP to specify the states it is waiting for.
371  * This means that Client has to expose states to an
372  * APP via internal m0_sm data structure(op_sm). Good or bad?
373  * 2. Is there any case an APP waiting for a state other than
374  * OS_FAILED or OS_STABLE? I don't see any case at this moment.
375  * so why not remove the states arguments?
376  * 3. Knowing the intermitte states like LAUNCHED and INITIALISED by
377  * an APP, good or bad?
378  * 4. Timeout handling. What is the best practice for an APP handling
379  * timeout? Ideaop_cancel is a must-have!!!
380  * Right now client doesn't have op_cancel. Can't call op_fini and
381  * op_free as they have asserts on op state, and if we call op_free
382  * and launched op's reply comes back later, this will cause problem
383  * as op has been freed!!
384  *
385  */
386  try_cnt = 0;
387  max_try = 120;
388  while (op->op_sm.sm_state == M0_OS_LAUNCHED) {
389  m0_op_wait(op,
391  M0_OS_STABLE),
392  m0_time_from_now(5,0));
393 
394  try_cnt++;
395  if (try_cnt == max_try) {
396  console_printf("ST WARNING: "
397  "It takes too long waiting for OP "
398  "to complete!!\n");
399  console_printf("ST_WARNING: "
400  "Kill me if you can't stand any longer\n");
401  sleep_time = m0_time(2, 0);
402  m0_nanosleep(sleep_time, NULL);
403  try_cnt = 0;
404  }
405  }
406 
407  if (M0_IN(op->op_sm.sm_state,
409  M0_OS_FAILED,
410  M0_OS_STABLE)))
411  {
412  m0_op_fini(op);
413  }
414 
415  m0_op_free(op);
416 }
417 
419 {
420  int i;
421  struct st_cleaning_bin *bin;
422 
423  bin = get_bin();
424  if (bin == NULL)
425  return;
426 
427  for (i = 0; i < bin->sg_op_cnt; i++) {
428  if (bin->sg_ops[i] != NULL) {
429  release_op(bin->sg_ops[i]);
430  bin->sg_ops[i] = NULL;
431  }
432  }
433  bin->sg_op_cnt = 0;
434 
435  for (i = 0; i < bin->sg_entity_cnt; i++) {
436  if (bin->sg_entities[i] != NULL) {
437  if(M0_IN(bin->sg_entities[i]->en_sm.sm_state,
438  (M0_ES_INIT, M0_ES_FAILED))) {
439  break;
440  }
442  bin->sg_entities[i] = NULL;
443  }
444  }
445  bin->sg_entity_cnt = 0;
446 
447  for (i = 0; i < bin->sg_ptr_cnt; i++) {
448  if (bin->sg_ptrs[i] != NULL) {
449  mem_free(bin->sg_ptrs[i]);
450  bin->sg_ptrs[i] = NULL;
451  }
452  }
453  bin->sg_ptr_cnt = 0;
454 }
455 
456 /*
457  * Local variables:
458  * c-indentation-style: "K&R"
459  * c-basic-offset: 8
460  * tab-width: 8
461  * fill-column: 80
462  * scroll-step: 1
463  * End:
464  */
static struct st_cleaning_bin * get_bin(void)
Definition: st_assert.c:120
static struct st_cleaning_bin worker_bins[ST_MAX_WORKER_NUM]
Definition: st_assert.c:84
static void ptr(struct m0_addb2__context *ctx, const uint64_t *v, char *buf)
Definition: dump.c:440
struct st_worker_stat * st_get_worker_stat(int idx)
Definition: st.c:67
void st_cleaner_disable()
Definition: st_assert.c:348
void m0_entity_fini(struct m0_entity *entity)
Definition: client.c:438
#define NULL
Definition: misc.h:38
void m0_op_fini(struct m0_op *op)
Definition: client.c:847
struct m0_file file
Definition: di.c:36
uint64_t m0_time_t
Definition: time.h:37
void st_unmark_op(struct m0_op *op)
Definition: st_assert.c:156
struct m0_entity ** sg_entities
Definition: st_assert.c:77
void st_cleaner_empty_bin()
Definition: st_assert.c:418
#define M0_BITS(...)
Definition: misc.h:236
m0_time_t m0_time(uint64_t secs, long ns)
Definition: time.c:41
Definition: sock.c:887
int32_t m0_op_wait(struct m0_op *op, uint64_t bits, m0_time_t to)
Definition: client.c:739
op
Definition: libdemo.c:64
int i
Definition: dir.c:1033
static bool st_cleaner_up_flags[ST_MAX_WORKER_NUM]
Definition: st_assert.c:63
Definition: client.h:641
int64_t sws_nr_failed_asserts
Definition: st.h:148
void st_mark_op(struct m0_op *op)
Definition: st_assert.c:131
static struct m0_addb2_callback c
Definition: consumer.c:41
void st_cleaner_fini()
Definition: st_assert.c:307
static void release_op(struct m0_op *op)
Definition: st_assert.c:359
void st_mark_ptr(void *ptr)
Definition: st_assert.c:218
void st_unmark_entity(struct m0_entity *entity)
Definition: st_assert.c:198
void console_printf(const char *fmt,...)
Definition: st_misc.c:155
static void mem_free(const struct m0_be_btree *btree, struct m0_be_tx *tx, void *ptr)
Definition: btree.c:102
int st_cleaner_init()
Definition: st_assert.c:259
static int lno
void st_unmark_ptr(void *ptr)
Definition: st_assert.c:239
#define MEM_ALLOC_ARR(arr, nr)
Definition: st_misc.h:80
m0_time_t m0_time_from_now(uint64_t secs, long ns)
Definition: time.c:96
bool st_is_cleaner_up()
Definition: st_assert.c:326
int64_t sws_nr_asserts
Definition: st.h:147
pid_t get_tid(void)
Definition: st_misc.c:150
int st_get_worker_idx(pid_t tid)
Definition: st.c:119
void st_mark_entity(struct m0_entity *entity)
Definition: st_assert.c:177
void ** sg_ptrs
Definition: st_assert.c:81
void m0_op_free(struct m0_op *op)
Definition: client.c:885
struct m0_sm en_sm
Definition: client.h:732
struct m0_op ** sg_ops
Definition: st_assert.c:73
uint32_t sm_state
Definition: sm.h:307
bool st_assertimpl(bool c, const char *str_c, const char *file, int lno, const char *func)
Definition: st_assert.c:93
void st_cleaner_enable()
Definition: st_assert.c:337
int m0_nanosleep(const m0_time_t req, m0_time_t *rem)
Definition: ktime.c:73