Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : locking benchmark
5 :
6 : Copyright (C) Andrew Tridgell 2006
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 "libcli/raw/libcliraw.h"
24 : #include "libcli/raw/raw_proto.h"
25 : #include "system/time.h"
26 : #include "system/filesys.h"
27 : #include "libcli/libcli.h"
28 : #include "torture/util.h"
29 : #include "lib/events/events.h"
30 : #include "lib/cmdline/cmdline.h"
31 : #include "libcli/composite/composite.h"
32 : #include "libcli/smb_composite/smb_composite.h"
33 : #include "libcli/resolve/resolve.h"
34 : #include "param/param.h"
35 : #include "torture/raw/proto.h"
36 : #include "libcli/smb/smbXcli_base.h"
37 : #include "../lib/util/util_net.h"
38 :
39 : #define BASEDIR "\\benchlock"
40 : #define FNAME BASEDIR "\\lock.dat"
41 :
42 : static int nprocs;
43 : static int lock_failed;
44 : static int num_connected;
45 :
46 : enum lock_stage {LOCK_INITIAL, LOCK_LOCK, LOCK_UNLOCK};
47 :
48 : struct benchlock_state {
49 : struct torture_context *tctx;
50 : struct tevent_context *ev;
51 : struct smbcli_tree *tree;
52 : TALLOC_CTX *mem_ctx;
53 : int client_num;
54 : int fnum;
55 : enum lock_stage stage;
56 : int lock_offset;
57 : int unlock_offset;
58 : int count;
59 : int lastcount;
60 : struct smbcli_request *req;
61 : struct smb_composite_connect reconnect;
62 : struct tevent_timer *te;
63 :
64 : /* these are used for reconnections */
65 : const char **dest_ports;
66 : const char *dest_host;
67 : const char *called_name;
68 : const char *service_type;
69 : };
70 :
71 : static void lock_completion(struct smbcli_request *);
72 :
73 : /*
74 : send the next lock request
75 : */
76 0 : static void lock_send(struct benchlock_state *state)
77 : {
78 0 : union smb_lock io;
79 0 : struct smb_lock_entry lock;
80 :
81 0 : switch (state->stage) {
82 0 : case LOCK_INITIAL:
83 0 : io.lockx.in.ulock_cnt = 0;
84 0 : io.lockx.in.lock_cnt = 1;
85 0 : state->lock_offset = 0;
86 0 : state->unlock_offset = 0;
87 0 : lock.offset = state->lock_offset;
88 0 : break;
89 0 : case LOCK_LOCK:
90 0 : io.lockx.in.ulock_cnt = 0;
91 0 : io.lockx.in.lock_cnt = 1;
92 0 : state->lock_offset = (state->lock_offset+1)%(nprocs+1);
93 0 : lock.offset = state->lock_offset;
94 0 : break;
95 0 : case LOCK_UNLOCK:
96 0 : io.lockx.in.ulock_cnt = 1;
97 0 : io.lockx.in.lock_cnt = 0;
98 0 : lock.offset = state->unlock_offset;
99 0 : state->unlock_offset = (state->unlock_offset+1)%(nprocs+1);
100 0 : break;
101 : }
102 :
103 0 : lock.count = 1;
104 0 : lock.pid = state->tree->session->pid;
105 :
106 0 : io.lockx.level = RAW_LOCK_LOCKX;
107 0 : io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
108 0 : io.lockx.in.timeout = 100000;
109 0 : io.lockx.in.locks = &lock;
110 0 : io.lockx.in.file.fnum = state->fnum;
111 :
112 0 : state->req = smb_raw_lock_send(state->tree, &io);
113 0 : if (state->req == NULL) {
114 0 : DEBUG(0,("Failed to setup lock\n"));
115 0 : lock_failed++;
116 : }
117 0 : state->req->async.private_data = state;
118 0 : state->req->async.fn = lock_completion;
119 0 : }
120 :
121 : static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te,
122 : struct timeval t, void *private_data);
123 :
124 :
125 0 : static void reopen_file(struct tevent_context *ev, struct tevent_timer *te,
126 : struct timeval t, void *private_data)
127 : {
128 0 : struct benchlock_state *state = (struct benchlock_state *)private_data;
129 :
130 : /* reestablish our open file */
131 0 : state->fnum = smbcli_open(state->tree, FNAME, O_RDWR|O_CREAT, DENY_NONE);
132 0 : if (state->fnum == -1) {
133 0 : printf("Failed to open %s on connection %d\n", FNAME, state->client_num);
134 0 : exit(1);
135 : }
136 :
137 0 : num_connected++;
138 :
139 0 : DEBUG(0,("reconnect to %s finished (%u connected)\n", state->dest_host,
140 : num_connected));
141 :
142 0 : state->stage = LOCK_INITIAL;
143 0 : lock_send(state);
144 0 : }
145 :
146 : /*
147 : complete an async reconnect
148 : */
149 0 : static void reopen_connection_complete(struct composite_context *ctx)
150 : {
151 0 : struct benchlock_state *state = (struct benchlock_state *)ctx->async.private_data;
152 0 : NTSTATUS status;
153 0 : struct smb_composite_connect *io = &state->reconnect;
154 :
155 0 : status = smb_composite_connect_recv(ctx, state->mem_ctx);
156 0 : if (!NT_STATUS_IS_OK(status)) {
157 0 : talloc_free(state->te);
158 0 : state->te = tevent_add_timer(state->ev, state->mem_ctx,
159 : timeval_current_ofs(1,0),
160 : reopen_connection, state);
161 0 : return;
162 : }
163 :
164 0 : talloc_free(state->tree);
165 0 : state->tree = io->out.tree;
166 :
167 : /* do the reopen as a separate event */
168 0 : tevent_add_timer(state->ev, state->mem_ctx, timeval_zero(), reopen_file, state);
169 : }
170 :
171 :
172 :
173 : /*
174 : reopen a connection
175 : */
176 0 : static void reopen_connection(struct tevent_context *ev, struct tevent_timer *te,
177 : struct timeval t, void *private_data)
178 : {
179 0 : struct benchlock_state *state = (struct benchlock_state *)private_data;
180 0 : struct composite_context *ctx;
181 0 : struct smb_composite_connect *io = &state->reconnect;
182 0 : char *host, *share;
183 :
184 0 : state->te = NULL;
185 :
186 0 : if (!torture_get_conn_index(state->client_num, state->mem_ctx, state->tctx, &host, &share)) {
187 0 : DEBUG(0,("Can't find host/share for reconnect?!\n"));
188 0 : exit(1);
189 : }
190 :
191 0 : io->in.dest_host = state->dest_host;
192 0 : io->in.dest_ports = state->dest_ports;
193 0 : io->in.gensec_settings = lpcfg_gensec_settings(state->mem_ctx, state->tctx->lp_ctx);
194 0 : io->in.socket_options = lpcfg_socket_options(state->tctx->lp_ctx);
195 0 : io->in.called_name = state->called_name;
196 0 : io->in.service = share;
197 0 : io->in.service_type = state->service_type;
198 0 : io->in.credentials = samba_cmdline_get_creds();
199 0 : io->in.fallback_to_anonymous = false;
200 0 : io->in.workgroup = lpcfg_workgroup(state->tctx->lp_ctx);
201 0 : lpcfg_smbcli_options(state->tctx->lp_ctx, &io->in.options);
202 0 : lpcfg_smbcli_session_options(state->tctx->lp_ctx, &io->in.session_options);
203 :
204 : /* kill off the remnants of the old connection */
205 0 : talloc_free(state->tree);
206 0 : state->tree = NULL;
207 :
208 0 : ctx = smb_composite_connect_send(io, state->mem_ctx,
209 0 : lpcfg_resolve_context(state->tctx->lp_ctx),
210 : state->ev);
211 0 : if (ctx == NULL) {
212 0 : DEBUG(0,("Failed to setup async reconnect\n"));
213 0 : exit(1);
214 : }
215 :
216 0 : ctx->async.fn = reopen_connection_complete;
217 0 : ctx->async.private_data = state;
218 0 : }
219 :
220 :
221 : /*
222 : called when a lock completes
223 : */
224 0 : static void lock_completion(struct smbcli_request *req)
225 : {
226 0 : struct benchlock_state *state = (struct benchlock_state *)req->async.private_data;
227 0 : NTSTATUS status = smbcli_request_simple_recv(req);
228 0 : state->req = NULL;
229 0 : if (!NT_STATUS_IS_OK(status)) {
230 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
231 0 : NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) ||
232 0 : NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
233 0 : talloc_free(state->tree);
234 0 : state->tree = NULL;
235 0 : num_connected--;
236 0 : DEBUG(0,("reopening connection to %s\n", 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 : } else {
242 0 : DEBUG(0,("Lock failed - %s\n", nt_errstr(status)));
243 0 : lock_failed++;
244 : }
245 0 : return;
246 : }
247 :
248 0 : switch (state->stage) {
249 0 : case LOCK_INITIAL:
250 0 : state->stage = LOCK_LOCK;
251 0 : break;
252 0 : case LOCK_LOCK:
253 0 : state->stage = LOCK_UNLOCK;
254 0 : break;
255 0 : case LOCK_UNLOCK:
256 0 : state->stage = LOCK_LOCK;
257 0 : break;
258 : }
259 :
260 0 : state->count++;
261 0 : lock_send(state);
262 : }
263 :
264 :
265 0 : static void echo_completion(struct smbcli_request *req)
266 : {
267 0 : struct benchlock_state *state = (struct benchlock_state *)req->async.private_data;
268 0 : NTSTATUS status = smbcli_request_simple_recv(req);
269 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
270 0 : NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) ||
271 0 : NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
272 0 : talloc_free(state->tree);
273 0 : state->tree = NULL;
274 0 : num_connected--;
275 0 : DEBUG(0,("reopening connection to %s\n", state->dest_host));
276 0 : talloc_free(state->te);
277 0 : state->te = tevent_add_timer(state->ev, state->mem_ctx,
278 : timeval_current_ofs(1,0),
279 : reopen_connection, state);
280 : }
281 0 : }
282 :
283 0 : static void report_rate(struct tevent_context *ev, struct tevent_timer *te,
284 : struct timeval t, void *private_data)
285 : {
286 0 : struct benchlock_state *state = talloc_get_type(private_data,
287 : struct benchlock_state);
288 0 : int i;
289 0 : for (i=0;i<nprocs;i++) {
290 0 : printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
291 0 : state[i].lastcount = state[i].count;
292 : }
293 0 : printf("\r");
294 0 : fflush(stdout);
295 0 : tevent_add_timer(ev, state, timeval_current_ofs(1, 0), report_rate, state);
296 :
297 : /* send an echo on each interface to ensure it stays alive - this helps
298 : with IP takeover */
299 0 : for (i=0;i<nprocs;i++) {
300 0 : struct smb_echo p;
301 0 : struct smbcli_request *req;
302 :
303 0 : if (!state[i].tree) {
304 0 : continue;
305 : }
306 :
307 0 : p.in.repeat_count = 1;
308 0 : p.in.size = 0;
309 0 : p.in.data = NULL;
310 0 : req = smb_raw_echo_send(state[i].tree->session->transport, &p);
311 0 : req->async.private_data = &state[i];
312 0 : req->async.fn = echo_completion;
313 : }
314 0 : }
315 :
316 : /*
317 : benchmark locking calls
318 : */
319 0 : bool torture_bench_lock(struct torture_context *torture)
320 : {
321 0 : bool ret = true;
322 0 : TALLOC_CTX *mem_ctx = talloc_new(torture);
323 0 : int i, j;
324 0 : int timelimit = torture_setting_int(torture, "timelimit", 10);
325 0 : struct timeval tv;
326 0 : struct benchlock_state *state;
327 0 : int total = 0, minops=0;
328 0 : struct smbcli_state *cli;
329 0 : bool progress;
330 0 : off_t offset;
331 0 : int initial_locks = torture_setting_int(torture, "initial_locks", 0);
332 :
333 0 : progress = torture_setting_bool(torture, "progress", true);
334 :
335 0 : nprocs = torture_setting_int(torture, "nprocs", 4);
336 :
337 0 : state = talloc_zero_array(mem_ctx, struct benchlock_state, nprocs);
338 :
339 0 : printf("Opening %d connections\n", nprocs);
340 0 : for (i=0;i<nprocs;i++) {
341 0 : const struct sockaddr_storage *dest_ss;
342 0 : char addrstr[INET6_ADDRSTRLEN];
343 0 : const char *dest_str;
344 0 : uint16_t dest_port;
345 :
346 0 : state[i].tctx = torture;
347 0 : state[i].mem_ctx = talloc_new(state);
348 0 : state[i].client_num = i;
349 0 : state[i].ev = torture->ev;
350 0 : if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) {
351 0 : return false;
352 : }
353 0 : talloc_steal(state[i].mem_ctx, cli);
354 0 : state[i].tree = cli->tree;
355 :
356 0 : dest_ss = smbXcli_conn_remote_sockaddr(
357 0 : state[i].tree->session->transport->conn);
358 0 : dest_str = print_sockaddr(addrstr, sizeof(addrstr), dest_ss);
359 0 : dest_port = get_sockaddr_port(dest_ss);
360 :
361 0 : state[i].dest_host = talloc_strdup(state[i].mem_ctx, dest_str);
362 0 : state[i].dest_ports = talloc_array(state[i].mem_ctx,
363 : const char *, 2);
364 0 : state[i].dest_ports[0] = talloc_asprintf(state[i].dest_ports,
365 : "%u", dest_port);
366 0 : state[i].dest_ports[1] = NULL;
367 0 : state[i].called_name = talloc_strdup(state[i].mem_ctx,
368 0 : smbXcli_conn_remote_name(cli->tree->session->transport->conn));
369 0 : state[i].service_type = talloc_strdup(state[i].mem_ctx, "?????");
370 : }
371 :
372 0 : num_connected = i;
373 :
374 0 : if (!torture_setup_dir(cli, BASEDIR)) {
375 0 : goto failed;
376 : }
377 :
378 0 : for (i=0;i<nprocs;i++) {
379 0 : state[i].fnum = smbcli_open(state[i].tree,
380 : FNAME,
381 : O_RDWR|O_CREAT, DENY_NONE);
382 0 : if (state[i].fnum == -1) {
383 0 : printf("Failed to open %s on connection %d\n", FNAME, i);
384 0 : goto failed;
385 : }
386 :
387 : /* Optionally, lock initial_locks for each proc beforehand. */
388 0 : if (i == 0 && initial_locks > 0) {
389 0 : printf("Initializing %d locks on each proc.\n",
390 : initial_locks);
391 : }
392 :
393 0 : for (j = 0; j < initial_locks; j++) {
394 0 : offset = (0xFFFFFED8LLU * (i+2)) + j;
395 0 : if (!NT_STATUS_IS_OK(smbcli_lock64(state[i].tree,
396 : state[i].fnum, offset, 1, 0, WRITE_LOCK))) {
397 0 : printf("Failed initializing, lock=%d\n", j);
398 0 : goto failed;
399 : }
400 : }
401 :
402 0 : state[i].stage = LOCK_INITIAL;
403 0 : lock_send(&state[i]);
404 : }
405 :
406 0 : tv = timeval_current();
407 :
408 0 : if (progress) {
409 0 : tevent_add_timer(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
410 : }
411 :
412 0 : printf("Running for %d seconds\n", timelimit);
413 0 : while (timeval_elapsed(&tv) < timelimit) {
414 0 : tevent_loop_once(torture->ev);
415 :
416 0 : if (lock_failed) {
417 0 : DEBUG(0,("locking failed\n"));
418 0 : goto failed;
419 : }
420 : }
421 :
422 0 : printf("%.2f ops/second\n", total/timeval_elapsed(&tv));
423 0 : minops = state[0].count;
424 0 : for (i=0;i<nprocs;i++) {
425 0 : printf("[%d] %u ops\n", i, state[i].count);
426 0 : if (state[i].count < minops) minops = state[i].count;
427 : }
428 0 : if (minops < 0.5*total/nprocs) {
429 0 : printf("Failed: unbalanced locking\n");
430 0 : goto failed;
431 : }
432 :
433 0 : for (i=0;i<nprocs;i++) {
434 0 : talloc_free(state[i].req);
435 0 : smb_raw_exit(state[i].tree->session);
436 : }
437 :
438 0 : smbcli_deltree(state[0].tree, BASEDIR);
439 0 : talloc_free(mem_ctx);
440 0 : printf("\n");
441 0 : return ret;
442 :
443 0 : failed:
444 0 : smbcli_deltree(state[0].tree, BASEDIR);
445 0 : talloc_free(mem_ctx);
446 0 : return false;
447 : }
|