Motr  M0
rdwr_test_bench.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012-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 "ut/ut.h"
23 #include "lib/misc.h"
24 #include "lib/memory.h"
25 #include "lib/mutex.h"
26 #include "reqh/reqh.h"
27 #include "fop/fom_long_lock.h"
28 
29 M0_TL_DESCR_DECLARE(m0_lll, M0_EXTERN);
30 M0_TL_DECLARE(m0_lll, M0_INTERNAL, struct m0_long_lock_link);
31 
37 };
38 
40  /* See comment on PH_REQ_LOCK value in fom_rdwr_state() function */
43 };
44 
45 struct test_min_max {
46  size_t min;
47  size_t max;
48 };
49 
50 struct test_request {
52  /* Expected count of waiters */
53  size_t tr_waiters;
60 };
61 
62 static struct m0_fom *sleeper;
63 static struct m0_chan chan[RDWR_REQUEST_MAX];
65 static struct m0_long_lock long_lock;
66 
71 static bool readers_check(struct m0_long_lock *lock)
72 {
73  struct m0_long_lock_link *head;
74  bool result;
75 
76  m0_mutex_lock(&lock->l_lock);
77 
78  head = m0_lll_tlist_head(&lock->l_waiters);
79  result = m0_tl_forall(m0_lll, l, &lock->l_owners,
80  l->lll_lock_type == M0_LONG_LOCK_READER) &&
81  ergo(head != NULL, head->lll_lock_type == M0_LONG_LOCK_WRITER);
82 
83  m0_mutex_unlock(&lock->l_lock);
84 
85  return result;
86 }
87 
92 static bool writer_check(struct m0_long_lock *lock)
93 {
94  struct m0_long_lock_link *head;
95  bool result;
96 
97  m0_mutex_lock(&lock->l_lock);
98 
99  head = m0_lll_tlist_head(&lock->l_owners);
100  result = head != NULL &&
101  head->lll_lock_type == M0_LONG_LOCK_WRITER &&
102  m0_lll_tlist_length(&lock->l_owners) == 1;
103 
104  m0_mutex_unlock(&lock->l_lock);
105 
106  return result;
107 }
108 
112 static bool lock_check(struct m0_long_lock *lock, enum tb_request_type type,
113  size_t owners_min, size_t owners_max, size_t waiters)
114 {
115  bool result;
116  size_t owners_len;
117 
118  m0_mutex_lock(&lock->l_lock);
119 
120  owners_len = m0_lll_tlist_length(&lock->l_owners);
121  result = owners_min <= owners_len && owners_len <= owners_max &&
122  m0_lll_tlist_length(&lock->l_waiters) == waiters &&
123 
124  (type == RQ_WRITE) ? lock->l_state == M0_LONG_LOCK_WR_LOCKED :
125  (type == RQ_READ) ? lock->l_state == M0_LONG_LOCK_RD_LOCKED :
126  false;
127 
128  m0_mutex_unlock(&lock->l_lock);
129 
130  return result;
131 }
132 
133 static int fom_rdwr_tick(struct m0_fom *fom)
134 {
135  struct fom_rdwr *request;
136  int rq_type;
137  int rq_seqn;
138  int result;
139 
140  request = container_of(fom, struct fom_rdwr, fr_gen);
141  M0_UT_ASSERT(request != NULL);
142  rq_type = request->fr_req->tr_type;
143  rq_seqn = request->fr_seqn;
144 
145  /*
146  * To pacify M0_PRE(M0_IN(m0_fom_phase(fom), (M0_FOPH_INIT,
147  * M0_FOPH_FAILURE))) precondition in m0_fom_queue(), special processing
148  * order of FOM phases is used.
149  *
150  * Do NOT use this code as a template for the general purpose. It's
151  * designed for tesing of m0_long_lock ONLY!
152  */
153  if (m0_fom_phase(fom) == PH_REQ_LOCK) {
154  if (rq_seqn == 0)
155  sleeper = fom;
156 
157  switch (rq_type) {
158  case RQ_READ:
159  result = M0_FOM_LONG_LOCK_RETURN(
161  &request->fr_link,
162  PH_GOT_LOCK));
163  M0_UT_ASSERT((result == M0_FSO_AGAIN)
164  == (rq_seqn == 0));
165  result = M0_FSO_WAIT;
166  break;
167  case RQ_WRITE:
168  result = M0_FOM_LONG_LOCK_RETURN(
170  &request->fr_link,
171  PH_GOT_LOCK));
172  M0_UT_ASSERT((result == M0_FSO_AGAIN)
173  == (rq_seqn == 0));
174  result = M0_FSO_WAIT;
175  break;
176  case RQ_WAKE_UP:
177  default:
180  result = M0_FSO_AGAIN;
181  }
182 
183  /* notify, fom ready */
184  m0_chan_signal_lock(&chan[rq_seqn]);
185  } else if (m0_fom_phase(fom) == PH_GOT_LOCK) {
186  M0_UT_ASSERT(ergo(M0_IN(rq_type, (RQ_READ, RQ_WRITE)),
187  lock_check(&long_lock, rq_type,
188  request->fr_req->tr_owners.min,
189  request->fr_req->tr_owners.max,
190  request->fr_req->tr_waiters)));
191 
192  switch (rq_type) {
193  case RQ_READ:
197  break;
198  case RQ_WRITE:
202  break;
203  case RQ_WAKE_UP:
204  default:
205  ;
206  }
207 
208  /* notify, fom ready */
209  m0_chan_signal_lock(&chan[rq_seqn]);
211  result = M0_FSO_WAIT;
212  } else {
213  M0_IMPOSSIBLE("");
214  result = 0;
215  }
216 
217  return result;
218 }
219 
220 static void reqh_fop_handle(struct m0_reqh *reqh, struct m0_fom *fom)
221 {
222  M0_PRE(reqh != NULL);
225  m0_fom_queue(fom);
227 }
228 
229 static void test_req_handle(struct m0_reqh *reqh,
230  struct test_request *rq, int seqn)
231 {
232  struct m0_fom *fom;
233  struct fom_rdwr *obj;
234  int rc;
235 
236  rc = rdwr_fom_create(&fom, reqh);
237  M0_UT_ASSERT(rc == 0);
238 
239  obj = container_of(fom, struct fom_rdwr, fr_gen);
240  obj->fr_req = rq;
241  obj->fr_seqn = seqn;
242 
244 }
245 
246 /* c. To make sure that the fairness queue works, lock should sequentially
247  * transit from "state to state" listed in the following structure: */
248 
249 static struct test_request test[3][RDWR_REQUEST_MAX] = {
250  [0] = {
251  {.tr_type = RQ_READ, .tr_owners = {1, 1}, .tr_waiters = 8},
252  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 7},
253  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 4},
254  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 4},
255  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 4},
256  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 3},
257  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
258  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
259  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
260 
261  {.tr_type = RQ_WAKE_UP, .tr_owners = {0, 0}, .tr_waiters = 0},
262  {.tr_type = RQ_LAST, .tr_owners = {0, 0}, .tr_waiters = 0},
263  },
264  [1] = {
265  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 9},
266  {.tr_type = RQ_READ, .tr_owners = {1, 1}, .tr_waiters = 8},
267  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 7},
268  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 4},
269  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 4},
270  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 4},
271  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 3},
272  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
273  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
274  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
275 
276  {.tr_type = RQ_WAKE_UP, .tr_owners = {0, 0}, .tr_waiters = 0},
277  {.tr_type = RQ_LAST, .tr_owners = {0, 0}, .tr_waiters = 0},
278  },
279  [2] = {
280  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 9},
281  {.tr_type = RQ_READ, .tr_owners = {1, 1}, .tr_waiters = 8},
282  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 7},
283  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 6},
284  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 5},
285  {.tr_type = RQ_READ, .tr_owners = {1, 1}, .tr_waiters = 4},
286  {.tr_type = RQ_WRITE, .tr_owners = {1, 1}, .tr_waiters = 3},
287  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
288  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
289  {.tr_type = RQ_READ, .tr_owners = {1, 3}, .tr_waiters = 0},
290 
291  {.tr_type = RQ_WAKE_UP, .tr_owners = {0, 0}, .tr_waiters = 0},
292  {.tr_type = RQ_LAST, .tr_owners = {0, 0}, .tr_waiters = 0},
293  },
294 };
295 
296 static void rdwr_send_fop(struct m0_reqh **reqh, size_t reqh_nr)
297 {
298  int i;
299  int j;
300 
301  for (j = 0; j < ARRAY_SIZE(test); ++j) {
303 
304  for (i = 0; test[j][i].tr_type != RQ_LAST; ++i) {
308 
309  /* d. Send FOMs from multiple request handlers, where
310  * they can contend for the lock. 'reqh[i % reqh_nr]'
311  * expression allows to send FOMs one by one into each
312  * request handler */
313  test_req_handle(reqh[i % reqh_nr], &test[j][i], i);
314 
315  /* Wait until the fom completes the first state
316  * transition. This is needed to achieve deterministic
317  * lock acquisition order. */
318  m0_chan_wait(&clink[i]);
319  }
320 
321  /* Wait until all queued foms are processed. */
322  for (i = 0; test[j][i].tr_type != RQ_LAST; ++i) {
323  m0_chan_wait(&clink[i]);
324  }
325 
326  /* Cleanup */
327  for (i = 0; test[j][i].tr_type != RQ_LAST; ++i) {
330  m0_clink_fini(&clink[i]);
331  }
332 
334  }
335 }
336 
337 /*
338  * Local variables:
339  * c-indentation-style: "K&R"
340  * c-basic-offset: 8
341  * tab-width: 8
342  * fill-column: 80
343  * scroll-step: 1
344  * End:
345  */
M0_INTERNAL void m0_long_lock_fini(struct m0_long_lock *lock)
static struct m0_mutex lock
Definition: transmit.c:326
M0_INTERNAL void m0_chan_wait(struct m0_clink *link)
Definition: chan.c:336
M0_INTERNAL void m0_fom_wakeup(struct m0_fom *fom)
Definition: fom.c:532
#define M0_PRE(cond)
M0_INTERNAL void m0_mutex_unlock(struct m0_mutex *mutex)
Definition: mutex.c:66
#define M0_FOM_LONG_LOCK_RETURN(rc)
#define NULL
Definition: misc.h:38
M0_INTERNAL void m0_clink_init(struct m0_clink *link, m0_chan_cb_t cb)
Definition: chan.c:201
tb_request_type
M0_INTERNAL void m0_clink_del_lock(struct m0_clink *link)
Definition: chan.c:293
#define ergo(a, b)
Definition: misc.h:293
struct m0_mutex l_lock
struct m0_long_lock_link fr_link
Definition: rdwr_fom.c:36
M0_TL_DECLARE(m0_lll, M0_INTERNAL, struct m0_long_lock_link)
M0_INTERNAL void m0_long_write_unlock(struct m0_long_lock *lock, struct m0_long_lock_link *link)
static struct m0_long_lock long_lock
#define container_of(ptr, type, member)
Definition: misc.h:33
M0_INTERNAL void m0_mutex_lock(struct m0_mutex *mutex)
Definition: mutex.c:49
size_t fr_seqn
Definition: rdwr_fom.c:40
struct m0_rwlock rh_rwlock
Definition: reqh.h:143
static struct foo * obj
Definition: tlist.c:302
m0_fom_phase
Definition: fom.h:372
M0_TL_DESCR_DECLARE(m0_lll, M0_EXTERN)
static int head(struct m0_sm *mach)
Definition: sm.c:468
M0_INTERNAL bool m0_long_write_lock(struct m0_long_lock *lk, struct m0_long_lock_link *link, int next_phase)
int i
Definition: dir.c:1033
M0_INTERNAL void m0_chan_init(struct m0_chan *chan, struct m0_mutex *ch_guard)
Definition: chan.c:96
static bool readers_check(struct m0_long_lock *lock)
static bool lock_check(struct m0_long_lock *lock, enum tb_request_type type, size_t owners_min, size_t owners_max, size_t waiters)
static bool writer_check(struct m0_long_lock *lock)
static int rdwr_fom_create(struct m0_fom **m, struct m0_reqh *reqh)
Definition: rdwr_fom.c:71
Definition: reqh.h:94
Definition: dump.c:103
Definition: chan.h:229
static struct m0_clink clink[RDWR_REQUEST_MAX]
static struct m0_clink l[NR]
Definition: chan.c:37
M0_INTERNAL void m0_chan_signal_lock(struct m0_chan *chan)
Definition: chan.c:165
M0_INTERNAL bool m0_long_is_read_locked(struct m0_long_lock *lock, const struct m0_fom *fom)
M0_INTERNAL bool m0_long_is_write_locked(struct m0_long_lock *lock, const struct m0_fom *fom)
M0_INTERNAL int m0_reqh_state_get(struct m0_reqh *reqh)
Definition: reqh.c:398
void m0_clink_add_lock(struct m0_chan *chan, struct m0_clink *link)
Definition: chan.c:255
M0_INTERNAL void m0_long_lock_init(struct m0_long_lock *lock)
Definition: fom.h:481
static int fom_rdwr_tick(struct m0_fom *fom)
struct m0_reqh reqh
Definition: rm_foms.c:48
struct test_min_max tr_owners
static void reqh_fop_handle(struct m0_reqh *reqh, struct m0_fom *fom)
static void test_req_handle(struct m0_reqh *reqh, struct test_request *rq, int seqn)
static struct m0_chan chan[RDWR_REQUEST_MAX]
tb_request_phase
static void rdwr_send_fop(struct m0_reqh **reqh, size_t reqh_nr)
Definition: list.c:42
M0_INTERNAL void m0_clink_fini(struct m0_clink *link)
Definition: chan.c:208
M0_INTERNAL void m0_rwlock_read_lock(struct m0_rwlock *lock)
Definition: rwlock.c:52
M0_INTERNAL void m0_fom_queue(struct m0_fom *fom)
Definition: fom.c:624
enum tb_request_type tr_type
M0_INTERNAL void m0_rwlock_read_unlock(struct m0_rwlock *lock)
Definition: rwlock.c:57
M0_INTERNAL void m0_long_read_unlock(struct m0_long_lock *lock, struct m0_long_lock_link *link)
struct m0_fom fr_gen
Definition: rdwr_fom.c:35
M0_INTERNAL bool m0_long_read_lock(struct m0_long_lock *lk, struct m0_long_lock_link *link, int next_phase)
void m0_fom_phase_set(struct m0_fom *fom, int phase)
Definition: fom.c:1688
int type
Definition: dir.c:1031
M0_INTERNAL void m0_chan_fini_lock(struct m0_chan *chan)
Definition: chan.c:112
int32_t rc
Definition: trigger_fop.h:47
#define ARRAY_SIZE(a)
Definition: misc.h:45
#define M0_UT_ASSERT(a)
Definition: ut.h:46
static struct m0_fom * sleeper
#define m0_tl_forall(name, var, head,...)
Definition: tlist.h:735
#define M0_IMPOSSIBLE(fmt,...)
struct test_request * fr_req
Definition: rdwr_fom.c:39