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