2 Unix SMB/CIFS implementation.
3 Scalability test for notifies
4 Copyright (C) Volker Lendecke 2012
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "torture/proto.h"
22 #include "libsmb/libsmb.h"
23 #include "lib/util/tevent_ntstatus.h"
24 #include "libcli/security/security.h"
25 #include "lib/tevent_barrier.h"
27 extern int torture_nprocs
, torture_numops
;
29 struct wait_for_one_notify_state
{
30 struct tevent_context
*ev
;
31 struct cli_state
*cli
;
35 unsigned *num_notifies
;
38 static void wait_for_one_notify_opened(struct tevent_req
*subreq
);
39 static void wait_for_one_notify_chkpath_done(struct tevent_req
*subreq
);
40 static void wait_for_one_notify_done(struct tevent_req
*subreq
);
41 static void wait_for_one_notify_closed(struct tevent_req
*subreq
);
43 static struct tevent_req
*wait_for_one_notify_send(TALLOC_CTX
*mem_ctx
,
44 struct tevent_context
*ev
,
45 struct cli_state
*cli
,
49 unsigned *num_notifies
)
51 struct tevent_req
*req
, *subreq
;
52 struct wait_for_one_notify_state
*state
;
54 req
= tevent_req_create(mem_ctx
, &state
,
55 struct wait_for_one_notify_state
);
61 state
->filter
= filter
;
62 state
->recursive
= recursive
;
63 state
->num_notifies
= num_notifies
;
65 subreq
= cli_ntcreate_send(
66 state
, state
->ev
, state
->cli
, path
, 0,
67 MAXIMUM_ALLOWED_ACCESS
,
68 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
69 FILE_OPEN
, FILE_DIRECTORY_FILE
,
70 SMB2_IMPERSONATION_IMPERSONATION
, 0);
71 if (tevent_req_nomem(subreq
, req
)) {
72 return tevent_req_post(req
, ev
);
74 tevent_req_set_callback(subreq
, wait_for_one_notify_opened
, req
);
78 static void wait_for_one_notify_opened(struct tevent_req
*subreq
)
80 struct tevent_req
*req
= tevent_req_callback_data(
81 subreq
, struct tevent_req
);
82 struct wait_for_one_notify_state
*state
= tevent_req_data(
83 req
, struct wait_for_one_notify_state
);
86 status
= cli_ntcreate_recv(subreq
, &state
->dnum
, NULL
);
88 if (tevent_req_nterror(req
, status
)) {
91 subreq
= cli_notify_send(state
, state
->ev
, state
->cli
, state
->dnum
,
92 0xffff, state
->filter
, state
->recursive
);
93 if (tevent_req_nomem(subreq
, req
)) {
96 tevent_req_set_callback(subreq
, wait_for_one_notify_done
, req
);
99 * To make sure the notify received at the server, we do another no-op
100 * that is replied to.
102 subreq
= cli_chkpath_send(state
, state
->ev
, state
->cli
, "\\");
103 if (tevent_req_nomem(subreq
, req
)) {
106 tevent_req_set_callback(subreq
, wait_for_one_notify_chkpath_done
, req
);
109 static void wait_for_one_notify_chkpath_done(struct tevent_req
*subreq
)
111 struct tevent_req
*req
= tevent_req_callback_data(
112 subreq
, struct tevent_req
);
113 struct wait_for_one_notify_state
*state
= tevent_req_data(
114 req
, struct wait_for_one_notify_state
);
117 status
= cli_chkpath_recv(subreq
);
119 if (tevent_req_nterror(req
, status
)) {
122 *state
->num_notifies
+= 1;
125 static void wait_for_one_notify_done(struct tevent_req
*subreq
)
127 struct tevent_req
*req
= tevent_req_callback_data(
128 subreq
, struct tevent_req
);
129 struct wait_for_one_notify_state
*state
= tevent_req_data(
130 req
, struct wait_for_one_notify_state
);
131 uint32_t num_changes
;
132 struct notify_change
*changes
;
135 status
= cli_notify_recv(subreq
, state
, &num_changes
, &changes
);
137 if (tevent_req_nterror(req
, status
)) {
140 subreq
= cli_close_send(state
, state
->ev
, state
->cli
, state
->dnum
);
141 if (tevent_req_nomem(subreq
, req
)) {
144 tevent_req_set_callback(subreq
, wait_for_one_notify_closed
, req
);
147 static void wait_for_one_notify_closed(struct tevent_req
*subreq
)
149 struct tevent_req
*req
= tevent_req_callback_data(
150 subreq
, struct tevent_req
);
151 struct wait_for_one_notify_state
*state
= tevent_req_data(
152 req
, struct wait_for_one_notify_state
);
155 status
= cli_close_recv(subreq
);
157 if (tevent_req_nterror(req
, status
)) {
160 *state
->num_notifies
-= 1;
161 tevent_req_done(req
);
164 static NTSTATUS
wait_for_one_notify_recv(struct tevent_req
*req
)
166 return tevent_req_simple_recv_ntstatus(req
);
169 static void notify_bench2_done(struct tevent_req
*req
);
171 bool run_notify_bench2(int dummy
)
173 struct cli_state
*cli
;
174 struct cli_state
**clis
;
175 struct tevent_context
*ev
;
176 unsigned num_notifies
= 0;
180 if (!torture_open_connection(&cli
, 0)) {
184 printf("starting notify bench 2 test\n");
186 cli_rmdir(cli
, "\\notify.dir\\subdir");
187 cli_rmdir(cli
, "\\notify.dir");
189 status
= cli_mkdir(cli
, "\\notify.dir");
190 if (!NT_STATUS_IS_OK(status
)) {
191 printf("mkdir failed : %s\n", nt_errstr(status
));
195 clis
= talloc_array(talloc_tos(), struct cli_state
*, torture_nprocs
);
197 printf("talloc failed\n");
201 ev
= samba_tevent_context_init(talloc_tos());
203 printf("tevent_context_create failed\n");
207 for (i
=0; i
<torture_nprocs
; i
++) {
209 if (!torture_open_connection(&clis
[i
], i
)) {
213 for (j
=0; j
<torture_numops
; j
++) {
214 struct tevent_req
*req
;
215 req
= wait_for_one_notify_send(
216 talloc_tos(), ev
, clis
[i
], "\\notify.dir",
217 FILE_NOTIFY_CHANGE_ALL
, true,
220 printf("wait_for_one_notify_send failed\n");
223 tevent_req_set_callback(req
, notify_bench2_done
, NULL
);
227 while (num_notifies
< (unsigned)(torture_nprocs
* torture_numops
)) {
229 ret
= tevent_loop_once(ev
);
231 printf("tevent_loop_once failed: %s\n",
237 cli_mkdir(cli
, "\\notify.dir\\subdir");
239 while (num_notifies
> 0) {
241 ret
= tevent_loop_once(ev
);
243 printf("tevent_loop_once failed: %s\n",
252 static void notify_bench2_done(struct tevent_req
*req
)
256 status
= wait_for_one_notify_recv(req
);
258 if (!NT_STATUS_IS_OK(status
)) {
259 printf("wait_for_one_notify returned %s\n",
265 * This test creates a subdirectory. It then waits on a barrier before the
266 * notify is sent. Then it creates the notify. It then waits for another
267 * barrier, so that all of the notifies have gone through. It then creates
268 * another subdirectory, which will trigger notifications to be sent. When the
269 * notifies have been received, it waits once more before everything is
273 struct notify_bench3_state
{
274 struct tevent_context
*ev
;
275 struct cli_state
*cli
;
278 const char *subdir_path
;
279 uint16_t subdir_dnum
;
281 struct tevent_barrier
*small
;
282 struct tevent_barrier
*large
;
285 static void notify_bench3_mkdir1_done(struct tevent_req
*subreq
);
286 static void notify_bench3_before_notify(struct tevent_req
*subreq
);
287 static void notify_bench3_chkpath_done(struct tevent_req
*subreq
);
288 static void notify_bench3_before_mkdir2(struct tevent_req
*subreq
);
289 static void notify_bench3_notify_done(struct tevent_req
*subreq
);
290 static void notify_bench3_notifies_done(struct tevent_req
*subreq
);
291 static void notify_bench3_mksubdir_done(struct tevent_req
*subreq
);
292 static void notify_bench3_before_close_subdir(struct tevent_req
*subreq
);
293 static void notify_bench3_close_subdir_done(struct tevent_req
*subreq
);
294 static void notify_bench3_deleted_subdir(struct tevent_req
*subreq
);
295 static void notify_bench3_deleted_subdirs(struct tevent_req
*subreq
);
296 static void notify_bench3_del_on_close_set(struct tevent_req
*subreq
);
297 static void notify_bench3_closed(struct tevent_req
*subreq
);
299 static struct tevent_req
*notify_bench3_send(
300 TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
, struct cli_state
*cli
,
301 const char *dir
, const char *subdir_path
,
302 struct tevent_barrier
*small
, struct tevent_barrier
*large
)
304 struct tevent_req
*req
, *subreq
;
305 struct notify_bench3_state
*state
;
307 req
= tevent_req_create(mem_ctx
, &state
, struct notify_bench3_state
);
314 state
->subdir_path
= subdir_path
;
315 state
->small
= small
;
316 state
->large
= large
;
318 subreq
= cli_ntcreate_send(
319 state
, state
->ev
, state
->cli
, state
->dir
, 0,
320 MAXIMUM_ALLOWED_ACCESS
, 0,
321 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
322 FILE_OPEN_IF
, FILE_DIRECTORY_FILE
,
323 SMB2_IMPERSONATION_IMPERSONATION
, 0);
324 if (tevent_req_nomem(subreq
, req
)) {
325 return tevent_req_post(req
, ev
);
327 tevent_req_set_callback(subreq
, notify_bench3_mkdir1_done
, req
);
331 static void notify_bench3_mkdir1_done(struct tevent_req
*subreq
)
333 struct tevent_req
*req
= tevent_req_callback_data(
334 subreq
, struct tevent_req
);
335 struct notify_bench3_state
*state
= tevent_req_data(
336 req
, struct notify_bench3_state
);
339 status
= cli_ntcreate_recv(subreq
, &state
->dnum
, NULL
);
341 if (tevent_req_nterror(req
, status
)) {
344 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->small
);
345 if (tevent_req_nomem(subreq
, req
)) {
348 tevent_req_set_callback(subreq
, notify_bench3_before_notify
, req
);
351 static void notify_bench3_before_notify(struct tevent_req
*subreq
)
353 struct tevent_req
*req
= tevent_req_callback_data(
354 subreq
, struct tevent_req
);
355 struct notify_bench3_state
*state
= tevent_req_data(
356 req
, struct notify_bench3_state
);
359 ret
= tevent_barrier_wait_recv(subreq
);
362 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
365 subreq
= cli_notify_send(state
, state
->ev
, state
->cli
, state
->dnum
,
366 0xffff, FILE_NOTIFY_CHANGE_ALL
, true);
367 if (tevent_req_nomem(subreq
, req
)) {
370 tevent_req_set_callback(subreq
, notify_bench3_notify_done
, req
);
373 * To make sure the notify received at the server, we do another no-op
374 * that is replied to.
376 subreq
= cli_chkpath_send(state
, state
->ev
, state
->cli
, "\\");
377 if (tevent_req_nomem(subreq
, req
)) {
380 tevent_req_set_callback(subreq
, notify_bench3_chkpath_done
, req
);
383 static void notify_bench3_notify_done(struct tevent_req
*subreq
)
385 struct tevent_req
*req
= tevent_req_callback_data(
386 subreq
, struct tevent_req
);
387 struct notify_bench3_state
*state
= tevent_req_data(
388 req
, struct notify_bench3_state
);
389 uint32_t num_changes
;
390 struct notify_change
*changes
;
393 status
= cli_notify_recv(subreq
, state
, &num_changes
, &changes
);
395 if (tevent_req_nterror(req
, status
)) {
398 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->large
);
399 if (tevent_req_nomem(subreq
, req
)) {
402 tevent_req_set_callback(subreq
, notify_bench3_notifies_done
, req
);
405 static void notify_bench3_notifies_done(struct tevent_req
*subreq
)
407 struct tevent_req
*req
= tevent_req_callback_data(
408 subreq
, struct tevent_req
);
411 ret
= tevent_barrier_wait_recv(subreq
);
414 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
419 static void notify_bench3_chkpath_done(struct tevent_req
*subreq
)
421 struct tevent_req
*req
= tevent_req_callback_data(
422 subreq
, struct tevent_req
);
423 struct notify_bench3_state
*state
= tevent_req_data(
424 req
, struct notify_bench3_state
);
427 status
= cli_chkpath_recv(subreq
);
429 if (tevent_req_nterror(req
, status
)) {
432 if (state
->subdir_path
== NULL
) {
435 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->small
);
436 if (tevent_req_nomem(subreq
, req
)) {
439 tevent_req_set_callback(subreq
, notify_bench3_before_mkdir2
, req
);
442 static void notify_bench3_before_mkdir2(struct tevent_req
*subreq
)
444 struct tevent_req
*req
= tevent_req_callback_data(
445 subreq
, struct tevent_req
);
446 struct notify_bench3_state
*state
= tevent_req_data(
447 req
, struct notify_bench3_state
);
450 ret
= tevent_barrier_wait_recv(subreq
);
453 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
456 subreq
= cli_ntcreate_send(
457 state
, state
->ev
, state
->cli
, state
->subdir_path
, 0,
458 MAXIMUM_ALLOWED_ACCESS
, 0,
459 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
462 SMB2_IMPERSONATION_IMPERSONATION
, 0);
463 if (tevent_req_nomem(subreq
, req
)) {
466 tevent_req_set_callback(subreq
, notify_bench3_mksubdir_done
, req
);
469 static void notify_bench3_mksubdir_done(struct tevent_req
*subreq
)
471 struct tevent_req
*req
= tevent_req_callback_data(
472 subreq
, struct tevent_req
);
473 struct notify_bench3_state
*state
= tevent_req_data(
474 req
, struct notify_bench3_state
);
477 status
= cli_ntcreate_recv(subreq
, &state
->subdir_dnum
, NULL
);
479 if (tevent_req_nterror(req
, status
)) {
482 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->large
);
483 if (tevent_req_nomem(subreq
, req
)) {
486 tevent_req_set_callback(subreq
, notify_bench3_before_close_subdir
,
490 static void notify_bench3_before_close_subdir(struct tevent_req
*subreq
)
492 struct tevent_req
*req
= tevent_req_callback_data(
493 subreq
, struct tevent_req
);
494 struct notify_bench3_state
*state
= tevent_req_data(
495 req
, struct notify_bench3_state
);
498 ret
= tevent_barrier_wait_recv(subreq
);
501 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
504 subreq
= cli_close_send(state
, state
->ev
, state
->cli
,
506 if (tevent_req_nomem(subreq
, req
)) {
509 tevent_req_set_callback(subreq
, notify_bench3_close_subdir_done
, req
);
512 static void notify_bench3_close_subdir_done(struct tevent_req
*subreq
)
514 struct tevent_req
*req
= tevent_req_callback_data(
515 subreq
, struct tevent_req
);
516 struct notify_bench3_state
*state
= tevent_req_data(
517 req
, struct notify_bench3_state
);
520 status
= cli_close_recv(subreq
);
522 if (tevent_req_nterror(req
, status
)) {
525 subreq
= cli_rmdir_send(state
, state
->ev
, state
->cli
,
527 if (tevent_req_nomem(subreq
, req
)) {
530 tevent_req_set_callback(subreq
, notify_bench3_deleted_subdir
, req
);
533 static void notify_bench3_deleted_subdir(struct tevent_req
*subreq
)
535 struct tevent_req
*req
= tevent_req_callback_data(
536 subreq
, struct tevent_req
);
537 struct notify_bench3_state
*state
= tevent_req_data(
538 req
, struct notify_bench3_state
);
541 status
= cli_rmdir_recv(subreq
);
543 if (tevent_req_nterror(req
, status
)) {
546 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->small
);
547 if (tevent_req_nomem(subreq
, req
)) {
550 tevent_req_set_callback(subreq
, notify_bench3_deleted_subdirs
, req
);
553 static void notify_bench3_deleted_subdirs(struct tevent_req
*subreq
)
555 struct tevent_req
*req
= tevent_req_callback_data(
556 subreq
, struct tevent_req
);
557 struct notify_bench3_state
*state
= tevent_req_data(
558 req
, struct notify_bench3_state
);
561 ret
= tevent_barrier_wait_recv(subreq
);
564 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
567 subreq
= cli_nt_delete_on_close_send(state
, state
->ev
, state
->cli
,
569 if (tevent_req_nomem(subreq
, req
)) {
572 tevent_req_set_callback(subreq
, notify_bench3_del_on_close_set
, req
);
575 static void notify_bench3_del_on_close_set(struct tevent_req
*subreq
)
577 struct tevent_req
*req
= tevent_req_callback_data(
578 subreq
, struct tevent_req
);
579 struct notify_bench3_state
*state
= tevent_req_data(
580 req
, struct notify_bench3_state
);
583 status
= cli_nt_delete_on_close_recv(subreq
);
585 if (tevent_req_nterror(req
, status
)) {
589 subreq
= cli_close_send(state
, state
->ev
, state
->cli
, state
->dnum
);
590 if (tevent_req_nomem(subreq
, req
)) {
593 tevent_req_set_callback(subreq
, notify_bench3_closed
, req
);
596 static void notify_bench3_closed(struct tevent_req
*subreq
)
598 struct tevent_req
*req
= tevent_req_callback_data(
599 subreq
, struct tevent_req
);
602 status
= cli_close_recv(subreq
);
604 if (tevent_req_nterror(req
, status
)) {
607 tevent_req_done(req
);
610 static NTSTATUS
notify_bench3_recv(struct tevent_req
*req
)
612 return tevent_req_simple_recv_ntstatus(req
);
615 static void notify_bench3_done(struct tevent_req
*req
)
617 unsigned *num_done
= (unsigned *)tevent_req_callback_data_void(req
);
620 status
= notify_bench3_recv(req
);
622 if (!NT_STATUS_IS_OK(status
)) {
623 d_printf("notify_bench3 returned %s\n", nt_errstr(status
));
628 static void notify_bench3_barrier_cb(void *private_data
)
630 struct timeval
*ts
= (struct timeval
*)private_data
;
634 printf("barrier triggered: %f\n", timeval_elapsed2(ts
, &now
));
638 bool run_notify_bench3(int dummy
)
640 struct cli_state
**clis
;
641 struct tevent_context
*ev
;
642 struct tevent_barrier
*small
;
643 struct tevent_barrier
*large
;
645 unsigned num_done
= 0;
646 struct timeval ts
, now
;
648 clis
= talloc_array(talloc_tos(), struct cli_state
*, torture_nprocs
);
650 printf("talloc failed\n");
656 small
= tevent_barrier_init(
657 talloc_tos(), torture_nprocs
* torture_numops
,
658 notify_bench3_barrier_cb
, &ts
);
663 large
= tevent_barrier_init(
664 talloc_tos(), 2 * torture_nprocs
* torture_numops
,
665 notify_bench3_barrier_cb
, &ts
);
670 ev
= samba_tevent_context_init(talloc_tos());
672 printf("tevent_context_create failed\n");
676 for (i
=0; i
<torture_nprocs
; i
++) {
677 if (!torture_open_connection(&clis
[i
], i
)) {
682 for (i
=0; i
<torture_nprocs
; i
++) {
684 for (j
=0; j
<torture_numops
; j
++) {
685 int idx
= i
* torture_numops
+ j
;
686 struct tevent_req
*req
;
687 char *dirname
, *subdirname
;
689 dirname
= talloc_asprintf(
690 talloc_tos(), "\\dir%.8d", idx
);
691 if (dirname
== NULL
) {
694 subdirname
= talloc_asprintf(
695 talloc_tos(), "\\dir%.8d\\subdir",
696 (idx
+ torture_numops
+ 1) %
697 (torture_nprocs
* torture_numops
));
698 if (subdirname
== NULL
) {
702 req
= notify_bench3_send(
703 talloc_tos(), ev
, clis
[i
], dirname
,
704 subdirname
, small
, large
);
708 tevent_req_set_callback(req
, notify_bench3_done
,
713 while (num_done
< (unsigned)(torture_nprocs
* torture_numops
)) {
715 ret
= tevent_loop_once(ev
);
717 printf("tevent_loop_once failed: %s\n",
724 printf("turndow: %f\n", timeval_elapsed2(&ts
, &now
));