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
, 0);
70 if (tevent_req_nomem(subreq
, req
)) {
71 return tevent_req_post(req
, ev
);
73 tevent_req_set_callback(subreq
, wait_for_one_notify_opened
, req
);
77 static void wait_for_one_notify_opened(struct tevent_req
*subreq
)
79 struct tevent_req
*req
= tevent_req_callback_data(
80 subreq
, struct tevent_req
);
81 struct wait_for_one_notify_state
*state
= tevent_req_data(
82 req
, struct wait_for_one_notify_state
);
85 status
= cli_ntcreate_recv(subreq
, &state
->dnum
);
87 if (tevent_req_nterror(req
, status
)) {
90 subreq
= cli_notify_send(state
, state
->ev
, state
->cli
, state
->dnum
,
91 0xffff, state
->filter
, state
->recursive
);
92 if (tevent_req_nomem(subreq
, req
)) {
95 tevent_req_set_callback(subreq
, wait_for_one_notify_done
, req
);
98 * To make sure the notify received at the server, we do another no-op
101 subreq
= cli_chkpath_send(state
, state
->ev
, state
->cli
, "\\");
102 if (tevent_req_nomem(subreq
, req
)) {
105 tevent_req_set_callback(subreq
, wait_for_one_notify_chkpath_done
, req
);
108 static void wait_for_one_notify_chkpath_done(struct tevent_req
*subreq
)
110 struct tevent_req
*req
= tevent_req_callback_data(
111 subreq
, struct tevent_req
);
112 struct wait_for_one_notify_state
*state
= tevent_req_data(
113 req
, struct wait_for_one_notify_state
);
116 status
= cli_chkpath_recv(subreq
);
118 if (tevent_req_nterror(req
, status
)) {
121 *state
->num_notifies
+= 1;
124 static void wait_for_one_notify_done(struct tevent_req
*subreq
)
126 struct tevent_req
*req
= tevent_req_callback_data(
127 subreq
, struct tevent_req
);
128 struct wait_for_one_notify_state
*state
= tevent_req_data(
129 req
, struct wait_for_one_notify_state
);
130 uint32_t num_changes
;
131 struct notify_change
*changes
;
134 status
= cli_notify_recv(subreq
, state
, &num_changes
, &changes
);
136 if (tevent_req_nterror(req
, status
)) {
139 subreq
= cli_close_send(state
, state
->ev
, state
->cli
, state
->dnum
);
140 if (tevent_req_nomem(subreq
, req
)) {
143 tevent_req_set_callback(subreq
, wait_for_one_notify_closed
, req
);
146 static void wait_for_one_notify_closed(struct tevent_req
*subreq
)
148 struct tevent_req
*req
= tevent_req_callback_data(
149 subreq
, struct tevent_req
);
150 struct wait_for_one_notify_state
*state
= tevent_req_data(
151 req
, struct wait_for_one_notify_state
);
154 status
= cli_close_recv(subreq
);
156 if (tevent_req_nterror(req
, status
)) {
159 *state
->num_notifies
-= 1;
160 tevent_req_done(req
);
163 static NTSTATUS
wait_for_one_notify_recv(struct tevent_req
*req
)
165 return tevent_req_simple_recv_ntstatus(req
);
168 static void notify_bench2_done(struct tevent_req
*req
);
170 bool run_notify_bench2(int dummy
)
172 struct cli_state
*cli
;
173 struct cli_state
**clis
;
174 struct tevent_context
*ev
;
175 unsigned num_notifies
= 0;
179 if (!torture_open_connection(&cli
, 0)) {
183 printf("starting notify bench 2 test\n");
185 cli_rmdir(cli
, "\\notify.dir\\subdir");
186 cli_rmdir(cli
, "\\notify.dir");
188 status
= cli_mkdir(cli
, "\\notify.dir");
189 if (!NT_STATUS_IS_OK(status
)) {
190 printf("mkdir failed : %s\n", nt_errstr(status
));
194 clis
= talloc_array(talloc_tos(), struct cli_state
*, torture_nprocs
);
196 printf("talloc failed\n");
200 ev
= samba_tevent_context_init(talloc_tos());
202 printf("tevent_context_create failed\n");
206 for (i
=0; i
<torture_nprocs
; i
++) {
208 if (!torture_open_connection(&clis
[i
], i
)) {
212 for (j
=0; j
<torture_numops
; j
++) {
213 struct tevent_req
*req
;
214 req
= wait_for_one_notify_send(
215 talloc_tos(), ev
, clis
[i
], "\\notify.dir",
216 FILE_NOTIFY_CHANGE_ALL
, true,
219 printf("wait_for_one_notify_send failed\n");
222 tevent_req_set_callback(req
, notify_bench2_done
, NULL
);
226 while (num_notifies
< torture_nprocs
* torture_numops
) {
228 ret
= tevent_loop_once(ev
);
230 printf("tevent_loop_once failed: %s\n",
236 cli_mkdir(cli
, "\\notify.dir\\subdir");
238 while (num_notifies
> 0) {
240 ret
= tevent_loop_once(ev
);
242 printf("tevent_loop_once failed: %s\n",
251 static void notify_bench2_done(struct tevent_req
*req
)
255 status
= wait_for_one_notify_recv(req
);
257 if (!NT_STATUS_IS_OK(status
)) {
258 printf("wait_for_one_notify returned %s\n",
264 * This test creates a subdirectory. It then waits on a barrier before the
265 * notify is sent. Then it creates the notify. It then waits for another
266 * barrier, so that all of the notifies have gone through. It then creates
267 * another subdirectory, which will trigger notifications to be sent. When the
268 * notifies have been received, it waits once more before everything is
272 struct notify_bench3_state
{
273 struct tevent_context
*ev
;
274 struct cli_state
*cli
;
277 const char *subdir_path
;
278 uint16_t subdir_dnum
;
280 struct tevent_barrier
*small
;
281 struct tevent_barrier
*large
;
284 static void notify_bench3_mkdir1_done(struct tevent_req
*subreq
);
285 static void notify_bench3_before_notify(struct tevent_req
*subreq
);
286 static void notify_bench3_chkpath_done(struct tevent_req
*subreq
);
287 static void notify_bench3_before_mkdir2(struct tevent_req
*subreq
);
288 static void notify_bench3_notify_done(struct tevent_req
*subreq
);
289 static void notify_bench3_notifies_done(struct tevent_req
*subreq
);
290 static void notify_bench3_mksubdir_done(struct tevent_req
*subreq
);
291 static void notify_bench3_before_close_subdir(struct tevent_req
*subreq
);
292 static void notify_bench3_close_subdir_done(struct tevent_req
*subreq
);
293 static void notify_bench3_deleted_subdir(struct tevent_req
*subreq
);
294 static void notify_bench3_deleted_subdirs(struct tevent_req
*subreq
);
295 static void notify_bench3_del_on_close_set(struct tevent_req
*subreq
);
296 static void notify_bench3_closed(struct tevent_req
*subreq
);
298 static struct tevent_req
*notify_bench3_send(
299 TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
, struct cli_state
*cli
,
300 const char *dir
, const char *subdir_path
,
301 struct tevent_barrier
*small
, struct tevent_barrier
*large
)
303 struct tevent_req
*req
, *subreq
;
304 struct notify_bench3_state
*state
;
306 req
= tevent_req_create(mem_ctx
, &state
, struct notify_bench3_state
);
313 state
->subdir_path
= subdir_path
;
314 state
->small
= small
;
315 state
->large
= large
;
317 subreq
= cli_ntcreate_send(
318 state
, state
->ev
, state
->cli
, state
->dir
, 0,
319 MAXIMUM_ALLOWED_ACCESS
, 0,
320 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
321 FILE_OPEN_IF
, FILE_DIRECTORY_FILE
, 0);
322 if (tevent_req_nomem(subreq
, req
)) {
323 return tevent_req_post(req
, ev
);
325 tevent_req_set_callback(subreq
, notify_bench3_mkdir1_done
, req
);
329 static void notify_bench3_mkdir1_done(struct tevent_req
*subreq
)
331 struct tevent_req
*req
= tevent_req_callback_data(
332 subreq
, struct tevent_req
);
333 struct notify_bench3_state
*state
= tevent_req_data(
334 req
, struct notify_bench3_state
);
337 status
= cli_ntcreate_recv(subreq
, &state
->dnum
);
339 if (tevent_req_nterror(req
, status
)) {
342 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->small
);
343 if (tevent_req_nomem(subreq
, req
)) {
346 tevent_req_set_callback(subreq
, notify_bench3_before_notify
, req
);
349 static void notify_bench3_before_notify(struct tevent_req
*subreq
)
351 struct tevent_req
*req
= tevent_req_callback_data(
352 subreq
, struct tevent_req
);
353 struct notify_bench3_state
*state
= tevent_req_data(
354 req
, struct notify_bench3_state
);
357 ret
= tevent_barrier_wait_recv(subreq
);
360 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
363 subreq
= cli_notify_send(state
, state
->ev
, state
->cli
, state
->dnum
,
364 0xffff, FILE_NOTIFY_CHANGE_ALL
, true);
365 if (tevent_req_nomem(subreq
, req
)) {
368 tevent_req_set_callback(subreq
, notify_bench3_notify_done
, req
);
371 * To make sure the notify received at the server, we do another no-op
372 * that is replied to.
374 subreq
= cli_chkpath_send(state
, state
->ev
, state
->cli
, "\\");
375 if (tevent_req_nomem(subreq
, req
)) {
378 tevent_req_set_callback(subreq
, notify_bench3_chkpath_done
, req
);
381 static void notify_bench3_notify_done(struct tevent_req
*subreq
)
383 struct tevent_req
*req
= tevent_req_callback_data(
384 subreq
, struct tevent_req
);
385 struct notify_bench3_state
*state
= tevent_req_data(
386 req
, struct notify_bench3_state
);
387 uint32_t num_changes
;
388 struct notify_change
*changes
;
391 status
= cli_notify_recv(subreq
, state
, &num_changes
, &changes
);
393 if (tevent_req_nterror(req
, status
)) {
396 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->large
);
397 if (tevent_req_nomem(subreq
, req
)) {
400 tevent_req_set_callback(subreq
, notify_bench3_notifies_done
, req
);
403 static void notify_bench3_notifies_done(struct tevent_req
*subreq
)
405 struct tevent_req
*req
= tevent_req_callback_data(
406 subreq
, struct tevent_req
);
409 ret
= tevent_barrier_wait_recv(subreq
);
412 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
417 static void notify_bench3_chkpath_done(struct tevent_req
*subreq
)
419 struct tevent_req
*req
= tevent_req_callback_data(
420 subreq
, struct tevent_req
);
421 struct notify_bench3_state
*state
= tevent_req_data(
422 req
, struct notify_bench3_state
);
425 status
= cli_chkpath_recv(subreq
);
427 if (tevent_req_nterror(req
, status
)) {
430 if (state
->subdir_path
== NULL
) {
433 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->small
);
434 if (tevent_req_nomem(subreq
, req
)) {
437 tevent_req_set_callback(subreq
, notify_bench3_before_mkdir2
, req
);
440 static void notify_bench3_before_mkdir2(struct tevent_req
*subreq
)
442 struct tevent_req
*req
= tevent_req_callback_data(
443 subreq
, struct tevent_req
);
444 struct notify_bench3_state
*state
= tevent_req_data(
445 req
, struct notify_bench3_state
);
448 ret
= tevent_barrier_wait_recv(subreq
);
451 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
454 subreq
= cli_ntcreate_send(
455 state
, state
->ev
, state
->cli
, state
->subdir_path
, 0,
456 MAXIMUM_ALLOWED_ACCESS
, 0,
457 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
459 FILE_DIRECTORY_FILE
, 0);
460 if (tevent_req_nomem(subreq
, req
)) {
463 tevent_req_set_callback(subreq
, notify_bench3_mksubdir_done
, req
);
466 static void notify_bench3_mksubdir_done(struct tevent_req
*subreq
)
468 struct tevent_req
*req
= tevent_req_callback_data(
469 subreq
, struct tevent_req
);
470 struct notify_bench3_state
*state
= tevent_req_data(
471 req
, struct notify_bench3_state
);
474 status
= cli_ntcreate_recv(subreq
, &state
->subdir_dnum
);
476 if (tevent_req_nterror(req
, status
)) {
479 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->large
);
480 if (tevent_req_nomem(subreq
, req
)) {
483 tevent_req_set_callback(subreq
, notify_bench3_before_close_subdir
,
487 static void notify_bench3_before_close_subdir(struct tevent_req
*subreq
)
489 struct tevent_req
*req
= tevent_req_callback_data(
490 subreq
, struct tevent_req
);
491 struct notify_bench3_state
*state
= tevent_req_data(
492 req
, struct notify_bench3_state
);
495 ret
= tevent_barrier_wait_recv(subreq
);
498 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
501 subreq
= cli_close_send(state
, state
->ev
, state
->cli
,
503 if (tevent_req_nomem(subreq
, req
)) {
506 tevent_req_set_callback(subreq
, notify_bench3_close_subdir_done
, req
);
509 static void notify_bench3_close_subdir_done(struct tevent_req
*subreq
)
511 struct tevent_req
*req
= tevent_req_callback_data(
512 subreq
, struct tevent_req
);
513 struct notify_bench3_state
*state
= tevent_req_data(
514 req
, struct notify_bench3_state
);
517 status
= cli_close_recv(subreq
);
519 if (tevent_req_nterror(req
, status
)) {
522 subreq
= cli_rmdir_send(state
, state
->ev
, state
->cli
,
524 if (tevent_req_nomem(subreq
, req
)) {
527 tevent_req_set_callback(subreq
, notify_bench3_deleted_subdir
, req
);
530 static void notify_bench3_deleted_subdir(struct tevent_req
*subreq
)
532 struct tevent_req
*req
= tevent_req_callback_data(
533 subreq
, struct tevent_req
);
534 struct notify_bench3_state
*state
= tevent_req_data(
535 req
, struct notify_bench3_state
);
538 status
= cli_rmdir_recv(subreq
);
540 if (tevent_req_nterror(req
, status
)) {
543 subreq
= tevent_barrier_wait_send(state
, state
->ev
, state
->small
);
544 if (tevent_req_nomem(subreq
, req
)) {
547 tevent_req_set_callback(subreq
, notify_bench3_deleted_subdirs
, req
);
550 static void notify_bench3_deleted_subdirs(struct tevent_req
*subreq
)
552 struct tevent_req
*req
= tevent_req_callback_data(
553 subreq
, struct tevent_req
);
554 struct notify_bench3_state
*state
= tevent_req_data(
555 req
, struct notify_bench3_state
);
558 ret
= tevent_barrier_wait_recv(subreq
);
561 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
564 subreq
= cli_nt_delete_on_close_send(state
, state
->ev
, state
->cli
,
566 if (tevent_req_nomem(subreq
, req
)) {
569 tevent_req_set_callback(subreq
, notify_bench3_del_on_close_set
, req
);
572 static void notify_bench3_del_on_close_set(struct tevent_req
*subreq
)
574 struct tevent_req
*req
= tevent_req_callback_data(
575 subreq
, struct tevent_req
);
576 struct notify_bench3_state
*state
= tevent_req_data(
577 req
, struct notify_bench3_state
);
580 status
= cli_nt_delete_on_close_recv(subreq
);
582 subreq
= cli_close_send(state
, state
->ev
, state
->cli
, state
->dnum
);
583 if (tevent_req_nomem(subreq
, req
)) {
586 tevent_req_set_callback(subreq
, notify_bench3_closed
, req
);
589 static void notify_bench3_closed(struct tevent_req
*subreq
)
591 struct tevent_req
*req
= tevent_req_callback_data(
592 subreq
, struct tevent_req
);
595 status
= cli_close_recv(subreq
);
597 if (tevent_req_nterror(req
, status
)) {
600 tevent_req_done(req
);
603 static NTSTATUS
notify_bench3_recv(struct tevent_req
*req
)
605 return tevent_req_simple_recv_ntstatus(req
);
608 static void notify_bench3_done(struct tevent_req
*req
)
610 unsigned *num_done
= (unsigned *)tevent_req_callback_data_void(req
);
613 status
= notify_bench3_recv(req
);
615 if (!NT_STATUS_IS_OK(status
)) {
616 d_printf("notify_bench3 returned %s\n", nt_errstr(status
));
621 static void notify_bench3_barrier_cb(void *private_data
)
623 struct timeval
*ts
= (struct timeval
*)private_data
;
627 printf("barrier triggered: %f\n", timeval_elapsed2(ts
, &now
));
631 bool run_notify_bench3(int dummy
)
633 struct cli_state
**clis
;
634 struct tevent_context
*ev
;
635 struct tevent_barrier
*small
;
636 struct tevent_barrier
*large
;
638 unsigned num_done
= 0;
639 struct timeval ts
, now
;
641 clis
= talloc_array(talloc_tos(), struct cli_state
*, torture_nprocs
);
643 printf("talloc failed\n");
649 small
= tevent_barrier_init(
650 talloc_tos(), torture_nprocs
* torture_numops
,
651 notify_bench3_barrier_cb
, &ts
);
656 large
= tevent_barrier_init(
657 talloc_tos(), 2 * torture_nprocs
* torture_numops
,
658 notify_bench3_barrier_cb
, &ts
);
663 ev
= samba_tevent_context_init(talloc_tos());
665 printf("tevent_context_create failed\n");
669 for (i
=0; i
<torture_nprocs
; i
++) {
670 if (!torture_open_connection(&clis
[i
], i
)) {
675 for (i
=0; i
<torture_nprocs
; i
++) {
676 for (j
=0; j
<torture_numops
; j
++) {
677 int idx
= i
* torture_numops
+ j
;
678 struct tevent_req
*req
;
679 char *dirname
, *subdirname
;
681 dirname
= talloc_asprintf(
682 talloc_tos(), "\\dir%.8d", idx
);
683 if (dirname
== NULL
) {
686 subdirname
= talloc_asprintf(
687 talloc_tos(), "\\dir%.8d\\subdir",
688 (idx
+ torture_numops
+ 1) %
689 (torture_nprocs
* torture_numops
));
690 if (subdirname
== NULL
) {
694 req
= notify_bench3_send(
695 talloc_tos(), ev
, clis
[i
], dirname
,
696 subdirname
, small
, large
);
700 tevent_req_set_callback(req
, notify_bench3_done
,
705 while (num_done
< torture_nprocs
* torture_numops
) {
707 ret
= tevent_loop_once(ev
);
709 printf("tevent_loop_once failed: %s\n",
716 printf("turndow: %f\n", timeval_elapsed2(&ts
, &now
));