The branch, master has been updated via 5303f6f7fd1 s4:torture/smb2: add smb2.bench.read test via 56488363862 s4:torture/smb2: add --option="torture:looplimit=150000" to smb2.bench.echo via d01db89d905 s4:torture/smb2: move benchmarking tests to bench.c via e03ccb5b12b smb2_negprot: add CALLGRIND_START_INSTRUMENTATION after SMB2 negprot via 77c925681dc lib/replace: check for valgrind/callgrind.h via bfb1494e818 lib/util: use RUNNING_ON_VALGRIND to check if valgrind is used via be5e4d164df smb2_server: use MSG_DONTWAIT to get non-blocking send/recvmsg via 6e848f9d22f s3:smbd: only do profiling overhead in smbd_tevent_trace_callback() when needed via ff259bd1b70 smbprofile: add smbprofile_active() helper via a08f8b2a2cc smb2_server: optimize SMB2_OP_KEEPALIVE (SMB2 Echo) from 9aa440d52d7 s4-rpc_server: Filter via dsdb_dc_functional_level() before we are returning a lookup directly
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 5303f6f7fd1ddccdfa6b752b20f712237850527c Author: Stefan Metzmacher <me...@samba.org> Date: Thu Sep 22 15:49:41 2022 +0200 s4:torture/smb2: add smb2.bench.read test This test opens one file for each loop (for nprocs * qdepth loops) and for each file it loops in read requests for the first io_size bytes. time smbtorture //127.0.0.1/m -Uroot%test smb2.bench.read \ --option="torture:timelimit=600" \ --option="torture:nprocs=1" \ --option="torture:qdepth=4" \ --option="torture:io_size=4096" In order to generate constant load for profiles --option="torture:looplimit=150000" can be used to stop after the given number of loops before the timelimit hits. Sometimes the bottleneck is the smbtorture process. In order to bring the smbd process to 100% cpu, you can use '--option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4"' and run multiple instances of the test at the same time, which both talk to the same smbd process. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Thu Jun 1 08:14:23 UTC 2023 on atb-devel-224 commit 56488363862aeeeacbdd675c09603c5624675d2b Author: Stefan Metzmacher <me...@samba.org> Date: Fri Apr 28 08:02:39 2023 +0000 s4:torture/smb2: add --option="torture:looplimit=150000" to smb2.bench.echo Also see the commit message of 23988f19e7cc2823d6c0c0f40af0195d0a3b81bf for other examples... This test calls SMB2_Echo in a loop per connection. time smbtorture //127.0.0.1/m -Uroot%test smb2.bench.echo \ --option="torture:timelimit=600" \ --option="torture:looplimit=150000" \ --option="torture:nprocs=1" \ --option="torture:qdepth=1" This is a very useful test to show how many requests are possible at the raw SMB2 layer. In order to do profiling and being able to compare the profiles between runs, it is important to produce the exact same load in each run, which is not possible with the typical --option="torture:timelimit=600". E.g. when the server runs under 'valgrind --tool=callgrind bin/smbd' I typically run without "torture:looplimit" first in order to see, which rate is possible per second, then I'll add a "torture:looplimit" in order to run about half of the timelimit. Then the looplimit should run for some time, but finish before the timelimit. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit d01db89d905eb85b014e2d9b701a507d41cd2dff Author: Stefan Metzmacher <me...@samba.org> Date: Thu Sep 22 15:02:04 2022 +0200 s4:torture/smb2: move benchmarking tests to bench.c I'll add more tests there soon Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit e03ccb5b12bca8588baca70c4d45702833c7bdd5 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Apr 25 15:38:30 2023 +0000 smb2_negprot: add CALLGRIND_START_INSTRUMENTATION after SMB2 negprot This allows us to support starting smbd under callgrind and only start the overhead and instrumentation after the SMB2 negprot, this allows us to profile only useful stuff and not all the smbd startup, forking and multichannel handling. This will do the trick: valgrind --tool=callgrind --instr-atstart=no smbd Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 77c925681dc964adc67aa866ae47149dabd576e9 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 31 12:59:47 2023 +0200 lib/replace: check for valgrind/callgrind.h Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit bfb1494e818a8ec1743c50a938e6475771d0a52f Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 31 12:09:09 2023 +0200 lib/util: use RUNNING_ON_VALGRIND to check if valgrind is used We should not skip all of close_low_fd() just because we detected valgrind headers at build time. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit be5e4d164df6dbfca391f46ad99350e3b72c311b Author: Stefan Metzmacher <me...@samba.org> Date: Wed Sep 30 23:43:37 2020 +0200 smb2_server: use MSG_DONTWAIT to get non-blocking send/recvmsg It means we can make the fd blocking, which will help with io_uring support. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 6e848f9d22ff32788a9544d937119a987c50cb9b Author: Stefan Metzmacher <me...@samba.org> Date: Wed Apr 26 17:22:01 2023 +0200 s3:smbd: only do profiling overhead in smbd_tevent_trace_callback() when needed Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit ff259bd1b70737a5b0ff31e33db4a18837c40e89 Author: Stefan Metzmacher <me...@samba.org> Date: Wed Apr 26 17:21:38 2023 +0200 smbprofile: add smbprofile_active() helper Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit a08f8b2a2cc1ba8f7246375d2d697ce979281402 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Feb 14 13:45:26 2023 +0000 smb2_server: optimize SMB2_OP_KEEPALIVE (SMB2 Echo) This is not strictly needed, but it helps profiling the core smb2_server.c code with the 'smb2.bench.echo' test. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> ----------------------------------------------------------------------- Summary of changes: lib/replace/wscript | 2 +- lib/util/close_low_fd.c | 14 +- source3/include/smbprofile.h | 10 + source3/smbd/smb2_negprot.c | 16 + source3/smbd/smb2_process.c | 51 +- source3/smbd/smb2_server.c | 37 +- source4/torture/smb2/bench.c | 1376 ++++++++++++++++++++++++++++++++++++ source4/torture/smb2/create.c | 813 --------------------- source4/torture/smb2/wscript_build | 1 + 9 files changed, 1494 insertions(+), 826 deletions(-) create mode 100644 source4/torture/smb2/bench.c Changeset truncated at 500 lines: diff --git a/lib/replace/wscript b/lib/replace/wscript index d6625817e02..f5c597c04c2 100644 --- a/lib/replace/wscript +++ b/lib/replace/wscript @@ -114,7 +114,7 @@ def configure(conf): conf.CHECK_HEADERS('xfs/libxfs.h netgroup.h') conf.CHECK_HEADERS('valgrind.h valgrind/valgrind.h') - conf.CHECK_HEADERS('valgrind/memcheck.h valgrind/helgrind.h') + conf.CHECK_HEADERS('valgrind/memcheck.h valgrind/helgrind.h valgrind/callgrind.h') conf.CHECK_HEADERS('nss_common.h nsswitch.h ns_api.h') conf.CHECK_HEADERS('sys/extattr.h sys/ea.h sys/proplist.h sys/cdefs.h') conf.CHECK_HEADERS('utmp.h utmpx.h lastlog.h') diff --git a/lib/util/close_low_fd.c b/lib/util/close_low_fd.c index 5e749187113..84a69067644 100644 --- a/lib/util/close_low_fd.c +++ b/lib/util/close_low_fd.c @@ -21,11 +21,22 @@ #include "system/filesys.h" #include "close_low_fd.h" +#ifdef HAVE_VALGRIND_VALGRIND_H +#include <valgrind/valgrind.h> +#elif defined(HAVE_VALGRIND_H) +#include <valgrind.h> +#else +#define RUNNING_ON_VALGRIND 0 +#endif + _PUBLIC_ int close_low_fd(int fd) { -#ifndef VALGRIND int ret, dev_null; + if (RUNNING_ON_VALGRIND) { + return 0; + } + dev_null = open("/dev/null", O_RDWR, 0); if ((dev_null == -1) && (errno == ENFILE)) { @@ -60,6 +71,5 @@ _PUBLIC_ int close_low_fd(int fd) return err; } close(dev_null); -#endif return 0; } diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index ce7a7405bf4..7b080e11ee1 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -497,6 +497,11 @@ static inline void smbprofile_dump_schedule(void) smbprofile_dump_schedule_timer(); } +static inline bool smbprofile_active(void) +{ + return smbprofile_state.config.do_count; +} + static inline bool smbprofile_dump_pending(void) { if (smbprofile_state.internal.te == NULL) { @@ -574,6 +579,11 @@ static inline uint64_t profile_timestamp(void) #define PROFILE_TIMESTAMP(x) (*(x)=(struct timespec){0}) +static inline bool smbprofile_active(void) +{ + return false; +} + static inline bool smbprofile_dump_pending(void) { return false; diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c index 408faf1481d..c13f472c4e9 100644 --- a/source3/smbd/smb2_negprot.c +++ b/source3/smbd/smb2_negprot.c @@ -30,6 +30,9 @@ #include "auth/gensec/gensec.h" #include "lib/util/string_wrappers.h" #include "source3/lib/substitute.h" +#ifdef HAVE_VALGRIND_CALLGRIND_H +#include <valgrind/callgrind.h> +#endif /* HAVE_VALGRIND_CALLGRIND_H */ #undef DBGC_CLASS #define DBGC_CLASS DBGC_SMB2 @@ -914,6 +917,19 @@ static void smbd_smb2_request_process_negprot_mc_done(struct tevent_req *subreq) */ status = smbd_smb2_request_done(req, state->outbody, &state->outdyn); if (NT_STATUS_IS_OK(status)) { + /* + * This allows us to support starting smbd under + * callgrind and only start the overhead and + * instrumentation after the SMB2 negprot, + * this allows us to profile only useful + * stuff and not all the smbd startup, forking + * and multichannel handling. + * + * valgrind --tool=callgrind --instr-atstart=no smbd + */ +#ifdef CALLGRIND_START_INSTRUMENTATION + CALLGRIND_START_INSTRUMENTATION; +#endif return; } diff --git a/source3/smbd/smb2_process.c b/source3/smbd/smb2_process.c index ad4386e08a4..e0c4cd459c1 100644 --- a/source3/smbd/smb2_process.c +++ b/source3/smbd/smb2_process.c @@ -1697,12 +1697,47 @@ struct smbd_tevent_trace_state { SMBPROFILE_BASIC_ASYNC_STATE(profile_idle); }; +static inline void smbd_tevent_trace_callback_before_loop_once( + struct smbd_tevent_trace_state *state) +{ + talloc_free(state->frame); + state->frame = talloc_stackframe_pool(8192); +} + +static inline void smbd_tevent_trace_callback_after_loop_once( + struct smbd_tevent_trace_state *state) +{ + TALLOC_FREE(state->frame); +} + static void smbd_tevent_trace_callback(enum tevent_trace_point point, void *private_data) { struct smbd_tevent_trace_state *state = (struct smbd_tevent_trace_state *)private_data; + switch (point) { + case TEVENT_TRACE_BEFORE_WAIT: + break; + case TEVENT_TRACE_AFTER_WAIT: + break; + case TEVENT_TRACE_BEFORE_LOOP_ONCE: + smbd_tevent_trace_callback_before_loop_once(state); + break; + case TEVENT_TRACE_AFTER_LOOP_ONCE: + smbd_tevent_trace_callback_after_loop_once(state); + break; + } + + errno = 0; +} + +static void smbd_tevent_trace_callback_profile(enum tevent_trace_point point, + void *private_data) +{ + struct smbd_tevent_trace_state *state = + (struct smbd_tevent_trace_state *)private_data; + switch (point) { case TEVENT_TRACE_BEFORE_WAIT: if (!smbprofile_dump_pending()) { @@ -1732,11 +1767,10 @@ static void smbd_tevent_trace_callback(enum tevent_trace_point point, } break; case TEVENT_TRACE_BEFORE_LOOP_ONCE: - TALLOC_FREE(state->frame); - state->frame = talloc_stackframe_pool(8192); + smbd_tevent_trace_callback_before_loop_once(state); break; case TEVENT_TRACE_AFTER_LOOP_ONCE: - TALLOC_FREE(state->frame); + smbd_tevent_trace_callback_after_loop_once(state); break; } @@ -2009,8 +2043,15 @@ void smbd_process(struct tevent_context *ev_ctx, TALLOC_FREE(trace_state.frame); - tevent_set_trace_callback(ev_ctx, smbd_tevent_trace_callback, - &trace_state); + if (smbprofile_active()) { + tevent_set_trace_callback(ev_ctx, + smbd_tevent_trace_callback_profile, + &trace_state); + } else { + tevent_set_trace_callback(ev_ctx, + smbd_tevent_trace_callback, + &trace_state); + } ret = tevent_loop_wait(ev_ctx); if (ret != 0) { diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 39d0e80f591..462e0d7ecac 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -135,7 +135,6 @@ static const struct smbd_smb2_dispatch_table { .as_root = true, },{ .opcode = SMB2_OP_KEEPALIVE, - .as_root = true, },{ .opcode = SMB2_OP_QUERY_DIRECTORY, .need_session = true, @@ -325,11 +324,23 @@ static NTSTATUS smbd_initialize_smb2(struct smbXsrv_connection *xconn, } tevent_fd_set_auto_close(xconn->transport.fde); - /* Ensure child is set to non-blocking mode */ + /* + * Ensure child is set to non-blocking mode, + * unless the system supports MSG_DONTWAIT, + * if MSG_DONTWAIT is available we should force + * blocking mode. + */ +#ifdef MSG_DONTWAIT + rc = set_blocking(xconn->transport.sock, true); + if (rc < 0) { + return NT_STATUS_INTERNAL_ERROR; + } +#else rc = set_blocking(xconn->transport.sock, false); if (rc < 0) { return NT_STATUS_INTERNAL_ERROR; } +#endif return NT_STATUS_OK; } @@ -3415,7 +3426,7 @@ skipped_signing: SMB_ASSERT(call->fileid_ofs == 0); /* This call needs to be run as root */ change_to_root_user(); - } else { + } else if (opcode != SMB2_OP_KEEPALIVE) { SMB_ASSERT(call->need_tcon); } @@ -4729,6 +4740,7 @@ static NTSTATUS smbd_smb2_flush_send_queue(struct smbXsrv_connection *xconn) while (xconn->smb2.send_queue != NULL) { struct smbd_smb2_send_queue *e = xconn->smb2.send_queue; + unsigned sendmsg_flags = 0; bool ok; struct msghdr msg; @@ -4802,7 +4814,14 @@ static NTSTATUS smbd_smb2_flush_send_queue(struct smbXsrv_connection *xconn) .msg_iovlen = e->count, }; - ret = sendmsg(xconn->transport.sock, &msg, 0); +#ifdef MSG_NOSIGNAL + sendmsg_flags |= MSG_NOSIGNAL; +#endif +#ifdef MSG_DONTWAIT + sendmsg_flags |= MSG_DONTWAIT; +#endif + + ret = sendmsg(xconn->transport.sock, &msg, sendmsg_flags); if (ret == 0) { /* propagate end of file */ return NT_STATUS_INTERNAL_ERROR; @@ -4865,6 +4884,7 @@ static NTSTATUS smbd_smb2_io_handler(struct smbXsrv_connection *xconn, struct smbd_smb2_request_read_state *state = &xconn->smb2.request_read_state; struct smbd_smb2_request *req = NULL; size_t min_recvfile_size = UINT32_MAX; + unsigned recvmsg_flags = 0; int ret; int err; bool retry; @@ -4910,7 +4930,14 @@ again: .msg_iovlen = 1, }; - ret = recvmsg(xconn->transport.sock, &msg, 0); +#ifdef MSG_NOSIGNAL + recvmsg_flags |= MSG_NOSIGNAL; +#endif +#ifdef MSG_DONTWAIT + recvmsg_flags |= MSG_DONTWAIT; +#endif + + ret = recvmsg(xconn->transport.sock, &msg, recvmsg_flags); if (ret == 0) { /* propagate end of file */ status = NT_STATUS_END_OF_FILE; diff --git a/source4/torture/smb2/bench.c b/source4/torture/smb2/bench.c new file mode 100644 index 00000000000..a91ca6c08d3 --- /dev/null +++ b/source4/torture/smb2/bench.c @@ -0,0 +1,1376 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 bench test suite + + Copyright (C) Stefan Metzmacher 2022 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" + +#include "system/filesys.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/security.h" +#include "lib/events/events.h" + +#define FNAME "test_create.dat" +#define DNAME "smb2_open" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + return false; \ + }} while (0) + +#define CHECK_EQUAL(v, correct) do { \ + if (v != correct) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value for %s 0x%08llx - " \ + "should be 0x%08llx\n", \ + __location__, #v, \ + (unsigned long long)v, \ + (unsigned long long)correct); \ + return false; \ + }} while (0) + +#define CHECK_TIME(t, field) do { \ + time_t t1, t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t1 = t & ~1; \ + t2 = nt_time_to_unix(finfo.all_info.out.field) & ~1; \ + if (abs(t1-t2) > 2) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + timestring(tctx, t1), \ + timestring(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(t, field) do { \ + NTTIME t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (llabs((int64_t)(t-t2)) > 20000) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + nt_time_string(tctx, t), \ + nt_time_string(tctx, t2)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, tctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != (finfo.all_info.out.field)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for field %s 0x%x - 0x%x\n", \ + __location__, #field, (int)v,\ + (int)(finfo.all_info.out.field)); \ + dump_all_info(tctx, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x - should be 0x%x\n", \ + __location__, #v, (int)(v), (int)correct); \ + ret = false; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.basic_info.in.file.handle = h1; \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(tctx, \ + "(%s) Failed to set attrib 0x%x on %s\n", \ + __location__, (unsigned int)(sattrib), fname); \ + }} while (0) + +/* + stress testing keepalive iops + */ + +struct test_smb2_bench_echo_conn; +struct test_smb2_bench_echo_loop; + +struct test_smb2_bench_echo_state { + struct torture_context *tctx; + size_t num_conns; + struct test_smb2_bench_echo_conn *conns; + size_t num_loops; + struct test_smb2_bench_echo_loop *loops; + size_t pending_loops; + struct timeval starttime; + int timecount; + int timelimit; + uint64_t num_finished; + double total_latency; + double min_latency; + double max_latency; + bool ok; + bool stop; +}; + +struct test_smb2_bench_echo_conn { + struct test_smb2_bench_echo_state *state; + int idx; + struct smb2_tree *tree; +}; + +struct test_smb2_bench_echo_loop { + struct test_smb2_bench_echo_state *state; + struct test_smb2_bench_echo_conn *conn; + int idx; + struct tevent_immediate *im; + struct tevent_req *req; + struct timeval starttime; + uint64_t num_started; + uint64_t num_finished; + uint64_t total_finished; + uint64_t max_finished; + double total_latency; + double min_latency; + double max_latency; + NTSTATUS error; +}; + +static void test_smb2_bench_echo_loop_do( + struct test_smb2_bench_echo_loop *loop); + +static void test_smb2_bench_echo_loop_start(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_smb2_bench_echo_loop *loop = + (struct test_smb2_bench_echo_loop *) + private_data; + + test_smb2_bench_echo_loop_do(loop); +} + +static void test_smb2_bench_echo_loop_done(struct tevent_req *req); + +static void test_smb2_bench_echo_loop_do( + struct test_smb2_bench_echo_loop *loop) +{ + struct test_smb2_bench_echo_state *state = loop->state; + + loop->num_started += 1; + loop->starttime = timeval_current(); + loop->req = smb2cli_echo_send(state->loops, + state->tctx->ev, + loop->conn->tree->session->transport->conn, + 1000); + torture_assert_goto(state->tctx, loop->req != NULL, + state->ok, asserted, "smb2cli_echo_send"); -- Samba Shared Repository