2006-12-09 06:16:17 +03:00
/*
Unix SMB / CIFS implementation .
locking benchmark
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2006-12-09 06:16:17 +03:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-12-09 06:16:17 +03:00
*/
# include "includes.h"
# include "libcli/raw/libcliraw.h"
2008-04-02 06:53:27 +04:00
# include "libcli/raw/raw_proto.h"
2006-12-09 06:16:17 +03:00
# 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"
2007-05-14 07:49:42 +04:00
# include "libcli/composite/composite.h"
# include "libcli/smb_composite/smb_composite.h"
2008-01-03 03:39:01 +03:00
# include "libcli/resolve/resolve.h"
2007-09-08 16:42:09 +04:00
# include "param/param.h"
2011-03-19 02:42:42 +03:00
# include "torture/raw/proto.h"
2006-12-09 06:16:17 +03:00
# define BASEDIR "\\benchlock"
# define FNAME BASEDIR "\\lock.dat"
static int nprocs ;
static int lock_failed ;
2007-05-15 09:42:16 +04:00
static int num_connected ;
2006-12-09 06:16:17 +03:00
2007-05-17 07:42:28 +04:00
enum lock_stage { LOCK_INITIAL , LOCK_LOCK , LOCK_UNLOCK } ;
2006-12-09 06:16:17 +03:00
struct benchlock_state {
2007-12-03 17:53:07 +03:00
struct torture_context * tctx ;
2008-12-29 22:24:57 +03:00
struct tevent_context * ev ;
2007-05-14 07:49:42 +04:00
struct smbcli_tree * tree ;
TALLOC_CTX * mem_ctx ;
2007-05-14 05:05:09 +04:00
int client_num ;
2006-12-09 06:16:17 +03:00
int fnum ;
2007-05-17 07:42:28 +04:00
enum lock_stage stage ;
int lock_offset ;
int unlock_offset ;
2006-12-09 06:16:17 +03:00
int count ;
2007-05-17 06:22:29 +04:00
int lastcount ;
2006-12-09 06:16:17 +03:00
struct smbcli_request * req ;
2007-05-14 07:49:42 +04:00
struct smb_composite_connect reconnect ;
2008-12-29 22:24:57 +03:00
struct tevent_timer * te ;
2007-05-14 07:49:42 +04:00
/* these are used for reconnections */
2008-01-04 02:21:50 +03:00
const char * * dest_ports ;
2007-05-14 07:49:42 +04:00
const char * dest_host ;
const char * called_name ;
const char * service_type ;
2006-12-09 06:16:17 +03:00
} ;
static void lock_completion ( struct smbcli_request * ) ;
/*
send the next lock request
*/
static void lock_send ( struct benchlock_state * state )
{
2007-05-17 07:42:28 +04:00
union smb_lock io ;
struct smb_lock_entry lock ;
switch ( state - > stage ) {
case LOCK_INITIAL :
io . lockx . in . ulock_cnt = 0 ;
io . lockx . in . lock_cnt = 1 ;
state - > lock_offset = 0 ;
state - > unlock_offset = 0 ;
lock . offset = state - > lock_offset ;
break ;
case LOCK_LOCK :
io . lockx . in . ulock_cnt = 0 ;
io . lockx . in . lock_cnt = 1 ;
state - > lock_offset = ( state - > lock_offset + 1 ) % ( nprocs + 1 ) ;
lock . offset = state - > lock_offset ;
break ;
case LOCK_UNLOCK :
io . lockx . in . ulock_cnt = 1 ;
io . lockx . in . lock_cnt = 0 ;
lock . offset = state - > unlock_offset ;
state - > unlock_offset = ( state - > unlock_offset + 1 ) % ( nprocs + 1 ) ;
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 = 100000 ;
io . lockx . in . locks = & lock ;
io . lockx . in . file . fnum = state - > fnum ;
state - > req = smb_raw_lock_send ( state - > tree , & io ) ;
2006-12-09 06:16:17 +03:00
if ( state - > req = = NULL ) {
DEBUG ( 0 , ( " Failed to setup lock \n " ) ) ;
lock_failed + + ;
}
2009-02-02 12:17:00 +03:00
state - > req - > async . private_data = state ;
2006-12-09 06:16:17 +03:00
state - > req - > async . fn = lock_completion ;
}
2008-12-29 22:24:57 +03:00
static void reopen_connection ( struct tevent_context * ev , struct tevent_timer * te ,
2007-05-15 07:00:58 +04:00
struct timeval t , void * private_data ) ;
2007-05-14 07:49:42 +04:00
2008-12-29 22:24:57 +03:00
static void reopen_file ( struct tevent_context * ev , struct tevent_timer * te ,
2007-05-14 07:49:42 +04:00
struct timeval t , void * private_data )
2007-05-14 05:05:09 +04:00
{
2007-05-14 07:49:42 +04:00
struct benchlock_state * state = ( struct benchlock_state * ) private_data ;
/* reestablish our open file */
state - > fnum = smbcli_open ( state - > tree , FNAME , O_RDWR | O_CREAT , DENY_NONE ) ;
2007-05-14 05:05:09 +04:00
if ( state - > fnum = = - 1 ) {
printf ( " Failed to open %s on connection %d \n " , FNAME , state - > client_num ) ;
exit ( 1 ) ;
}
2007-05-15 09:42:16 +04:00
num_connected + + ;
DEBUG ( 0 , ( " reconnect to %s finished (%u connected) \n " , state - > dest_host ,
num_connected ) ) ;
2007-05-17 07:42:28 +04:00
state - > stage = LOCK_INITIAL ;
lock_send ( state ) ;
2007-05-14 05:05:09 +04:00
}
2007-05-14 07:49:42 +04:00
/*
complete an async reconnect
*/
static void reopen_connection_complete ( struct composite_context * ctx )
{
struct benchlock_state * state = ( struct benchlock_state * ) ctx - > async . private_data ;
NTSTATUS status ;
struct smb_composite_connect * io = & state - > reconnect ;
status = smb_composite_connect_recv ( ctx , state - > mem_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2007-05-29 12:22:45 +04:00
talloc_free ( state - > te ) ;
2010-05-25 23:23:55 +04:00
state - > te = tevent_add_timer ( state - > ev , state - > mem_ctx ,
2007-05-29 12:22:45 +04:00
timeval_current_ofs ( 1 , 0 ) ,
reopen_connection , state ) ;
2007-05-14 07:49:42 +04:00
return ;
}
talloc_free ( state - > tree ) ;
state - > tree = io - > out . tree ;
/* do the reopen as a separate event */
2010-05-25 23:23:55 +04:00
tevent_add_timer ( state - > ev , state - > mem_ctx , timeval_zero ( ) , reopen_file , state ) ;
2007-05-14 07:49:42 +04:00
}
/*
2007-05-15 07:00:58 +04:00
reopen a connection
2007-05-14 07:49:42 +04:00
*/
2008-12-29 22:24:57 +03:00
static void reopen_connection ( struct tevent_context * ev , struct tevent_timer * te ,
2007-05-15 07:00:58 +04:00
struct timeval t , void * private_data )
2007-05-14 07:49:42 +04:00
{
2007-05-15 07:00:58 +04:00
struct benchlock_state * state = ( struct benchlock_state * ) private_data ;
2007-05-14 07:49:42 +04:00
struct composite_context * ctx ;
struct smb_composite_connect * io = & state - > reconnect ;
char * host , * share ;
2007-05-29 12:30:41 +04:00
state - > te = NULL ;
2007-12-03 17:53:07 +03:00
if ( ! torture_get_conn_index ( state - > client_num , state - > mem_ctx , state - > tctx , & host , & share ) ) {
2007-05-14 07:49:42 +04:00
DEBUG ( 0 , ( " Can't find host/share for reconnect?! \n " ) ) ;
exit ( 1 ) ;
}
io - > in . dest_host = state - > dest_host ;
2008-01-04 02:21:50 +03:00
io - > in . dest_ports = state - > dest_ports ;
2010-07-16 08:32:42 +04:00
io - > in . gensec_settings = lpcfg_gensec_settings ( state - > mem_ctx , state - > tctx - > lp_ctx ) ;
io - > in . socket_options = lpcfg_socket_options ( state - > tctx - > lp_ctx ) ;
2007-05-14 07:49:42 +04:00
io - > in . called_name = state - > called_name ;
io - > in . service = share ;
io - > in . service_type = state - > service_type ;
io - > in . credentials = cmdline_credentials ;
2007-10-07 02:28:14 +04:00
io - > in . fallback_to_anonymous = false ;
2010-07-16 08:32:42 +04:00
io - > in . workgroup = lpcfg_workgroup ( state - > tctx - > lp_ctx ) ;
lpcfg_smbcli_options ( state - > tctx - > lp_ctx , & io - > in . options ) ;
lpcfg_smbcli_session_options ( state - > tctx - > lp_ctx , & io - > in . session_options ) ;
2007-05-14 07:49:42 +04:00
/* kill off the remnants of the old connection */
talloc_free ( state - > tree ) ;
state - > tree = NULL ;
2008-01-03 03:39:01 +03:00
ctx = smb_composite_connect_send ( io , state - > mem_ctx ,
2010-07-16 08:32:42 +04:00
lpcfg_resolve_context ( state - > tctx - > lp_ctx ) ,
2008-01-03 03:39:01 +03:00
state - > ev ) ;
2007-05-14 07:49:42 +04:00
if ( ctx = = NULL ) {
DEBUG ( 0 , ( " Failed to setup async reconnect \n " ) ) ;
exit ( 1 ) ;
}
ctx - > async . fn = reopen_connection_complete ;
ctx - > async . private_data = state ;
}
2006-12-09 06:16:17 +03:00
/*
called when a lock completes
*/
static void lock_completion ( struct smbcli_request * req )
{
2009-02-02 12:17:00 +03:00
struct benchlock_state * state = ( struct benchlock_state * ) req - > async . private_data ;
2006-12-09 06:16:17 +03:00
NTSTATUS status = smbcli_request_simple_recv ( req ) ;
2007-01-19 06:52:21 +03:00
state - > req = NULL ;
2006-12-09 06:16:17 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2007-05-29 11:32:28 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_END_OF_FILE ) | |
2010-02-04 12:19:05 +03:00
NT_STATUS_EQUAL ( status , NT_STATUS_LOCAL_DISCONNECT ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_CONNECTION_RESET ) ) {
2007-05-15 09:42:16 +04:00
talloc_free ( state - > tree ) ;
state - > tree = NULL ;
num_connected - - ;
DEBUG ( 0 , ( " reopening connection to %s \n " , state - > dest_host ) ) ;
2007-05-29 12:22:45 +04:00
talloc_free ( state - > te ) ;
2010-05-25 23:23:55 +04:00
state - > te = tevent_add_timer ( state - > ev , state - > mem_ctx ,
2007-05-29 12:22:45 +04:00
timeval_current_ofs ( 1 , 0 ) ,
reopen_connection , state ) ;
2007-05-14 07:49:42 +04:00
} else {
DEBUG ( 0 , ( " Lock failed - %s \n " , nt_errstr ( status ) ) ) ;
lock_failed + + ;
2007-05-14 05:05:09 +04:00
}
2007-05-17 07:42:28 +04:00
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 ;
2006-12-09 06:16:17 +03:00
}
2007-05-17 07:42:28 +04:00
state - > count + + ;
lock_send ( state ) ;
2006-12-09 06:16:17 +03:00
}
2007-05-17 06:22:29 +04:00
2007-05-25 16:21:29 +04:00
static void echo_completion ( struct smbcli_request * req )
{
2009-02-02 12:17:00 +03:00
struct benchlock_state * state = ( struct benchlock_state * ) req - > async . private_data ;
2007-05-25 16:21:29 +04:00
NTSTATUS status = smbcli_request_simple_recv ( req ) ;
2007-05-29 11:32:28 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_END_OF_FILE ) | |
2010-02-04 12:19:05 +03:00
NT_STATUS_EQUAL ( status , NT_STATUS_LOCAL_DISCONNECT ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_CONNECTION_RESET ) ) {
2007-05-25 16:21:29 +04:00
talloc_free ( state - > tree ) ;
state - > tree = NULL ;
num_connected - - ;
DEBUG ( 0 , ( " reopening connection to %s \n " , state - > dest_host ) ) ;
2007-05-29 12:22:45 +04:00
talloc_free ( state - > te ) ;
2010-05-25 23:23:55 +04:00
state - > te = tevent_add_timer ( state - > ev , state - > mem_ctx ,
2007-05-29 12:22:45 +04:00
timeval_current_ofs ( 1 , 0 ) ,
reopen_connection , state ) ;
2007-05-25 16:21:29 +04:00
}
}
2008-12-29 22:24:57 +03:00
static void report_rate ( struct tevent_context * ev , struct tevent_timer * te ,
2007-05-17 06:22:29 +04:00
struct timeval t , void * private_data )
{
struct benchlock_state * state = talloc_get_type ( private_data ,
struct benchlock_state ) ;
int i ;
for ( i = 0 ; i < nprocs ; i + + ) {
printf ( " %5u " , ( unsigned ) ( state [ i ] . count - state [ i ] . lastcount ) ) ;
state [ i ] . lastcount = state [ i ] . count ;
}
printf ( " \r " ) ;
fflush ( stdout ) ;
2010-05-25 23:23:55 +04:00
tevent_add_timer ( ev , state , timeval_current_ofs ( 1 , 0 ) , report_rate , state ) ;
2007-05-25 14:43:06 +04:00
/* send an echo on each interface to ensure it stays alive - this helps
with IP takeover */
for ( i = 0 ; i < nprocs ; i + + ) {
struct smb_echo p ;
2007-05-25 16:21:29 +04:00
struct smbcli_request * req ;
2007-05-25 16:35:03 +04:00
if ( ! state [ i ] . tree ) {
continue ;
}
2007-05-25 16:21:29 +04:00
p . in . repeat_count = 1 ;
2007-05-25 14:43:06 +04:00
p . in . size = 0 ;
p . in . data = NULL ;
2007-05-25 16:21:29 +04:00
req = smb_raw_echo_send ( state [ i ] . tree - > session - > transport , & p ) ;
2009-02-02 12:17:00 +03:00
req - > async . private_data = & state [ i ] ;
2007-05-25 16:21:29 +04:00
req - > async . fn = echo_completion ;
2007-05-25 14:43:06 +04:00
}
2007-05-17 06:22:29 +04:00
}
2006-12-09 06:16:17 +03:00
/*
benchmark locking calls
*/
2007-10-07 02:28:14 +04:00
bool torture_bench_lock ( struct torture_context * torture )
2006-12-09 06:16:17 +03:00
{
2007-10-07 02:28:14 +04:00
bool ret = true ;
2006-12-09 06:16:17 +03:00
TALLOC_CTX * mem_ctx = talloc_new ( torture ) ;
2009-04-02 04:46:40 +04:00
int i , j ;
2006-12-09 06:16:17 +03:00
int timelimit = torture_setting_int ( torture , " timelimit " , 10 ) ;
struct timeval tv ;
struct benchlock_state * state ;
2007-05-17 06:22:29 +04:00
int total = 0 , minops = 0 ;
2007-05-14 07:49:42 +04:00
struct smbcli_state * cli ;
2007-05-17 06:22:29 +04:00
bool progress ;
2009-04-02 04:46:40 +04:00
off_t offset ;
int initial_locks = torture_setting_int ( torture , " initial_locks " , 0 ) ;
2007-05-17 06:22:29 +04:00
progress = torture_setting_bool ( torture , " progress " , true ) ;
2007-12-03 02:28:22 +03:00
nprocs = torture_setting_int ( torture , " nprocs " , 4 ) ;
2006-12-09 06:16:17 +03:00
state = talloc_zero_array ( mem_ctx , struct benchlock_state , nprocs ) ;
printf ( " Opening %d connections \n " , nprocs ) ;
for ( i = 0 ; i < nprocs ; i + + ) {
2007-12-03 17:53:07 +03:00
state [ i ] . tctx = torture ;
2007-05-14 07:49:42 +04:00
state [ i ] . mem_ctx = talloc_new ( state ) ;
2007-05-14 05:05:09 +04:00
state [ i ] . client_num = i ;
2008-04-17 03:19:20 +04:00
state [ i ] . ev = torture - > ev ;
if ( ! torture_open_connection_ev ( & cli , i , torture , torture - > ev ) ) {
2007-10-07 02:28:14 +04:00
return false ;
2006-12-09 06:16:17 +03:00
}
talloc_steal ( mem_ctx , state ) ;
2007-05-14 07:49:42 +04:00
state [ i ] . tree = cli - > tree ;
state [ i ] . dest_host = talloc_strdup ( state [ i ] . mem_ctx ,
cli - > tree - > session - > transport - > socket - > hostname ) ;
2008-01-04 02:21:50 +03:00
state [ i ] . dest_ports = talloc_array ( state [ i ] . mem_ctx ,
const char * , 2 ) ;
state [ i ] . dest_ports [ 0 ] = talloc_asprintf ( state [ i ] . dest_ports ,
" %u " ,
cli - > tree - > session - > transport - > socket - > port ) ;
state [ i ] . dest_ports [ 1 ] = NULL ;
2007-05-14 07:49:42 +04:00
state [ i ] . called_name = talloc_strdup ( state [ i ] . mem_ctx ,
cli - > tree - > session - > transport - > called . name ) ;
state [ i ] . service_type = talloc_strdup ( state [ i ] . mem_ctx ,
cli - > tree - > device ) ;
2006-12-09 06:16:17 +03:00
}
2007-05-15 09:42:16 +04:00
num_connected = i ;
2007-05-14 07:49:42 +04:00
if ( ! torture_setup_dir ( cli , BASEDIR ) ) {
2006-12-09 06:16:17 +03:00
goto failed ;
}
for ( i = 0 ; i < nprocs ; i + + ) {
2007-05-14 07:49:42 +04:00
state [ i ] . fnum = smbcli_open ( state [ i ] . tree ,
2006-12-09 06:16:17 +03:00
FNAME ,
O_RDWR | O_CREAT , DENY_NONE ) ;
if ( state [ i ] . fnum = = - 1 ) {
printf ( " Failed to open %s on connection %d \n " , FNAME , i ) ;
goto failed ;
}
2009-04-02 04:46:40 +04:00
/* Optionally, lock initial_locks for each proc beforehand. */
if ( i = = 0 & & initial_locks > 0 ) {
printf ( " Initializing %d locks on each proc. \n " ,
initial_locks ) ;
}
for ( j = 0 ; j < initial_locks ; j + + ) {
offset = ( 0xFFFFFED8LLU * ( i + 2 ) ) + j ;
if ( ! NT_STATUS_IS_OK ( smbcli_lock64 ( state [ i ] . tree ,
state [ i ] . fnum , offset , 1 , 0 , WRITE_LOCK ) ) ) {
printf ( " Failed initializing, lock=%d \n " , j ) ;
goto failed ;
}
}
2007-05-17 07:42:28 +04:00
state [ i ] . stage = LOCK_INITIAL ;
2006-12-09 06:16:17 +03:00
lock_send ( & state [ i ] ) ;
}
tv = timeval_current ( ) ;
2007-05-17 06:22:29 +04:00
if ( progress ) {
2010-05-25 23:23:55 +04:00
tevent_add_timer ( torture - > ev , state , timeval_current_ofs ( 1 , 0 ) , report_rate , state ) ;
2007-05-17 06:22:29 +04:00
}
2006-12-09 06:16:17 +03:00
printf ( " Running for %d seconds \n " , timelimit ) ;
while ( timeval_elapsed ( & tv ) < timelimit ) {
2010-05-25 23:23:55 +04:00
tevent_loop_once ( torture - > ev ) ;
2006-12-09 06:16:17 +03:00
if ( lock_failed ) {
DEBUG ( 0 , ( " locking failed \n " ) ) ;
goto failed ;
}
}
printf ( " %.2f ops/second \n " , total / timeval_elapsed ( & tv ) ) ;
2007-02-07 10:11:20 +03:00
minops = state [ 0 ] . count ;
for ( i = 0 ; i < nprocs ; i + + ) {
printf ( " [%d] %u ops \n " , i , state [ i ] . count ) ;
if ( state [ i ] . count < minops ) minops = state [ i ] . count ;
}
if ( minops < 0.5 * total / nprocs ) {
printf ( " Failed: unbalanced locking \n " ) ;
goto failed ;
}
2006-12-09 06:16:17 +03:00
for ( i = 0 ; i < nprocs ; i + + ) {
talloc_free ( state [ i ] . req ) ;
2007-05-14 07:49:42 +04:00
smb_raw_exit ( state [ i ] . tree - > session ) ;
2006-12-09 06:16:17 +03:00
}
2007-05-14 07:49:42 +04:00
smbcli_deltree ( state [ 0 ] . tree , BASEDIR ) ;
2006-12-09 06:16:17 +03:00
talloc_free ( mem_ctx ) ;
2007-08-26 23:58:40 +04:00
printf ( " \n " ) ;
2006-12-09 06:16:17 +03:00
return ret ;
failed :
2009-04-02 04:46:40 +04:00
smbcli_deltree ( state [ 0 ] . tree , BASEDIR ) ;
2006-12-09 06:16:17 +03:00
talloc_free ( mem_ctx ) ;
2007-08-26 23:58:40 +04:00
return false ;
2006-12-09 06:16:17 +03:00
}