WHATSNEW: Add release notes for Samba 4.5.4.
[Samba.git] / source3 / torture / test_notify.c
blobe377875ef85e79662cf4dcdc8c29e09aaa5cbdf0
1 /*
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/>.
20 #include "includes.h"
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;
32 uint16_t dnum;
33 uint32_t filter;
34 bool recursive;
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,
46 const char *path,
47 uint32_t filter,
48 bool recursive,
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);
56 if (req == NULL) {
57 return NULL;
59 state->ev = ev;
60 state->cli = cli;
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);
74 return 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);
83 NTSTATUS status;
85 status = cli_ntcreate_recv(subreq, &state->dnum, NULL);
86 TALLOC_FREE(subreq);
87 if (tevent_req_nterror(req, status)) {
88 return;
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)) {
93 return;
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
99 * that is replied to.
101 subreq = cli_chkpath_send(state, state->ev, state->cli, "\\");
102 if (tevent_req_nomem(subreq, req)) {
103 return;
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);
114 NTSTATUS status;
116 status = cli_chkpath_recv(subreq);
117 TALLOC_FREE(subreq);
118 if (tevent_req_nterror(req, status)) {
119 return;
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;
132 NTSTATUS status;
134 status = cli_notify_recv(subreq, state, &num_changes, &changes);
135 TALLOC_FREE(subreq);
136 if (tevent_req_nterror(req, status)) {
137 return;
139 subreq = cli_close_send(state, state->ev, state->cli, state->dnum);
140 if (tevent_req_nomem(subreq, req)) {
141 return;
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);
152 NTSTATUS status;
154 status = cli_close_recv(subreq);
155 TALLOC_FREE(subreq);
156 if (tevent_req_nterror(req, status)) {
157 return;
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;
176 NTSTATUS status;
177 int i;
179 if (!torture_open_connection(&cli, 0)) {
180 return false;
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));
191 return false;
194 clis = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
195 if (clis == NULL) {
196 printf("talloc failed\n");
197 return false;
200 ev = samba_tevent_context_init(talloc_tos());
201 if (ev == NULL) {
202 printf("tevent_context_create failed\n");
203 return false;
206 for (i=0; i<torture_nprocs; i++) {
207 int j;
208 if (!torture_open_connection(&clis[i], i)) {
209 return false;
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,
217 &num_notifies);
218 if (req == NULL) {
219 printf("wait_for_one_notify_send failed\n");
220 return false;
222 tevent_req_set_callback(req, notify_bench2_done, NULL);
226 while (num_notifies < torture_nprocs * torture_numops) {
227 int ret;
228 ret = tevent_loop_once(ev);
229 if (ret != 0) {
230 printf("tevent_loop_once failed: %s\n",
231 strerror(errno));
232 return false;
236 cli_mkdir(cli, "\\notify.dir\\subdir");
238 while (num_notifies > 0) {
239 int ret;
240 ret = tevent_loop_once(ev);
241 if (ret != 0) {
242 printf("tevent_loop_once failed: %s\n",
243 strerror(errno));
244 return false;
248 return true;
251 static void notify_bench2_done(struct tevent_req *req)
253 NTSTATUS status;
255 status = wait_for_one_notify_recv(req);
256 TALLOC_FREE(req);
257 if (!NT_STATUS_IS_OK(status)) {
258 printf("wait_for_one_notify returned %s\n",
259 nt_errstr(status));
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
269 * cleaned up.
272 struct notify_bench3_state {
273 struct tevent_context *ev;
274 struct cli_state *cli;
275 const char *dir;
276 uint16_t dnum;
277 const char *subdir_path;
278 uint16_t subdir_dnum;
279 int wait_timeout;
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);
307 if (req == NULL) {
308 return NULL;
310 state->ev = ev;
311 state->cli = cli;
312 state->dir = dir;
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);
326 return 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);
335 NTSTATUS status;
337 status = cli_ntcreate_recv(subreq, &state->dnum, NULL);
338 TALLOC_FREE(subreq);
339 if (tevent_req_nterror(req, status)) {
340 return;
342 subreq = tevent_barrier_wait_send(state, state->ev, state->small);
343 if (tevent_req_nomem(subreq, req)) {
344 return;
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);
355 int ret;
357 ret = tevent_barrier_wait_recv(subreq);
358 TALLOC_FREE(subreq);
359 if (ret != 0) {
360 tevent_req_nterror(req, map_nt_error_from_unix(ret));
361 return;
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)) {
366 return;
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)) {
376 return;
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;
389 NTSTATUS status;
391 status = cli_notify_recv(subreq, state, &num_changes, &changes);
392 TALLOC_FREE(subreq);
393 if (tevent_req_nterror(req, status)) {
394 return;
396 subreq = tevent_barrier_wait_send(state, state->ev, state->large);
397 if (tevent_req_nomem(subreq, req)) {
398 return;
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);
407 int ret;
409 ret = tevent_barrier_wait_recv(subreq);
410 TALLOC_FREE(subreq);
411 if (ret != 0) {
412 tevent_req_nterror(req, map_nt_error_from_unix(ret));
413 return;
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);
423 NTSTATUS status;
425 status = cli_chkpath_recv(subreq);
426 TALLOC_FREE(subreq);
427 if (tevent_req_nterror(req, status)) {
428 return;
430 if (state->subdir_path == NULL) {
431 return;
433 subreq = tevent_barrier_wait_send(state, state->ev, state->small);
434 if (tevent_req_nomem(subreq, req)) {
435 return;
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);
446 int ret;
448 ret = tevent_barrier_wait_recv(subreq);
449 TALLOC_FREE(subreq);
450 if (ret != 0) {
451 tevent_req_nterror(req, map_nt_error_from_unix(ret));
452 return;
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,
458 FILE_CREATE,
459 FILE_DIRECTORY_FILE, 0);
460 if (tevent_req_nomem(subreq, req)) {
461 return;
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);
472 NTSTATUS status;
474 status = cli_ntcreate_recv(subreq, &state->subdir_dnum, NULL);
475 TALLOC_FREE(subreq);
476 if (tevent_req_nterror(req, status)) {
477 return;
479 subreq = tevent_barrier_wait_send(state, state->ev, state->large);
480 if (tevent_req_nomem(subreq, req)) {
481 return;
483 tevent_req_set_callback(subreq, notify_bench3_before_close_subdir,
484 req);
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);
493 int ret;
495 ret = tevent_barrier_wait_recv(subreq);
496 TALLOC_FREE(subreq);
497 if (ret != 0) {
498 tevent_req_nterror(req, map_nt_error_from_unix(ret));
499 return;
501 subreq = cli_close_send(state, state->ev, state->cli,
502 state->subdir_dnum);
503 if (tevent_req_nomem(subreq, req)) {
504 return;
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);
515 NTSTATUS status;
517 status = cli_close_recv(subreq);
518 TALLOC_FREE(subreq);
519 if (tevent_req_nterror(req, status)) {
520 return;
522 subreq = cli_rmdir_send(state, state->ev, state->cli,
523 state->subdir_path);
524 if (tevent_req_nomem(subreq, req)) {
525 return;
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);
536 NTSTATUS status;
538 status = cli_rmdir_recv(subreq);
539 TALLOC_FREE(subreq);
540 if (tevent_req_nterror(req, status)) {
541 return;
543 subreq = tevent_barrier_wait_send(state, state->ev, state->small);
544 if (tevent_req_nomem(subreq, req)) {
545 return;
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);
556 int ret;
558 ret = tevent_barrier_wait_recv(subreq);
559 TALLOC_FREE(subreq);
560 if (ret != 0) {
561 tevent_req_nterror(req, map_nt_error_from_unix(ret));
562 return;
564 subreq = cli_nt_delete_on_close_send(state, state->ev, state->cli,
565 state->dnum, true);
566 if (tevent_req_nomem(subreq, req)) {
567 return;
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);
578 NTSTATUS status;
580 status = cli_nt_delete_on_close_recv(subreq);
581 TALLOC_FREE(subreq);
582 if (tevent_req_nterror(req, status)) {
583 return;
586 subreq = cli_close_send(state, state->ev, state->cli, state->dnum);
587 if (tevent_req_nomem(subreq, req)) {
588 return;
590 tevent_req_set_callback(subreq, notify_bench3_closed, req);
593 static void notify_bench3_closed(struct tevent_req *subreq)
595 struct tevent_req *req = tevent_req_callback_data(
596 subreq, struct tevent_req);
597 NTSTATUS status;
599 status = cli_close_recv(subreq);
600 TALLOC_FREE(subreq);
601 if (tevent_req_nterror(req, status)) {
602 return;
604 tevent_req_done(req);
607 static NTSTATUS notify_bench3_recv(struct tevent_req *req)
609 return tevent_req_simple_recv_ntstatus(req);
612 static void notify_bench3_done(struct tevent_req *req)
614 unsigned *num_done = (unsigned *)tevent_req_callback_data_void(req);
615 NTSTATUS status;
617 status = notify_bench3_recv(req);
618 TALLOC_FREE(req);
619 if (!NT_STATUS_IS_OK(status)) {
620 d_printf("notify_bench3 returned %s\n", nt_errstr(status));
622 *num_done += 1;
625 static void notify_bench3_barrier_cb(void *private_data)
627 struct timeval *ts = (struct timeval *)private_data;
628 struct timeval now;
630 GetTimeOfDay(&now);
631 printf("barrier triggered: %f\n", timeval_elapsed2(ts, &now));
632 GetTimeOfDay(ts);
635 bool run_notify_bench3(int dummy)
637 struct cli_state **clis;
638 struct tevent_context *ev;
639 struct tevent_barrier *small;
640 struct tevent_barrier *large;
641 unsigned i, j;
642 unsigned num_done = 0;
643 struct timeval ts, now;
645 clis = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
646 if (clis == NULL) {
647 printf("talloc failed\n");
648 return false;
651 GetTimeOfDay(&ts);
653 small = tevent_barrier_init(
654 talloc_tos(), torture_nprocs * torture_numops,
655 notify_bench3_barrier_cb, &ts);
656 if (small == NULL) {
657 return false;
660 large = tevent_barrier_init(
661 talloc_tos(), 2 * torture_nprocs * torture_numops,
662 notify_bench3_barrier_cb, &ts);
663 if (large == NULL) {
664 return false;
667 ev = samba_tevent_context_init(talloc_tos());
668 if (ev == NULL) {
669 printf("tevent_context_create failed\n");
670 return false;
673 for (i=0; i<torture_nprocs; i++) {
674 if (!torture_open_connection(&clis[i], i)) {
675 return false;
679 for (i=0; i<torture_nprocs; i++) {
680 for (j=0; j<torture_numops; j++) {
681 int idx = i * torture_numops + j;
682 struct tevent_req *req;
683 char *dirname, *subdirname;
685 dirname = talloc_asprintf(
686 talloc_tos(), "\\dir%.8d", idx);
687 if (dirname == NULL) {
688 return false;
690 subdirname = talloc_asprintf(
691 talloc_tos(), "\\dir%.8d\\subdir",
692 (idx + torture_numops + 1) %
693 (torture_nprocs * torture_numops));
694 if (subdirname == NULL) {
695 return false;
698 req = notify_bench3_send(
699 talloc_tos(), ev, clis[i], dirname,
700 subdirname, small, large);
701 if (req == NULL) {
702 return false;
704 tevent_req_set_callback(req, notify_bench3_done,
705 &num_done);
709 while (num_done < torture_nprocs * torture_numops) {
710 int ret;
711 ret = tevent_loop_once(ev);
712 if (ret != 0) {
713 printf("tevent_loop_once failed: %s\n",
714 strerror(errno));
715 return false;
719 GetTimeOfDay(&now);
720 printf("turndow: %f\n", timeval_elapsed2(&ts, &now));
721 TALLOC_FREE(small);
722 TALLOC_FREE(large);
723 return true;