2009-04-29 19:50:38 +04:00
/*
2009-05-05 15:16:38 +04:00
A ping - pong fcntl byte range lock test
Copyright ( C ) Andrew Tridgell 2002
2012-11-06 04:26:05 +04:00
Copyright ( C ) Michael Adam 2012
2009-05-05 15:16:38 +04:00
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/>.
*/
/*
This measures the ping - pong byte range lock latency . It is
2009-04-29 19:50:38 +04:00
especially useful on a cluster of nodes sharing a common lock
manager as it will give some indication of the lock managers
2009-05-05 15:16:38 +04:00
performance under stress .
2009-04-29 19:50:38 +04:00
tridge @ samba . org , February 2002
*/
2009-04-30 02:35:55 +04:00
# define _XOPEN_SOURCE 500
2009-04-29 19:50:38 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <sys/time.h>
# include <time.h>
# include <errno.h>
# include <string.h>
# include <unistd.h>
# include <fcntl.h>
# include <sys/mman.h>
2015-05-10 02:39:16 +03:00
# include <stdbool.h>
2009-04-29 19:50:38 +04:00
static struct timeval tp1 , tp2 ;
2015-05-10 02:39:16 +03:00
static int do_reads , do_writes , use_mmap , do_check , do_brl_test ;
2009-04-29 19:50:38 +04:00
2009-04-29 20:03:03 +04:00
static void start_timer ( void )
2009-04-29 19:50:38 +04:00
{
gettimeofday ( & tp1 , NULL ) ;
}
2009-04-29 20:03:03 +04:00
static double end_timer ( void )
2009-04-29 19:50:38 +04:00
{
gettimeofday ( & tp2 , NULL ) ;
return ( tp2 . tv_sec + ( tp2 . tv_usec * 1.0e-6 ) ) -
( tp1 . tv_sec + ( tp1 . tv_usec * 1.0e-6 ) ) ;
}
/* lock a byte range in a open file */
2015-05-10 02:39:16 +03:00
static int lock_range ( int fd , int offset , int len , bool wait )
2009-04-29 19:50:38 +04:00
{
struct flock lock ;
lock . l_type = F_WRLCK ;
lock . l_whence = SEEK_SET ;
lock . l_start = offset ;
lock . l_len = len ;
lock . l_pid = 0 ;
2015-05-10 02:39:16 +03:00
return fcntl ( fd , wait ? F_SETLKW : F_SETLK , & lock ) ;
2009-04-29 19:50:38 +04:00
}
2012-11-06 04:26:05 +04:00
/* check whether we could place a lock */
2014-09-04 07:28:34 +04:00
static int check_lock ( int fd , int offset , int len )
2012-11-06 04:26:05 +04:00
{
struct flock lock ;
int ret ;
lock . l_type = F_WRLCK ;
lock . l_whence = SEEK_SET ;
lock . l_start = offset ;
lock . l_len = len ;
lock . l_pid = 0 ;
ret = fcntl ( fd , F_GETLK , & lock ) ;
if ( ret ! = 0 ) {
printf ( " error calling fcntl F_GETLCK: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
if ( lock . l_type = = F_UNLCK ) {
/* we would be able to place the lock */
return 0 ;
}
/* we would not be able to place lock */
printf ( " check_lock failed: lock held: "
" pid='%d', type='%d', start='%d', len='%d' \n " ,
( int ) lock . l_pid , ( int ) lock . l_type , ( int ) lock . l_start , ( int ) lock . l_len ) ;
return 1 ;
}
2009-04-29 19:50:38 +04:00
/* unlock a byte range in a open file */
static int unlock_range ( int fd , int offset , int len )
{
struct flock lock ;
lock . l_type = F_UNLCK ;
lock . l_whence = SEEK_SET ;
lock . l_start = offset ;
lock . l_len = len ;
lock . l_pid = 0 ;
return fcntl ( fd , F_SETLKW , & lock ) ;
}
/* run the ping pong test on fd */
static void ping_pong ( int fd , int num_locks )
{
unsigned count = 0 ;
int i = 0 , loops = 0 ;
unsigned char * val ;
unsigned char incr = 0 , last_incr = 0 ;
unsigned char * p = NULL ;
2009-05-20 14:08:13 +04:00
int ret ;
2009-04-29 19:50:38 +04:00
2009-05-20 14:08:13 +04:00
ret = ftruncate ( fd , num_locks + 1 ) ;
if ( ret = = - 1 ) {
printf ( " ftruncate failed: %s \n " , strerror ( errno ) ) ;
return ;
}
2009-04-29 19:50:38 +04:00
if ( use_mmap ) {
p = mmap ( NULL , num_locks + 1 , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0 ) ;
2009-05-20 14:08:13 +04:00
if ( p = = MAP_FAILED ) {
printf ( " mmap failed: %s \n " , strerror ( errno ) ) ;
return ;
}
2009-04-29 19:50:38 +04:00
}
val = ( unsigned char * ) calloc ( num_locks + 1 , sizeof ( unsigned char ) ) ;
2009-05-20 14:08:13 +04:00
if ( val = = NULL ) {
printf ( " calloc failed \n " ) ;
2015-05-03 12:34:41 +03:00
munmap ( p , num_locks + 1 ) ;
2009-05-20 14:08:13 +04:00
return ;
}
2009-04-29 19:50:38 +04:00
2009-05-20 14:08:13 +04:00
start_timer ( ) ;
2009-04-29 19:50:38 +04:00
2015-05-10 02:39:16 +03:00
lock_range ( fd , 0 , 1 , true ) ;
2009-04-29 19:50:38 +04:00
i = 0 ;
while ( 1 ) {
2015-05-10 02:39:16 +03:00
if ( lock_range ( fd , ( i + 1 ) % num_locks , 1 , true ) ! = 0 ) {
2009-04-29 19:50:38 +04:00
printf ( " lock at %d failed! - %s \n " ,
( i + 1 ) % num_locks , strerror ( errno ) ) ;
}
2012-11-06 04:26:05 +04:00
if ( do_check ) {
ret = check_lock ( fd , i , 1 ) ;
}
2009-04-29 19:50:38 +04:00
if ( do_reads ) {
unsigned char c ;
if ( use_mmap ) {
c = p [ i ] ;
2009-05-12 09:56:23 +04:00
} else if ( pread ( fd , & c , 1 , i ) ! = 1 ) {
2009-04-29 19:50:38 +04:00
printf ( " read failed at %d \n " , i ) ;
}
incr = c - val [ i ] ;
val [ i ] = c ;
}
if ( do_writes ) {
char c = val [ i ] + 1 ;
if ( use_mmap ) {
p [ i ] = c ;
} else if ( pwrite ( fd , & c , 1 , i ) ! = 1 ) {
printf ( " write failed at %d \n " , i ) ;
}
}
if ( unlock_range ( fd , i , 1 ) ! = 0 ) {
printf ( " unlock at %d failed! - %s \n " ,
i , strerror ( errno ) ) ;
}
i = ( i + 1 ) % num_locks ;
count + + ;
if ( loops > num_locks & & incr ! = last_incr ) {
last_incr = incr ;
printf ( " data increment = %u \n " , incr ) ;
fflush ( stdout ) ;
}
if ( end_timer ( ) > 1.0 ) {
printf ( " %8u locks/sec \r " ,
( unsigned ) ( 2 * count / end_timer ( ) ) ) ;
fflush ( stdout ) ;
start_timer ( ) ;
count = 0 ;
}
loops + + ;
}
}
2015-05-10 02:39:16 +03:00
static void usage ( void )
{
printf ( " ping_pong -rwmc <file> <num_locks> \n " ) ;
printf ( " ping_pong -l <file> \n \n " ) ;
printf ( " Options \n " ) ;
printf ( " -r do reads \n " ) ;
printf ( " -w do writes \n " ) ;
printf ( " -m use mmap \n " ) ;
printf ( " -c check locks \n " ) ;
printf ( " -l test for working byte range locks \n " ) ;
}
2009-04-29 19:50:38 +04:00
int main ( int argc , char * argv [ ] )
{
char * fname ;
int fd , num_locks ;
int c ;
2015-05-10 02:39:16 +03:00
while ( ( c = getopt ( argc , argv , " rwmcl " ) ) ! = - 1 ) {
2009-04-29 19:50:38 +04:00
switch ( c ) {
case ' w ' :
do_writes = 1 ;
break ;
case ' r ' :
do_reads = 1 ;
break ;
case ' m ' :
use_mmap = 1 ;
break ;
2012-11-06 04:26:05 +04:00
case ' c ' :
do_check = 1 ;
break ;
2015-05-10 02:39:16 +03:00
case ' l ' :
do_brl_test = 1 ;
break ;
2009-04-29 19:50:38 +04:00
default :
fprintf ( stderr , " Unknown option '%c' \n " , c ) ;
exit ( 1 ) ;
}
}
argv + = optind ;
argc - = optind ;
2015-05-10 02:39:16 +03:00
if ( argc < 1 ) {
usage ( ) ;
2009-04-29 19:50:38 +04:00
exit ( 1 ) ;
}
fname = argv [ 0 ] ;
2015-05-10 02:39:16 +03:00
fd = open ( fname , O_CREAT | O_RDWR , 0600 ) ;
if ( fd = = - 1 ) {
exit ( 1 ) ;
}
if ( do_brl_test ) {
if ( lock_range ( fd , 0 , 0 , false ) ! = 0 ) {
printf ( " file already locked, calling check_lock to tell us who has it locked: \n " ) ;
( void ) check_lock ( fd , 0 , 0 ) ;
printf ( " Working POSIX byte range locks \n " ) ;
exit ( 0 ) ;
}
printf ( " Holding lock, press any key to continue... \n " ) ;
printf ( " You should run the same command on another node now. \n " ) ;
getchar ( ) ;
printf ( " Good bye. \n " ) ;
exit ( 0 ) ;
}
if ( argc < 2 ) {
usage ( ) ;
exit ( 1 ) ;
}
2009-04-29 19:50:38 +04:00
num_locks = atoi ( argv [ 1 ] ) ;
2013-07-04 11:37:05 +04:00
if ( num_locks < = 0 ) {
printf ( " num_locks should be > 0 \n " ) ;
exit ( 1 ) ;
}
2009-04-29 19:50:38 +04:00
ping_pong ( fd , num_locks ) ;
return 0 ;
}