Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : open benchmark
5 :
6 : Copyright (C) Andrew Tridgell 2007
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "torture/torture.h"
24 : #include "libcli/raw/libcliraw.h"
25 : #include "libcli/raw/raw_proto.h"
26 : #include "system/time.h"
27 : #include "system/filesys.h"
28 : #include "libcli/libcli.h"
29 : #include "torture/util.h"
30 : #include "lib/events/events.h"
31 : #include "lib/cmdline/cmdline.h"
32 : #include "libcli/composite/composite.h"
33 : #include "libcli/smb_composite/smb_composite.h"
34 : #include "libcli/resolve/resolve.h"
35 : #include "param/param.h"
36 : #include "torture/raw/proto.h"
37 : #include "libcli/smb/smbXcli_base.h"
38 : #include "../lib/util/util_net.h"
39 :
40 : #define BASEDIR "\\benchopen"
41 :
42 : static int nprocs;
43 : static int open_failed;
44 : static int close_failed;
45 : static char **fnames;
46 : static int num_connected;
47 : static struct tevent_timer *report_te;
48 :
49 : struct benchopen_state {
50 : struct torture_context *tctx;
51 : TALLOC_CTX *mem_ctx;
52 : struct tevent_context *ev;
53 : struct smbcli_state *cli;
54 : struct smbcli_tree *tree;
55 : int client_num;
56 : int close_fnum;
57 : int open_fnum;
58 : int close_file_num;
59 : int open_file_num;
60 : int pending_file_num;
61 : int next_file_num;
62 : int count;
63 : int lastcount;
64 : union smb_open open_parms;
65 : int open_retries;
66 : union smb_close close_parms;
67 : struct smbcli_request *req_open;
68 : struct smbcli_request *req_close;
69 : struct smb_composite_connect reconnect;
70 : struct tevent_timer *te;
71 :
72 : /* these are used for reconnections */
73 : const char **dest_ports;
74 : const char *dest_host;
75 : const char *called_name;
76 : const char *service_type;
77 : };
78 :
79 : static void next_open(struct benchopen_state *state);
80 : static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te,
81 : struct timeval t, void *private_data);
82 :
83 :
84 : /*
85 : complete an async reconnect
86 : */
87 0 : static void reopen_connection_complete(struct composite_context *ctx)
88 : {
89 0 : struct benchopen_state *state = (struct benchopen_state *)ctx->async.private_data;
90 0 : NTSTATUS status;
91 0 : struct smb_composite_connect *io = &state->reconnect;
92 :
93 0 : status = smb_composite_connect_recv(ctx, state->mem_ctx);
94 0 : if (!NT_STATUS_IS_OK(status)) {
95 0 : talloc_free(state->te);
96 0 : state->te = tevent_add_timer(state->ev, state->mem_ctx,
97 : timeval_current_ofs(1,0),
98 : reopen_connection, state);
99 0 : return;
100 : }
101 :
102 0 : state->tree = io->out.tree;
103 :
104 0 : num_connected++;
105 :
106 0 : DEBUG(0,("[%u] reconnect to %s finished (%u connected)\n",
107 : state->client_num, state->dest_host, num_connected));
108 :
109 0 : state->open_fnum = -1;
110 0 : state->close_fnum = -1;
111 0 : next_open(state);
112 : }
113 :
114 :
115 :
116 : /*
117 : reopen a connection
118 : */
119 0 : static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te,
120 : struct timeval t, void *private_data)
121 : {
122 0 : struct benchopen_state *state = (struct benchopen_state *)private_data;
123 0 : struct composite_context *ctx;
124 0 : struct smb_composite_connect *io = &state->reconnect;
125 0 : char *host, *share;
126 :
127 0 : state->te = NULL;
128 :
129 0 : if (!torture_get_conn_index(state->client_num, state->mem_ctx, state->tctx, &host, &share)) {
130 0 : DEBUG(0,("Can't find host/share for reconnect?!\n"));
131 0 : exit(1);
132 : }
133 :
134 0 : io->in.dest_host = state->dest_host;
135 0 : io->in.dest_ports = state->dest_ports;
136 0 : io->in.socket_options = lpcfg_socket_options(state->tctx->lp_ctx);
137 0 : io->in.called_name = state->called_name;
138 0 : io->in.service = share;
139 0 : io->in.service_type = state->service_type;
140 0 : io->in.credentials = samba_cmdline_get_creds();
141 0 : io->in.fallback_to_anonymous = false;
142 0 : io->in.workgroup = lpcfg_workgroup(state->tctx->lp_ctx);
143 0 : io->in.gensec_settings = lpcfg_gensec_settings(state->mem_ctx, state->tctx->lp_ctx);
144 0 : lpcfg_smbcli_options(state->tctx->lp_ctx, &io->in.options);
145 0 : lpcfg_smbcli_session_options(state->tctx->lp_ctx, &io->in.session_options);
146 :
147 : /* kill off the remnants of the old connection */
148 0 : talloc_free(state->tree);
149 0 : state->tree = NULL;
150 0 : state->open_fnum = -1;
151 0 : state->close_fnum = -1;
152 :
153 0 : ctx = smb_composite_connect_send(io, state->mem_ctx,
154 0 : lpcfg_resolve_context(state->tctx->lp_ctx),
155 : state->ev);
156 0 : if (ctx == NULL) {
157 0 : DEBUG(0,("Failed to setup async reconnect\n"));
158 0 : exit(1);
159 : }
160 :
161 0 : ctx->async.fn = reopen_connection_complete;
162 0 : ctx->async.private_data = state;
163 0 : }
164 :
165 : static void open_completed(struct smbcli_request *req);
166 : static void close_completed(struct smbcli_request *req);
167 :
168 :
169 0 : static void next_open(struct benchopen_state *state)
170 : {
171 0 : state->count++;
172 :
173 0 : state->pending_file_num = state->next_file_num;
174 0 : state->next_file_num = (state->next_file_num+1) % (3*nprocs);
175 :
176 0 : DEBUG(2,("[%d] opening %u\n", state->client_num, state->pending_file_num));
177 0 : state->open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
178 0 : state->open_parms.ntcreatex.in.flags = 0;
179 0 : state->open_parms.ntcreatex.in.root_fid.fnum = 0;
180 0 : state->open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
181 0 : state->open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
182 0 : state->open_parms.ntcreatex.in.alloc_size = 0;
183 0 : state->open_parms.ntcreatex.in.share_access = 0;
184 0 : state->open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
185 0 : state->open_parms.ntcreatex.in.create_options = 0;
186 0 : state->open_parms.ntcreatex.in.impersonation = 0;
187 0 : state->open_parms.ntcreatex.in.security_flags = 0;
188 0 : state->open_parms.ntcreatex.in.fname = fnames[state->pending_file_num];
189 :
190 0 : state->req_open = smb_raw_open_send(state->tree, &state->open_parms);
191 0 : state->req_open->async.fn = open_completed;
192 0 : state->req_open->async.private_data = state;
193 0 : }
194 :
195 :
196 0 : static void next_close(struct benchopen_state *state)
197 : {
198 0 : if (state->close_fnum == -1) {
199 0 : return;
200 : }
201 0 : DEBUG(2,("[%d] closing %d (fnum[%d])\n",
202 : state->client_num, state->close_file_num, state->close_fnum));
203 0 : state->close_parms.close.level = RAW_CLOSE_CLOSE;
204 0 : state->close_parms.close.in.file.fnum = state->close_fnum;
205 0 : state->close_parms.close.in.write_time = 0;
206 :
207 0 : state->req_close = smb_raw_close_send(state->tree, &state->close_parms);
208 0 : state->req_close->async.fn = close_completed;
209 0 : state->req_close->async.private_data = state;
210 : }
211 :
212 : /*
213 : called when a open completes
214 : */
215 0 : static void open_completed(struct smbcli_request *req)
216 : {
217 0 : struct benchopen_state *state = (struct benchopen_state *)req->async.private_data;
218 0 : TALLOC_CTX *tmp_ctx = talloc_new(state->mem_ctx);
219 0 : NTSTATUS status;
220 :
221 0 : status = smb_raw_open_recv(req, tmp_ctx, &state->open_parms);
222 :
223 0 : talloc_free(tmp_ctx);
224 :
225 0 : state->req_open = NULL;
226 :
227 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
228 0 : NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) ||
229 0 : NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
230 0 : talloc_free(state->tree);
231 0 : talloc_free(state->cli);
232 0 : state->tree = NULL;
233 0 : state->cli = NULL;
234 0 : num_connected--;
235 0 : DEBUG(0,("[%u] reopening connection to %s\n",
236 : state->client_num, state->dest_host));
237 0 : talloc_free(state->te);
238 0 : state->te = tevent_add_timer(state->ev, state->mem_ctx,
239 : timeval_current_ofs(1,0),
240 : reopen_connection, state);
241 0 : return;
242 : }
243 :
244 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
245 0 : DEBUG(2,("[%d] retrying open %d\n",
246 : state->client_num, state->pending_file_num));
247 0 : state->open_retries++;
248 0 : state->req_open = smb_raw_open_send(state->tree, &state->open_parms);
249 0 : state->req_open->async.fn = open_completed;
250 0 : state->req_open->async.private_data = state;
251 0 : return;
252 : }
253 :
254 0 : if (!NT_STATUS_IS_OK(status)) {
255 0 : open_failed++;
256 0 : DEBUG(0,("[%u] open failed %d - %s\n",
257 : state->client_num, state->pending_file_num,
258 : nt_errstr(status)));
259 0 : return;
260 : }
261 :
262 0 : state->close_file_num = state->open_file_num;
263 0 : state->close_fnum = state->open_fnum;
264 0 : state->open_file_num = state->pending_file_num;
265 0 : state->open_fnum = state->open_parms.ntcreatex.out.file.fnum;
266 :
267 0 : DEBUG(2,("[%d] open completed %d (fnum[%d])\n",
268 : state->client_num, state->open_file_num, state->open_fnum));
269 :
270 0 : if (state->close_fnum != -1) {
271 0 : next_close(state);
272 : }
273 :
274 0 : next_open(state);
275 : }
276 :
277 : /*
278 : called when a close completes
279 : */
280 0 : static void close_completed(struct smbcli_request *req)
281 : {
282 0 : struct benchopen_state *state = (struct benchopen_state *)req->async.private_data;
283 0 : NTSTATUS status = smbcli_request_simple_recv(req);
284 :
285 0 : state->req_close = NULL;
286 :
287 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
288 0 : NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) ||
289 0 : NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
290 0 : talloc_free(state->tree);
291 0 : talloc_free(state->cli);
292 0 : state->tree = NULL;
293 0 : state->cli = NULL;
294 0 : num_connected--;
295 0 : DEBUG(0,("[%u] reopening connection to %s\n",
296 : state->client_num, state->dest_host));
297 0 : talloc_free(state->te);
298 0 : state->te = tevent_add_timer(state->ev, state->mem_ctx,
299 : timeval_current_ofs(1,0),
300 : reopen_connection, state);
301 0 : return;
302 : }
303 :
304 0 : if (!NT_STATUS_IS_OK(status)) {
305 0 : close_failed++;
306 0 : DEBUG(0,("[%u] close failed %d (fnum[%d]) - %s\n",
307 : state->client_num, state->close_file_num,
308 : state->close_fnum,
309 : nt_errstr(status)));
310 0 : return;
311 : }
312 :
313 0 : DEBUG(2,("[%d] close completed %d (fnum[%d])\n",
314 : state->client_num, state->close_file_num,
315 : state->close_fnum));
316 : }
317 :
318 0 : static void echo_completion(struct smbcli_request *req)
319 : {
320 0 : struct benchopen_state *state = (struct benchopen_state *)req->async.private_data;
321 0 : NTSTATUS status = smbcli_request_simple_recv(req);
322 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
323 0 : NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) ||
324 0 : NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
325 0 : talloc_free(state->tree);
326 0 : state->tree = NULL;
327 0 : num_connected--;
328 0 : DEBUG(0,("[%u] reopening connection to %s\n",
329 : state->client_num, state->dest_host));
330 0 : talloc_free(state->te);
331 0 : state->te = tevent_add_timer(state->ev, state->mem_ctx,
332 : timeval_current_ofs(1,0),
333 : reopen_connection, state);
334 : }
335 0 : }
336 :
337 0 : static void report_rate(struct tevent_context *ev, struct tevent_timer *te,
338 : struct timeval t, void *private_data)
339 : {
340 0 : struct benchopen_state *state = talloc_get_type(private_data,
341 : struct benchopen_state);
342 0 : int i;
343 0 : for (i=0;i<nprocs;i++) {
344 0 : printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
345 0 : state[i].lastcount = state[i].count;
346 : }
347 0 : printf("\r");
348 0 : fflush(stdout);
349 0 : report_te = tevent_add_timer(ev, state, timeval_current_ofs(1, 0),
350 : report_rate, state);
351 :
352 : /* send an echo on each interface to ensure it stays alive - this helps
353 : with IP takeover */
354 0 : for (i=0;i<nprocs;i++) {
355 0 : struct smb_echo p;
356 0 : struct smbcli_request *req;
357 :
358 0 : if (!state[i].tree) {
359 0 : continue;
360 : }
361 :
362 0 : p.in.repeat_count = 1;
363 0 : p.in.size = 0;
364 0 : p.in.data = NULL;
365 0 : req = smb_raw_echo_send(state[i].tree->session->transport, &p);
366 0 : req->async.private_data = &state[i];
367 0 : req->async.fn = echo_completion;
368 : }
369 0 : }
370 :
371 : /*
372 : benchmark open calls
373 : */
374 0 : bool torture_bench_open(struct torture_context *torture)
375 : {
376 0 : bool ret = true;
377 0 : TALLOC_CTX *mem_ctx = talloc_new(torture);
378 0 : int i;
379 0 : int timelimit = torture_setting_int(torture, "timelimit", 10);
380 0 : struct timeval tv;
381 0 : struct benchopen_state *state;
382 0 : int total = 0;
383 0 : int total_retries = 0;
384 0 : int minops = 0;
385 0 : bool progress=false;
386 :
387 0 : progress = torture_setting_bool(torture, "progress", true);
388 :
389 0 : nprocs = torture_setting_int(torture, "nprocs", 4);
390 :
391 0 : state = talloc_zero_array(mem_ctx, struct benchopen_state, nprocs);
392 :
393 0 : printf("Opening %d connections\n", nprocs);
394 0 : for (i=0;i<nprocs;i++) {
395 0 : const struct sockaddr_storage *dest_ss;
396 0 : char addrstr[INET6_ADDRSTRLEN];
397 0 : const char *dest_str;
398 0 : uint16_t dest_port;
399 :
400 0 : state[i].tctx = torture;
401 0 : state[i].mem_ctx = talloc_new(state);
402 0 : state[i].client_num = i;
403 0 : state[i].ev = torture->ev;
404 0 : if (!torture_open_connection_ev(&state[i].cli, i, torture, torture->ev)) {
405 0 : return false;
406 : }
407 0 : talloc_steal(state[i].mem_ctx, state[i].cli);
408 0 : state[i].tree = state[i].cli->tree;
409 :
410 0 : dest_ss = smbXcli_conn_remote_sockaddr(
411 0 : state[i].tree->session->transport->conn);
412 0 : dest_str = print_sockaddr(addrstr, sizeof(addrstr), dest_ss);
413 0 : dest_port = get_sockaddr_port(dest_ss);
414 :
415 0 : state[i].dest_host = talloc_strdup(state[i].mem_ctx, dest_str);
416 0 : state[i].dest_ports = talloc_array(state[i].mem_ctx,
417 : const char *, 2);
418 0 : state[i].dest_ports[0] = talloc_asprintf(state[i].dest_ports,
419 : "%u", dest_port);
420 0 : state[i].dest_ports[1] = NULL;
421 0 : state[i].called_name = talloc_strdup(state[i].mem_ctx,
422 0 : smbXcli_conn_remote_name(state[i].tree->session->transport->conn));
423 0 : state[i].service_type = talloc_strdup(state[i].mem_ctx, "?????");
424 : }
425 :
426 0 : num_connected = i;
427 :
428 0 : if (!torture_setup_dir(state[0].cli, BASEDIR)) {
429 0 : goto failed;
430 : }
431 :
432 0 : fnames = talloc_array(mem_ctx, char *, 3*nprocs);
433 0 : for (i=0;i<3*nprocs;i++) {
434 0 : fnames[i] = talloc_asprintf(fnames, "%s\\file%d.dat", BASEDIR, i);
435 : }
436 :
437 0 : for (i=0;i<nprocs;i++) {
438 : /* all connections start with the same file */
439 0 : state[i].next_file_num = 0;
440 0 : state[i].open_fnum = -1;
441 0 : state[i].close_fnum = -1;
442 0 : next_open(&state[i]);
443 : }
444 :
445 0 : tv = timeval_current();
446 :
447 0 : if (progress) {
448 0 : report_te = tevent_add_timer(torture->ev, state, timeval_current_ofs(1, 0),
449 : report_rate, state);
450 : }
451 :
452 0 : printf("Running for %d seconds\n", timelimit);
453 0 : while (timeval_elapsed(&tv) < timelimit) {
454 0 : tevent_loop_once(torture->ev);
455 :
456 0 : if (open_failed) {
457 0 : DEBUG(0,("open failed\n"));
458 0 : goto failed;
459 : }
460 0 : if (close_failed) {
461 0 : DEBUG(0,("open failed\n"));
462 0 : goto failed;
463 : }
464 : }
465 :
466 0 : talloc_free(report_te);
467 0 : if (progress) {
468 0 : for (i=0;i<nprocs;i++) {
469 0 : printf(" ");
470 : }
471 0 : printf("\r");
472 : }
473 :
474 0 : minops = state[0].count;
475 0 : for (i=0;i<nprocs;i++) {
476 0 : total += state[i].count;
477 0 : total_retries += state[i].open_retries;
478 0 : printf("[%d] %u ops (%u retries)\n",
479 0 : i, state[i].count, state[i].open_retries);
480 0 : if (state[i].count < minops) minops = state[i].count;
481 : }
482 0 : printf("%.2f ops/second (%d retries)\n",
483 0 : total/timeval_elapsed(&tv), total_retries);
484 0 : if (minops < 0.5*total/nprocs) {
485 0 : printf("Failed: unbalanced open\n");
486 0 : goto failed;
487 : }
488 :
489 0 : for (i=0;i<nprocs;i++) {
490 0 : talloc_free(state[i].req_open);
491 0 : talloc_free(state[i].req_close);
492 0 : smb_raw_exit(state[i].tree->session);
493 : }
494 :
495 0 : smbcli_deltree(state[0].tree, BASEDIR);
496 0 : talloc_free(mem_ctx);
497 0 : return ret;
498 :
499 0 : failed:
500 0 : talloc_free(mem_ctx);
501 0 : return false;
502 : }
|