root/source4/torture/raw/lockbench.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lock_send
  2. reopen_file
  3. reopen_connection_complete
  4. reopen_connection
  5. lock_completion
  6. echo_completion
  7. report_rate
  8. torture_bench_lock

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

/* [<][>][^][v][top][bottom][index][help] */