r24767: add a torture test that performs "ping-pong" that works the same way as
authorRonnie Sahlberg <sahlberg@samba.org>
Wed, 29 Aug 2007 05:45:24 +0000 (05:45 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 20:03:07 +0000 (15:03 -0500)
ping-pong.c

this is a manual test and only makes sense to be used in parallell with
the real ping-pong.c tool

source/samba4-skip
source/torture/config.mk
source/torture/raw/pingpong.c [new file with mode: 0755]
source/torture/raw/raw.c

index f72f4a97914aafd98edcb9038504b1208d03cb2a..678f1a159135e890a97b19a22757b6f04c96e7c4 100644 (file)
@@ -11,6 +11,7 @@ BASE-BENCH-HOLDCON
 BASE-SCAN-MAXFID
 RAW-BENCH-OPLOCK
 RAW-HOLD-OPLOCK
+RAW-PING-PONG
 RAW-SAMBA3HIDE
 RAW-SAMBA3CLOSEERR
 RAW-SAMBA3CHECKFSP
index 2afb94cf1b4206343efcd46ddd1e50ed3c264388..fc85cc160aed50726d81a4fbcaefb7c23bd1a7db 100644 (file)
@@ -77,6 +77,7 @@ OBJ_FILES = \
                raw/context.o \
                raw/write.o \
                raw/lock.o \
+               raw/pingpong.o \
                raw/lockbench.o \
                raw/openbench.o \
                raw/rename.o \
diff --git a/source/torture/raw/pingpong.c b/source/torture/raw/pingpong.c
new file mode 100755 (executable)
index 0000000..a3ee5b5
--- /dev/null
@@ -0,0 +1,377 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   ping pong test
+   filename is specified by
+        --option=torture:filename=...
+
+   number of locks is specified by
+        --option=torture:num_locks=...
+
+   locktimeout is specified in ms by
+        --option=torture:locktimeout=...
+
+       default is 100 seconds
+       if set to 0 pingpong will instead loop trying the lock operation
+       over and over until it completes.
+
+   reading from the file can be enabled with
+        --option=torture:read=True
+
+   writing to the file can be enabled with
+        --option=torture:write=True
+
+
+   Copyright (C) Ronnie Sahlberg
+
+   Significantly based on and borrowed from lockbench.c by
+   Copyright (C) Andrew Tridgell 2006
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+
+static BOOL do_reads;
+static BOOL do_writes;
+
+static int lock_failed;
+
+enum lock_stage {LOCK_INITIAL, LOCK_LOCK, LOCK_UNLOCK};
+
+struct pingpong_state {
+       struct event_context *ev;
+       struct smbcli_tree *tree;
+       TALLOC_CTX *mem_ctx;
+       int fnum;
+       enum lock_stage stage;
+       int num_locks;
+       int lock_offset;
+       int unlock_offset;
+       struct smbcli_request *req;
+       int count;
+       int lastcount;
+       int lock_timeout;
+       uint8_t c;
+       uint8_t incr;
+       uint8_t last_incr;
+       uint8_t *val;
+};
+
+
+
+static void lock_completion(struct smbcli_request *);
+
+/*
+  send the next lock request
+*/
+static void lock_send(struct pingpong_state *state, BOOL retry_lock)
+{
+       union smb_lock io;
+       struct smb_lock_entry lock;
+
+
+       /* we have completed one lock/unlock pair */
+       if (state->stage == LOCK_UNLOCK) {
+               if ( (state->count > state->num_locks)
+                  &&(state->incr != state->last_incr) ) {
+                       state->last_incr = state->incr;
+                       printf("data increment = %u\n", state->incr);
+               }
+       }
+
+       switch (state->stage) {
+       case LOCK_INITIAL:
+               io.lockx.in.ulock_cnt = 0;
+               io.lockx.in.lock_cnt = 1;
+               state->lock_offset = -1;
+               state->unlock_offset = 0;
+               lock.offset = (state->lock_offset+1)%state->num_locks;
+               break;
+       case LOCK_LOCK:
+               io.lockx.in.ulock_cnt = 0;
+               io.lockx.in.lock_cnt = 1;
+               if (!retry_lock) {
+                       state->lock_offset = (state->lock_offset+1)%state->num_locks;
+               }
+               lock.offset = (state->lock_offset+1)%state->num_locks;
+               break;
+       case LOCK_UNLOCK:
+               state->count++;
+               io.lockx.in.ulock_cnt = 1;
+               io.lockx.in.lock_cnt = 0;
+               lock.offset = state->lock_offset;
+               break;
+       }
+
+       lock.count = 1;
+       lock.pid = state->tree->session->pid;
+
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.timeout = state->lock_timeout;
+       io.lockx.in.locks = &lock;
+       io.lockx.in.file.fnum = state->fnum;
+
+       state->req = smb_raw_lock_send(state->tree, &io);
+       if (state->req == NULL) {
+               DEBUG(0,("Failed to setup lock\n"));
+               lock_failed++;
+       }
+       state->req->async.private = state;
+       state->req->async.fn      = lock_completion;
+}
+
+
+/*
+  called when a write completes
+*/
+static void write_completion(struct smbcli_request *req)
+{
+       struct pingpong_state *state = (struct pingpong_state *)req->async.private;
+       NTSTATUS status = smbcli_request_simple_recv(req);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("write failed\n");
+               exit(1);
+       }
+
+       lock_send(state, False);
+}
+
+
+static void write_send(struct pingpong_state *state) 
+{
+       union smb_write io;
+
+       io.generic.level = RAW_WRITE_WRITEX;
+       io.writex.in.file.fnum = state->fnum;
+       io.writex.in.offset = state->lock_offset;
+       io.writex.in.wmode = 0;
+       io.writex.in.remaining = 0;
+       io.writex.in.count = 1;
+       state->c = state->val[state->lock_offset]+1;
+       io.writex.in.data = &state->c;
+
+       state->req = smb_raw_write_send(state->tree, &io);
+       if (state->req == NULL) {
+               DEBUG(0,("Failed to setup write\n"));
+               exit(1);
+       }
+       state->req->async.private = state;
+       state->req->async.fn      = write_completion;
+}
+       
+
+/*
+  called when a read completes
+*/
+static void read_completion(struct smbcli_request *req)
+{
+       struct pingpong_state *state = (struct pingpong_state *)req->async.private;
+       NTSTATUS status = smbcli_request_simple_recv(req);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("read failed\n");
+               exit(1);
+       }
+
+       state->req = NULL;
+
+       state->incr = state->c - state->val[state->lock_offset];
+       state->val[state->lock_offset] = state->c;
+
+       /* a read just completed, now spawn off to the write handler, if 
+          write is enabled.   othervise spawn off to the lock handler
+          to proceed to unlock the previous lock
+       */
+       if (do_writes) {
+               write_send(state);
+               return;
+       }
+       lock_send(state, False);
+}
+
+
+static void read_send(struct pingpong_state *state) 
+{
+       union smb_read io;
+
+       io.generic.level = RAW_READ_READX;
+       io.readx.in.file.fnum = state->fnum;
+       io.readx.in.mincnt = 1;
+       io.readx.in.maxcnt = 1;
+       io.readx.in.offset = state->lock_offset;
+       io.readx.in.remaining = 0;
+       io.readx.in.read_for_execute = False;
+       io.readx.out.data = &state->c;
+
+       state->req = smb_raw_read_send(state->tree, &io);
+       if (state->req == NULL) {
+               DEBUG(0,("Failed to setup read\n"));
+               exit(1);
+       }
+       state->req->async.private = state;
+       state->req->async.fn      = read_completion;
+}
+
+
+/*
+  called when a lock completes
+*/
+static void lock_completion(struct smbcli_request *req)
+{
+       struct pingpong_state *state = (struct pingpong_state *)req->async.private;
+       NTSTATUS status = smbcli_request_simple_recv(req);
+       state->req = NULL;
+       /* If we dont use timeouts and we got file lock conflict
+          just try the lock again.
+       */
+       if (state->lock_timeout==0) {
+               if ( (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status))
+                  ||(NT_STATUS_EQUAL(NT_STATUS_LOCK_NOT_GRANTED, status)) ) {
+                       lock_send(state, True);
+                       return;
+               }
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               lock_failed++;
+               return;
+       }
+
+       switch (state->stage) {
+       case LOCK_INITIAL:
+               state->stage = LOCK_LOCK;
+               break;
+       case LOCK_LOCK:
+               state->stage = LOCK_UNLOCK;
+               break;
+       case LOCK_UNLOCK:
+               state->stage = LOCK_LOCK;
+               break;
+       }
+
+       /* if we just completed a lock  and we have read enabled
+          then spawn off to the read handler instead of sending an unlock
+       */
+       if ( (state->stage == LOCK_UNLOCK)
+          &&(do_reads) ){
+               read_send(state);
+               return;
+       }
+       /* if we just completed a lock  and we we didnt have reads enabled
+          but we do have write enabled, thenspawn off to the write handler
+          instead of sending an unlock
+       */
+       if ( (state->stage == LOCK_UNLOCK)
+          &&(do_writes) ){
+               write_send(state);
+               return;
+       }
+
+       lock_send(state, False);
+}
+
+static void report_rate(struct event_context *ev, struct timed_event *te, 
+                       struct timeval t, void *private_data)
+{
+       struct pingpong_state *state = talloc_get_type(private_data, 
+                                                       struct pingpong_state);
+       printf("%5u ", 2*(unsigned)(state->count - state->lastcount));
+       state->lastcount = state->count;
+
+       printf("\r");
+       fflush(stdout);
+       event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
+}
+
+/* 
+   ping pong
+*/
+BOOL torture_ping_pong(struct torture_context *torture)
+{
+       const char *fn;
+       TALLOC_CTX *mem_ctx = talloc_new(torture);
+       struct event_context *ev = event_context_find(mem_ctx);
+       int timelimit = torture_setting_int(torture, "timelimit", 10);
+       struct timeval tv;
+       struct pingpong_state *state;
+       struct smbcli_state *cli;
+       int num_locks;
+
+       fn = lp_parm_string(-1, "torture", "filename");
+       if (fn == NULL) {
+               DEBUG(0,("You must specify the filename using --option=torture:filename=...\n"));
+               return false;
+       }
+
+       num_locks = lp_parm_int(-1, "torture", "num_locks", -1);
+       if (num_locks == -1) {
+               DEBUG(0,("You must specify num_locks using --option=torture:num_locks=...\n"));
+               return false;
+       }
+
+       do_reads  = lp_parm_bool(-1, "torture", "read", False);
+       do_writes = lp_parm_bool(-1, "torture", "write", False);
+
+       if (!torture_open_connection_ev(&cli, 0, ev)) {
+               DEBUG(0,("Could not open connection to share\n"));
+               return False;
+       }
+
+       
+       state            = talloc_zero(mem_ctx, struct pingpong_state);
+       state->ev        = ev;
+       state->tree      = cli->tree;
+       state->num_locks = num_locks;
+       state->lock_timeout =  lp_parm_int(-1, "torture", "lock_timeout", 100000);
+       state->fnum      = smbcli_open(state->tree, fn, O_RDWR|O_CREAT, DENY_NONE);
+       if (state->fnum == -1) {
+               printf("Failed to open %s\n", fn);
+               exit(1);
+       }
+       state->val = talloc_zero_array(state, uint8_t, state->num_locks+1);
+       state->stage = LOCK_INITIAL;
+       lock_send(state, False);
+
+       tv = timeval_current(); 
+
+       printf("Running for %d seconds\n", timelimit);
+       event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
+       while (timeval_elapsed(&tv) < timelimit) {
+               event_loop_once(ev);
+
+               if (lock_failed) {
+                       DEBUG(0,("locking failed\n"));
+                       goto failed;
+               }
+       }
+
+
+failed:
+       talloc_free(mem_ctx);
+       return true;
+}
index d85f26703501d1672fffebb6ec20b6809d054117..598241b178d85efb18041eaff443bd3980ce1310 100644 (file)
@@ -30,6 +30,7 @@ NTSTATUS torture_raw_init(void)
                "RAW");
        /* RAW smb tests */
        torture_suite_add_simple_test(suite, "BENCH-OPLOCK", torture_bench_oplock);
+       torture_suite_add_simple_test(suite, "PING-PONG", torture_ping_pong);
        torture_suite_add_simple_test(suite, "BENCH-LOCK", torture_bench_lock);
        torture_suite_add_simple_test(suite, "BENCH-OPEN", torture_bench_open);
        torture_suite_add_1smb_test(suite, "QFSINFO", torture_raw_qfsinfo);