r23056: added automatic reconnect to RAW-BENCH-OPEN, similar to RAW-BENCH-LOCK
[ira/wip.git] / source4 / torture / raw / openbench.c
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "torture/torture.h"
25 #include "libcli/raw/libcliraw.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/popt_common.h"
32 #include "libcli/composite/composite.h"
33 #include "libcli/smb_composite/smb_composite.h"
34
35 #define BASEDIR "\\benchopen"
36
37 static int nprocs;
38 static int open_failed;
39 static int open_retries;
40 static char **fnames;
41 static int num_connected;
42
43 struct benchopen_state {
44         TALLOC_CTX *mem_ctx;
45         struct event_context *ev;
46         struct smbcli_state *cli;
47         struct smbcli_tree *tree;
48         int client_num;
49         int fnum;
50         int file_num;
51         int count;
52         int lastcount;
53         BOOL waiting_open, waiting_close;
54         union smb_open open_parms;
55         union smb_close close_parms;
56         struct smbcli_request *req_open;
57         struct smbcli_request *req_close;
58         struct smb_composite_connect reconnect;
59
60         /* these are used for reconnections */
61         int dest_port;
62         const char *dest_host;
63         const char *called_name;
64         const char *service_type;
65 };
66
67 static void next_open(struct benchopen_state *state);
68 static void reopen_connection(struct event_context *ev, struct timed_event *te, 
69                               struct timeval t, void *private_data);
70
71
72 /*
73   complete an async reconnect
74  */
75 static void reopen_connection_complete(struct composite_context *ctx)
76 {
77         struct benchopen_state *state = (struct benchopen_state *)ctx->async.private_data;
78         NTSTATUS status;
79         struct smb_composite_connect *io = &state->reconnect;
80
81         status = smb_composite_connect_recv(ctx, state->mem_ctx);
82         if (!NT_STATUS_IS_OK(status)) {
83                 event_add_timed(state->ev, state->mem_ctx, 
84                                 timeval_current_ofs(1,0), 
85                                 reopen_connection, state);
86                 return;
87         }
88
89         state->tree = io->out.tree;
90
91         num_connected++;
92
93         DEBUG(0,("reconnect to %s finished (%u connected)\n", state->dest_host,
94                  num_connected));
95
96         next_open(state);
97 }
98
99         
100
101 /*
102   reopen a connection
103  */
104 static void reopen_connection(struct event_context *ev, struct timed_event *te, 
105                               struct timeval t, void *private_data)
106 {
107         struct benchopen_state *state = (struct benchopen_state *)private_data;
108         struct composite_context *ctx;
109         struct smb_composite_connect *io = &state->reconnect;
110         char *host, *share;
111
112         if (!torture_get_conn_index(state->client_num, state->mem_ctx, &host, &share)) {
113                 DEBUG(0,("Can't find host/share for reconnect?!\n"));
114                 exit(1);
115         }
116
117         io->in.dest_host    = state->dest_host;
118         io->in.port         = state->dest_port;
119         io->in.called_name  = state->called_name;
120         io->in.service      = share;
121         io->in.service_type = state->service_type;
122         io->in.credentials  = cmdline_credentials;
123         io->in.fallback_to_anonymous = False;
124         io->in.workgroup    = lp_workgroup();
125
126         /* kill off the remnants of the old connection */
127         talloc_free(state->tree);
128         state->tree = NULL;
129         state->fnum = -1;
130         state->waiting_open = False;
131         state->waiting_close = False;
132
133         ctx = smb_composite_connect_send(io, state->mem_ctx, state->ev);
134         if (ctx == NULL) {
135                 DEBUG(0,("Failed to setup async reconnect\n"));
136                 exit(1);
137         }
138
139         ctx->async.fn = reopen_connection_complete;
140         ctx->async.private_data = state;
141 }
142
143 static void open_completed(struct smbcli_request *req);
144 static void close_completed(struct smbcli_request *req);
145
146
147 static void next_open(struct benchopen_state *state)
148 {
149         state->count++;
150
151         state->file_num = (state->file_num+1) % (nprocs+1);
152         state->open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
153         state->open_parms.ntcreatex.in.flags = 0;
154         state->open_parms.ntcreatex.in.root_fid = 0;
155         state->open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
156         state->open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
157         state->open_parms.ntcreatex.in.alloc_size = 0;
158         state->open_parms.ntcreatex.in.share_access = 0;
159         state->open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
160         state->open_parms.ntcreatex.in.create_options = 0;
161         state->open_parms.ntcreatex.in.impersonation = 0;
162         state->open_parms.ntcreatex.in.security_flags = 0;
163         state->open_parms.ntcreatex.in.fname = fnames[state->file_num];
164
165         state->req_open = smb_raw_open_send(state->tree, &state->open_parms);
166         state->req_open->async.fn = open_completed;
167         state->req_open->async.private = state;
168         state->waiting_open = True;
169
170         if (state->fnum == -1) {
171                 return;
172         }
173
174         state->close_parms.close.level = RAW_CLOSE_CLOSE;
175         state->close_parms.close.in.file.fnum = state->fnum;
176         state->close_parms.close.in.write_time = 0;
177
178         state->req_close = smb_raw_close_send(state->tree, &state->close_parms);
179         state->req_close->async.fn = close_completed;
180         state->req_close->async.private = state;
181         state->waiting_close = True;
182 }
183
184 /*
185   called when a open completes
186 */
187 static void open_completed(struct smbcli_request *req)
188 {
189         struct benchopen_state *state = (struct benchopen_state *)req->async.private;
190         TALLOC_CTX *tmp_ctx = talloc_new(state->mem_ctx);
191         NTSTATUS status;
192
193         status = smb_raw_open_recv(req, tmp_ctx, &state->open_parms);
194
195         talloc_free(tmp_ctx);
196
197         state->req_open = NULL;
198
199         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
200             NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
201                 talloc_free(state->tree);
202                 talloc_free(state->cli);
203                 state->tree = NULL;
204                 state->cli = NULL;
205                 num_connected--;        
206                 DEBUG(0,("reopening connection to %s\n", state->dest_host));
207                 event_add_timed(state->ev, state->mem_ctx, 
208                                 timeval_current_ofs(1,0), 
209                                 reopen_connection, state);
210                 return;
211         }
212
213         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
214                 open_retries++;
215                 state->req_open = smb_raw_open_send(state->tree, &state->open_parms);
216                 state->req_open->async.fn = open_completed;
217                 state->req_open->async.private = state;
218                 return;
219         }
220
221         if (!NT_STATUS_IS_OK(status)) {
222                 open_failed++;
223                 DEBUG(0,("open failed - %s\n", nt_errstr(status)));
224                 return;
225         }
226
227         state->fnum = state->open_parms.ntcreatex.out.file.fnum;
228         state->waiting_open = False;
229
230         if (!state->waiting_close) {
231                 next_open(state);
232         }
233 }       
234
235 /*
236   called when a close completes
237 */
238 static void close_completed(struct smbcli_request *req)
239 {
240         struct benchopen_state *state = (struct benchopen_state *)req->async.private;
241         NTSTATUS status = smbcli_request_simple_recv(req);
242
243         state->req_close = NULL;
244
245         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
246             NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
247                 talloc_free(state->tree);
248                 talloc_free(state->cli);
249                 state->tree = NULL;
250                 state->cli = NULL;
251                 num_connected--;        
252                 DEBUG(0,("reopening connection to %s\n", state->dest_host));
253                 event_add_timed(state->ev, state->mem_ctx, 
254                                 timeval_current_ofs(1,0), 
255                                 reopen_connection, state);
256                 return;
257         }
258
259         if (!NT_STATUS_IS_OK(status)) {
260                 open_failed++;
261                 DEBUG(0,("close failed - %s\n", nt_errstr(status)));
262                 return;
263         }
264
265         state->waiting_close = False;
266
267         if (!state->waiting_open) {
268                 next_open(state);
269         }
270 }       
271
272
273 static void report_rate(struct event_context *ev, struct timed_event *te, 
274                         struct timeval t, void *private_data)
275 {
276         struct benchopen_state *state = talloc_get_type(private_data, 
277                                                         struct benchopen_state);
278         int i;
279         for (i=0;i<nprocs;i++) {
280                 printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
281                 state[i].lastcount = state[i].count;
282         }
283         printf("\r");
284         fflush(stdout);
285         event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
286 }
287
288 /* 
289    benchmark open calls
290 */
291 BOOL torture_bench_open(struct torture_context *torture)
292 {
293         BOOL ret = True;
294         TALLOC_CTX *mem_ctx = talloc_new(torture);
295         int i;
296         int timelimit = torture_setting_int(torture, "timelimit", 10);
297         struct timeval tv;
298         struct event_context *ev = event_context_find(mem_ctx);
299         struct benchopen_state *state;
300         int total = 0, minops=0;
301         bool progress;
302
303         progress = torture_setting_bool(torture, "progress", true);
304         
305         nprocs = lp_parm_int(-1, "torture", "nprocs", 4);
306
307         state = talloc_zero_array(mem_ctx, struct benchopen_state, nprocs);
308
309         printf("Opening %d connections\n", nprocs);
310         for (i=0;i<nprocs;i++) {
311                 state[i].mem_ctx = talloc_new(state);
312                 state[i].client_num = i;
313                 state[i].ev = ev;
314                 if (!torture_open_connection_ev(&state[i].cli, i, ev)) {
315                         return False;
316                 }
317                 talloc_steal(mem_ctx, state);
318                 state[i].tree = state[i].cli->tree;
319                 state[i].dest_host = talloc_strdup(state[i].mem_ctx, 
320                                                    state[i].cli->tree->session->transport->socket->hostname);
321                 state[i].dest_port = state[i].cli->tree->session->transport->socket->port;
322                 state[i].called_name  = talloc_strdup(state[i].mem_ctx,
323                                                       state[i].cli->tree->session->transport->called.name);
324                 state[i].service_type = talloc_strdup(state[i].mem_ctx,
325                                                       state[i].cli->tree->device);
326         }
327
328         num_connected = i;
329
330         if (!torture_setup_dir(state[0].cli, BASEDIR)) {
331                 goto failed;
332         }
333
334         fnames = talloc_array(mem_ctx, char *, nprocs+1);
335         for (i=0;i<nprocs+1;i++) {
336                 fnames[i] = talloc_asprintf(fnames, "%s\\file%d.dat", BASEDIR, i);
337         }
338
339         for (i=0;i<nprocs;i++) {
340                 state[i].fnum = -1;
341                 state[i].file_num = i;          
342                 next_open(&state[i]);
343         }
344
345         tv = timeval_current(); 
346
347         if (progress) {
348                 event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
349         }
350
351         printf("Running for %d seconds\n", timelimit);
352         while (timeval_elapsed(&tv) < timelimit) {
353                 event_loop_once(ev);
354
355                 if (open_failed) {
356                         DEBUG(0,("open failed\n"));
357                         goto failed;
358                 }
359         }
360
361         printf("%.2f ops/second (%d retries)\n", 
362                total/timeval_elapsed(&tv), open_retries);
363         minops = state[0].count;
364         for (i=0;i<nprocs;i++) {
365                 printf("[%d] %u ops\n", i, state[i].count);
366                 if (state[i].count < minops) minops = state[i].count;
367         }
368         if (minops < 0.5*total/nprocs) {
369                 printf("Failed: unbalanced open\n");
370                 goto failed;
371         }
372
373         for (i=0;i<nprocs;i++) {
374                 talloc_free(state[i].req_open);
375                 talloc_free(state[i].req_close);
376                 smb_raw_exit(state[i].tree->session);
377         }
378
379         smbcli_deltree(state[0].tree, BASEDIR);
380         talloc_free(mem_ctx);
381         return ret;
382
383 failed:
384         talloc_free(mem_ctx);
385         return False;
386 }