2013-06-05 16:19:31 +04:00
/*
* Command line utility to exercise the QEMU I / O path .
*
* Copyright ( C ) 2009 Red Hat , Inc .
* Copyright ( c ) 2003 - 2005 Silicon Graphics , Inc .
*
* This work is licensed under the terms of the GNU GPL , version 2 or later .
* See the COPYING file in the top - level directory .
*/
2013-06-05 16:19:39 +04:00
# include "qemu-io.h"
2013-06-05 16:19:31 +04:00
# include "block/block_int.h"
# define CMD_NOFILE_OK 0x01
int qemuio_misalign ;
2013-06-05 16:19:36 +04:00
static cmdinfo_t * cmdtab ;
static int ncmds ;
static int compare_cmdname ( const void * a , const void * b )
{
return strcmp ( ( ( const cmdinfo_t * ) a ) - > name ,
( ( const cmdinfo_t * ) b ) - > name ) ;
}
void qemuio_add_command ( const cmdinfo_t * ci )
{
cmdtab = g_realloc ( cmdtab , + + ncmds * sizeof ( * cmdtab ) ) ;
cmdtab [ ncmds - 1 ] = * ci ;
qsort ( cmdtab , ncmds , sizeof ( * cmdtab ) , compare_cmdname ) ;
}
int qemuio_command_usage ( const cmdinfo_t * ci )
{
printf ( " %s %s -- %s \n " , ci - > name , ci - > args , ci - > oneline ) ;
return 0 ;
}
static int init_check_command ( BlockDriverState * bs , const cmdinfo_t * ct )
{
if ( ct - > flags & CMD_FLAG_GLOBAL ) {
return 1 ;
}
if ( ! ( ct - > flags & CMD_NOFILE_OK ) & & ! bs ) {
fprintf ( stderr , " no file open, try 'help open' \n " ) ;
return 0 ;
}
return 1 ;
}
2013-06-05 16:19:39 +04:00
static int command ( BlockDriverState * bs , const cmdinfo_t * ct , int argc ,
char * * argv )
2013-06-05 16:19:36 +04:00
{
char * cmd = argv [ 0 ] ;
2013-06-05 16:19:39 +04:00
if ( ! init_check_command ( bs , ct ) ) {
2013-06-05 16:19:36 +04:00
return 0 ;
}
if ( argc - 1 < ct - > argmin | | ( ct - > argmax ! = - 1 & & argc - 1 > ct - > argmax ) ) {
if ( ct - > argmax = = - 1 ) {
fprintf ( stderr ,
" bad argument count %d to %s, expected at least %d arguments \n " ,
argc - 1 , cmd , ct - > argmin ) ;
} else if ( ct - > argmin = = ct - > argmax ) {
fprintf ( stderr ,
" bad argument count %d to %s, expected %d arguments \n " ,
argc - 1 , cmd , ct - > argmin ) ;
} else {
fprintf ( stderr ,
" bad argument count %d to %s, expected between %d and %d arguments \n " ,
argc - 1 , cmd , ct - > argmin , ct - > argmax ) ;
}
return 0 ;
}
optind = 0 ;
2013-06-05 16:19:39 +04:00
return ct - > cfunc ( bs , argc , argv ) ;
2013-06-05 16:19:36 +04:00
}
static const cmdinfo_t * find_command ( const char * cmd )
{
cmdinfo_t * ct ;
for ( ct = cmdtab ; ct < & cmdtab [ ncmds ] ; ct + + ) {
if ( strcmp ( ct - > name , cmd ) = = 0 | |
( ct - > altname & & strcmp ( ct - > altname , cmd ) = = 0 ) )
{
return ( const cmdinfo_t * ) ct ;
}
}
return NULL ;
}
static char * * breakline ( char * input , int * count )
{
int c = 0 ;
char * p ;
char * * rval = g_malloc0 ( sizeof ( char * ) ) ;
char * * tmp ;
while ( rval & & ( p = qemu_strsep ( & input , " " ) ) ! = NULL ) {
if ( ! * p ) {
continue ;
}
c + + ;
tmp = g_realloc ( rval , sizeof ( * rval ) * ( c + 1 ) ) ;
if ( ! tmp ) {
g_free ( rval ) ;
rval = NULL ;
c = 0 ;
break ;
} else {
rval = tmp ;
}
rval [ c - 1 ] = p ;
rval [ c ] = NULL ;
}
* count = c ;
return rval ;
}
2013-06-05 16:19:31 +04:00
static int64_t cvtnum ( const char * s )
{
char * end ;
return strtosz_suffix ( s , & end , STRTOSZ_DEFSUFFIX_B ) ;
}
2013-06-05 16:19:38 +04:00
# define EXABYTES(x) ((long long)(x) << 60)
# define PETABYTES(x) ((long long)(x) << 50)
# define TERABYTES(x) ((long long)(x) << 40)
# define GIGABYTES(x) ((long long)(x) << 30)
# define MEGABYTES(x) ((long long)(x) << 20)
# define KILOBYTES(x) ((long long)(x) << 10)
# define TO_EXABYTES(x) ((x) / EXABYTES(1))
# define TO_PETABYTES(x) ((x) / PETABYTES(1))
# define TO_TERABYTES(x) ((x) / TERABYTES(1))
# define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
# define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
# define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
static void cvtstr ( double value , char * str , size_t size )
{
char * trim ;
const char * suffix ;
if ( value > = EXABYTES ( 1 ) ) {
suffix = " EiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_EXABYTES ( value ) ) ;
} else if ( value > = PETABYTES ( 1 ) ) {
suffix = " PiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_PETABYTES ( value ) ) ;
} else if ( value > = TERABYTES ( 1 ) ) {
suffix = " TiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_TERABYTES ( value ) ) ;
} else if ( value > = GIGABYTES ( 1 ) ) {
suffix = " GiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_GIGABYTES ( value ) ) ;
} else if ( value > = MEGABYTES ( 1 ) ) {
suffix = " MiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_MEGABYTES ( value ) ) ;
} else if ( value > = KILOBYTES ( 1 ) ) {
suffix = " KiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_KILOBYTES ( value ) ) ;
} else {
suffix = " bytes " ;
snprintf ( str , size - 6 , " %f " , value ) ;
}
trim = strstr ( str , " .000 " ) ;
if ( trim ) {
strcpy ( trim , suffix ) ;
} else {
strcat ( str , suffix ) ;
}
}
static struct timeval tsub ( struct timeval t1 , struct timeval t2 )
{
t1 . tv_usec - = t2 . tv_usec ;
if ( t1 . tv_usec < 0 ) {
t1 . tv_usec + = 1000000 ;
t1 . tv_sec - - ;
}
t1 . tv_sec - = t2 . tv_sec ;
return t1 ;
}
static double tdiv ( double value , struct timeval tv )
{
return value / ( ( double ) tv . tv_sec + ( ( double ) tv . tv_usec / 1000000.0 ) ) ;
}
# define HOURS(sec) ((sec) / (60 * 60))
# define MINUTES(sec) (((sec) % (60 * 60)) / 60)
# define SECONDS(sec) ((sec) % 60)
enum {
DEFAULT_TIME = 0x0 ,
TERSE_FIXED_TIME = 0x1 ,
VERBOSE_FIXED_TIME = 0x2 ,
} ;
static void timestr ( struct timeval * tv , char * ts , size_t size , int format )
{
double usec = ( double ) tv - > tv_usec / 1000000.0 ;
if ( format & TERSE_FIXED_TIME ) {
if ( ! HOURS ( tv - > tv_sec ) ) {
snprintf ( ts , size , " %u:%02u.%02u " ,
( unsigned int ) MINUTES ( tv - > tv_sec ) ,
( unsigned int ) SECONDS ( tv - > tv_sec ) ,
( unsigned int ) ( usec * 100 ) ) ;
return ;
}
format | = VERBOSE_FIXED_TIME ; /* fallback if hours needed */
}
if ( ( format & VERBOSE_FIXED_TIME ) | | tv - > tv_sec ) {
snprintf ( ts , size , " %u:%02u:%02u.%02u " ,
( unsigned int ) HOURS ( tv - > tv_sec ) ,
( unsigned int ) MINUTES ( tv - > tv_sec ) ,
( unsigned int ) SECONDS ( tv - > tv_sec ) ,
( unsigned int ) ( usec * 100 ) ) ;
} else {
snprintf ( ts , size , " 0.%04u sec " , ( unsigned int ) ( usec * 10000 ) ) ;
}
}
2013-06-05 16:19:31 +04:00
/*
* Parse the pattern argument to various sub - commands .
*
* Because the pattern is used as an argument to memset it must evaluate
* to an unsigned integer that fits into a single byte .
*/
static int parse_pattern ( const char * arg )
{
char * endptr = NULL ;
long pattern ;
pattern = strtol ( arg , & endptr , 0 ) ;
if ( pattern < 0 | | pattern > UCHAR_MAX | | * endptr ! = ' \0 ' ) {
printf ( " %s is not a valid pattern byte \n " , arg ) ;
return - 1 ;
}
return pattern ;
}
/*
* Memory allocation helpers .
*
* Make sure memory is aligned by default , or purposefully misaligned if
* that is specified on the command line .
*/
# define MISALIGN_OFFSET 16
static void * qemu_io_alloc ( BlockDriverState * bs , size_t len , int pattern )
{
void * buf ;
if ( qemuio_misalign ) {
len + = MISALIGN_OFFSET ;
}
buf = qemu_blockalign ( bs , len ) ;
memset ( buf , pattern , len ) ;
if ( qemuio_misalign ) {
buf + = MISALIGN_OFFSET ;
}
return buf ;
}
static void qemu_io_free ( void * p )
{
if ( qemuio_misalign ) {
p - = MISALIGN_OFFSET ;
}
qemu_vfree ( p ) ;
}
static void dump_buffer ( const void * buffer , int64_t offset , int len )
{
int i , j ;
const uint8_t * p ;
for ( i = 0 , p = buffer ; i < len ; i + = 16 ) {
const uint8_t * s = p ;
printf ( " %08 " PRIx64 " : " , offset + i ) ;
for ( j = 0 ; j < 16 & & i + j < len ; j + + , p + + ) {
printf ( " %02x " , * p ) ;
}
printf ( " " ) ;
for ( j = 0 ; j < 16 & & i + j < len ; j + + , s + + ) {
if ( isalnum ( * s ) ) {
printf ( " %c " , * s ) ;
} else {
printf ( " . " ) ;
}
}
printf ( " \n " ) ;
}
}
static void print_report ( const char * op , struct timeval * t , int64_t offset ,
int count , int total , int cnt , int Cflag )
{
char s1 [ 64 ] , s2 [ 64 ] , ts [ 64 ] ;
timestr ( t , ts , sizeof ( ts ) , Cflag ? VERBOSE_FIXED_TIME : 0 ) ;
if ( ! Cflag ) {
cvtstr ( ( double ) total , s1 , sizeof ( s1 ) ) ;
cvtstr ( tdiv ( ( double ) total , * t ) , s2 , sizeof ( s2 ) ) ;
printf ( " %s %d/%d bytes at offset % " PRId64 " \n " ,
op , total , count , offset ) ;
printf ( " %s, %d ops; %s (%s/sec and %.4f ops/sec) \n " ,
s1 , cnt , ts , s2 , tdiv ( ( double ) cnt , * t ) ) ;
} else { /* bytes,ops,time,bytes/sec,ops/sec */
printf ( " %d,%d,%s,%.3f,%.3f \n " ,
total , cnt , ts ,
tdiv ( ( double ) total , * t ) ,
tdiv ( ( double ) cnt , * t ) ) ;
}
}
/*
* Parse multiple length statements for vectored I / O , and construct an I / O
* vector matching it .
*/
static void *
create_iovec ( BlockDriverState * bs , QEMUIOVector * qiov , char * * argv , int nr_iov ,
int pattern )
{
size_t * sizes = g_new0 ( size_t , nr_iov ) ;
size_t count = 0 ;
void * buf = NULL ;
void * p ;
int i ;
for ( i = 0 ; i < nr_iov ; i + + ) {
char * arg = argv [ i ] ;
int64_t len ;
len = cvtnum ( arg ) ;
if ( len < 0 ) {
printf ( " non-numeric length argument -- %s \n " , arg ) ;
goto fail ;
}
/* should be SIZE_T_MAX, but that doesn't exist */
if ( len > INT_MAX ) {
printf ( " too large length argument -- %s \n " , arg ) ;
goto fail ;
}
if ( len & 0x1ff ) {
printf ( " length argument % " PRId64
" is not sector aligned \n " , len ) ;
goto fail ;
}
sizes [ i ] = len ;
count + = len ;
}
qemu_iovec_init ( qiov , nr_iov ) ;
buf = p = qemu_io_alloc ( bs , count , pattern ) ;
for ( i = 0 ; i < nr_iov ; i + + ) {
qemu_iovec_add ( qiov , p , sizes [ i ] ) ;
p + = sizes [ i ] ;
}
fail :
g_free ( sizes ) ;
return buf ;
}
static int do_read ( BlockDriverState * bs , char * buf , int64_t offset , int count ,
int * total )
{
int ret ;
ret = bdrv_read ( bs , offset > > 9 , ( uint8_t * ) buf , count > > 9 ) ;
if ( ret < 0 ) {
return ret ;
}
* total = count ;
return 1 ;
}
static int do_write ( BlockDriverState * bs , char * buf , int64_t offset , int count ,
int * total )
{
int ret ;
ret = bdrv_write ( bs , offset > > 9 , ( uint8_t * ) buf , count > > 9 ) ;
if ( ret < 0 ) {
return ret ;
}
* total = count ;
return 1 ;
}
static int do_pread ( BlockDriverState * bs , char * buf , int64_t offset , int count ,
int * total )
{
* total = bdrv_pread ( bs , offset , ( uint8_t * ) buf , count ) ;
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
static int do_pwrite ( BlockDriverState * bs , char * buf , int64_t offset , int count ,
int * total )
{
* total = bdrv_pwrite ( bs , offset , ( uint8_t * ) buf , count ) ;
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
typedef struct {
BlockDriverState * bs ;
int64_t offset ;
int count ;
int * total ;
int ret ;
bool done ;
} CoWriteZeroes ;
static void coroutine_fn co_write_zeroes_entry ( void * opaque )
{
CoWriteZeroes * data = opaque ;
data - > ret = bdrv_co_write_zeroes ( data - > bs , data - > offset / BDRV_SECTOR_SIZE ,
data - > count / BDRV_SECTOR_SIZE ) ;
data - > done = true ;
if ( data - > ret < 0 ) {
* data - > total = data - > ret ;
return ;
}
* data - > total = data - > count ;
}
static int do_co_write_zeroes ( BlockDriverState * bs , int64_t offset , int count ,
int * total )
{
Coroutine * co ;
CoWriteZeroes data = {
. bs = bs ,
. offset = offset ,
. count = count ,
. total = total ,
. done = false ,
} ;
co = qemu_coroutine_create ( co_write_zeroes_entry ) ;
qemu_coroutine_enter ( co , & data ) ;
while ( ! data . done ) {
qemu_aio_wait ( ) ;
}
if ( data . ret < 0 ) {
return data . ret ;
} else {
return 1 ;
}
}
static int do_write_compressed ( BlockDriverState * bs , char * buf , int64_t offset ,
int count , int * total )
{
int ret ;
ret = bdrv_write_compressed ( bs , offset > > 9 , ( uint8_t * ) buf , count > > 9 ) ;
if ( ret < 0 ) {
return ret ;
}
* total = count ;
return 1 ;
}
static int do_load_vmstate ( BlockDriverState * bs , char * buf , int64_t offset ,
int count , int * total )
{
* total = bdrv_load_vmstate ( bs , ( uint8_t * ) buf , offset , count ) ;
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
static int do_save_vmstate ( BlockDriverState * bs , char * buf , int64_t offset ,
int count , int * total )
{
* total = bdrv_save_vmstate ( bs , ( uint8_t * ) buf , offset , count ) ;
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
# define NOT_DONE 0x7fffffff
static void aio_rw_done ( void * opaque , int ret )
{
* ( int * ) opaque = ret ;
}
static int do_aio_readv ( BlockDriverState * bs , QEMUIOVector * qiov ,
int64_t offset , int * total )
{
int async_ret = NOT_DONE ;
bdrv_aio_readv ( bs , offset > > 9 , qiov , qiov - > size > > 9 ,
aio_rw_done , & async_ret ) ;
while ( async_ret = = NOT_DONE ) {
main_loop_wait ( false ) ;
}
* total = qiov - > size ;
return async_ret < 0 ? async_ret : 1 ;
}
static int do_aio_writev ( BlockDriverState * bs , QEMUIOVector * qiov ,
int64_t offset , int * total )
{
int async_ret = NOT_DONE ;
bdrv_aio_writev ( bs , offset > > 9 , qiov , qiov - > size > > 9 ,
aio_rw_done , & async_ret ) ;
while ( async_ret = = NOT_DONE ) {
main_loop_wait ( false ) ;
}
* total = qiov - > size ;
return async_ret < 0 ? async_ret : 1 ;
}
struct multiwrite_async_ret {
int num_done ;
int error ;
} ;
static void multiwrite_cb ( void * opaque , int ret )
{
struct multiwrite_async_ret * async_ret = opaque ;
async_ret - > num_done + + ;
if ( ret < 0 ) {
async_ret - > error = ret ;
}
}
static int do_aio_multiwrite ( BlockDriverState * bs , BlockRequest * reqs ,
int num_reqs , int * total )
{
int i , ret ;
struct multiwrite_async_ret async_ret = {
. num_done = 0 ,
. error = 0 ,
} ;
* total = 0 ;
for ( i = 0 ; i < num_reqs ; i + + ) {
reqs [ i ] . cb = multiwrite_cb ;
reqs [ i ] . opaque = & async_ret ;
* total + = reqs [ i ] . qiov - > size ;
}
ret = bdrv_aio_multiwrite ( bs , reqs , num_reqs ) ;
if ( ret < 0 ) {
return ret ;
}
while ( async_ret . num_done < num_reqs ) {
main_loop_wait ( false ) ;
}
return async_ret . error < 0 ? async_ret . error : 1 ;
}
static void read_help ( void )
{
printf (
" \n "
" reads a range of bytes from the given offset \n "
" \n "
" Example: \n "
" 'read -v 512 1k' - dumps 1 kilobyte read from 512 bytes into the file \n "
" \n "
" Reads a segment of the currently open file, optionally dumping it to the \n "
" standard output stream (with -v option) for subsequent inspection. \n "
" -b, -- read from the VM state rather than the virtual disk \n "
" -C, -- report statistics in a machine parsable format \n "
" -l, -- length for pattern verification (only with -P) \n "
" -p, -- use bdrv_pread to read the file \n "
" -P, -- use a pattern to verify read data \n "
" -q, -- quiet mode, do not show I/O statistics \n "
" -s, -- start offset for pattern verification (only with -P) \n "
" -v, -- dump buffer to standard output \n "
" \n " ) ;
}
static int read_f ( BlockDriverState * bs , int argc , char * * argv ) ;
static const cmdinfo_t read_cmd = {
. name = " read " ,
. altname = " r " ,
. cfunc = read_f ,
. argmin = 2 ,
. argmax = - 1 ,
. args = " [-abCpqv] [-P pattern [-s off] [-l len]] off len " ,
. oneline = " reads a number of bytes at a specified offset " ,
. help = read_help ,
} ;
static int read_f ( BlockDriverState * bs , int argc , char * * argv )
{
struct timeval t1 , t2 ;
int Cflag = 0 , pflag = 0 , qflag = 0 , vflag = 0 ;
int Pflag = 0 , sflag = 0 , lflag = 0 , bflag = 0 ;
int c , cnt ;
char * buf ;
int64_t offset ;
int count ;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0 ;
int pattern = 0 , pattern_offset = 0 , pattern_count = 0 ;
while ( ( c = getopt ( argc , argv , " bCl:pP:qs:v " ) ) ! = EOF ) {
switch ( c ) {
case ' b ' :
bflag = 1 ;
break ;
case ' C ' :
Cflag = 1 ;
break ;
case ' l ' :
lflag = 1 ;
pattern_count = cvtnum ( optarg ) ;
if ( pattern_count < 0 ) {
printf ( " non-numeric length argument -- %s \n " , optarg ) ;
return 0 ;
}
break ;
case ' p ' :
pflag = 1 ;
break ;
case ' P ' :
Pflag = 1 ;
pattern = parse_pattern ( optarg ) ;
if ( pattern < 0 ) {
return 0 ;
}
break ;
case ' q ' :
qflag = 1 ;
break ;
case ' s ' :
sflag = 1 ;
pattern_offset = cvtnum ( optarg ) ;
if ( pattern_offset < 0 ) {
printf ( " non-numeric length argument -- %s \n " , optarg ) ;
return 0 ;
}
break ;
case ' v ' :
vflag = 1 ;
break ;
default :
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & read_cmd ) ;
2013-06-05 16:19:31 +04:00
}
}
if ( optind ! = argc - 2 ) {
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & read_cmd ) ;
2013-06-05 16:19:31 +04:00
}
if ( bflag & & pflag ) {
printf ( " -b and -p cannot be specified at the same time \n " ) ;
return 0 ;
}
offset = cvtnum ( argv [ optind ] ) ;
if ( offset < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
optind + + ;
count = cvtnum ( argv [ optind ] ) ;
if ( count < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
if ( ! Pflag & & ( lflag | | sflag ) ) {
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & read_cmd ) ;
2013-06-05 16:19:31 +04:00
}
if ( ! lflag ) {
pattern_count = count - pattern_offset ;
}
if ( ( pattern_count < 0 ) | | ( pattern_count + pattern_offset > count ) ) {
printf ( " pattern verification range exceeds end of read data \n " ) ;
return 0 ;
}
if ( ! pflag ) {
if ( offset & 0x1ff ) {
printf ( " offset % " PRId64 " is not sector aligned \n " ,
offset ) ;
return 0 ;
}
if ( count & 0x1ff ) {
printf ( " count %d is not sector aligned \n " ,
count ) ;
return 0 ;
}
}
buf = qemu_io_alloc ( bs , count , 0xab ) ;
gettimeofday ( & t1 , NULL ) ;
if ( pflag ) {
cnt = do_pread ( bs , buf , offset , count , & total ) ;
} else if ( bflag ) {
cnt = do_load_vmstate ( bs , buf , offset , count , & total ) ;
} else {
cnt = do_read ( bs , buf , offset , count , & total ) ;
}
gettimeofday ( & t2 , NULL ) ;
if ( cnt < 0 ) {
printf ( " read failed: %s \n " , strerror ( - cnt ) ) ;
goto out ;
}
if ( Pflag ) {
void * cmp_buf = g_malloc ( pattern_count ) ;
memset ( cmp_buf , pattern , pattern_count ) ;
if ( memcmp ( buf + pattern_offset , cmp_buf , pattern_count ) ) {
printf ( " Pattern verification failed at offset % "
PRId64 " , %d bytes \n " ,
offset + pattern_offset , pattern_count ) ;
}
g_free ( cmp_buf ) ;
}
if ( qflag ) {
goto out ;
}
if ( vflag ) {
dump_buffer ( buf , offset , count ) ;
}
/* Finally, report back -- -C gives a parsable format */
t2 = tsub ( t2 , t1 ) ;
print_report ( " read " , & t2 , offset , count , total , cnt , Cflag ) ;
out :
qemu_io_free ( buf ) ;
return 0 ;
}
static void readv_help ( void )
{
printf (
" \n "
" reads a range of bytes from the given offset into multiple buffers \n "
" \n "
" Example: \n "
" 'readv -v 512 1k 1k ' - dumps 2 kilobytes read from 512 bytes into the file \n "
" \n "
" Reads a segment of the currently open file, optionally dumping it to the \n "
" standard output stream (with -v option) for subsequent inspection. \n "
" Uses multiple iovec buffers if more than one byte range is specified. \n "
" -C, -- report statistics in a machine parsable format \n "
" -P, -- use a pattern to verify read data \n "
" -v, -- dump buffer to standard output \n "
" -q, -- quiet mode, do not show I/O statistics \n "
" \n " ) ;
}
static int readv_f ( BlockDriverState * bs , int argc , char * * argv ) ;
static const cmdinfo_t readv_cmd = {
. name = " readv " ,
. cfunc = readv_f ,
. argmin = 2 ,
. argmax = - 1 ,
. args = " [-Cqv] [-P pattern ] off len [len..] " ,
. oneline = " reads a number of bytes at a specified offset " ,
. help = readv_help ,
} ;
static int readv_f ( BlockDriverState * bs , int argc , char * * argv )
{
struct timeval t1 , t2 ;
int Cflag = 0 , qflag = 0 , vflag = 0 ;
int c , cnt ;
char * buf ;
int64_t offset ;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0 ;
int nr_iov ;
QEMUIOVector qiov ;
int pattern = 0 ;
int Pflag = 0 ;
while ( ( c = getopt ( argc , argv , " CP:qv " ) ) ! = EOF ) {
switch ( c ) {
case ' C ' :
Cflag = 1 ;
break ;
case ' P ' :
Pflag = 1 ;
pattern = parse_pattern ( optarg ) ;
if ( pattern < 0 ) {
return 0 ;
}
break ;
case ' q ' :
qflag = 1 ;
break ;
case ' v ' :
vflag = 1 ;
break ;
default :
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & readv_cmd ) ;
2013-06-05 16:19:31 +04:00
}
}
if ( optind > argc - 2 ) {
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & readv_cmd ) ;
2013-06-05 16:19:31 +04:00
}
offset = cvtnum ( argv [ optind ] ) ;
if ( offset < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
optind + + ;
if ( offset & 0x1ff ) {
printf ( " offset % " PRId64 " is not sector aligned \n " ,
offset ) ;
return 0 ;
}
nr_iov = argc - optind ;
buf = create_iovec ( bs , & qiov , & argv [ optind ] , nr_iov , 0xab ) ;
if ( buf = = NULL ) {
return 0 ;
}
gettimeofday ( & t1 , NULL ) ;
cnt = do_aio_readv ( bs , & qiov , offset , & total ) ;
gettimeofday ( & t2 , NULL ) ;
if ( cnt < 0 ) {
printf ( " readv failed: %s \n " , strerror ( - cnt ) ) ;
goto out ;
}
if ( Pflag ) {
void * cmp_buf = g_malloc ( qiov . size ) ;
memset ( cmp_buf , pattern , qiov . size ) ;
if ( memcmp ( buf , cmp_buf , qiov . size ) ) {
printf ( " Pattern verification failed at offset % "
PRId64 " , %zd bytes \n " , offset , qiov . size ) ;
}
g_free ( cmp_buf ) ;
}
if ( qflag ) {
goto out ;
}
if ( vflag ) {
dump_buffer ( buf , offset , qiov . size ) ;
}
/* Finally, report back -- -C gives a parsable format */
t2 = tsub ( t2 , t1 ) ;
print_report ( " read " , & t2 , offset , qiov . size , total , cnt , Cflag ) ;
out :
qemu_iovec_destroy ( & qiov ) ;
qemu_io_free ( buf ) ;
return 0 ;
}
static void write_help ( void )
{
printf (
" \n "
" writes a range of bytes from the given offset \n "
" \n "
" Example: \n "
" 'write 512 1k' - writes 1 kilobyte at 512 bytes into the open file \n "
" \n "
" Writes into a segment of the currently open file, using a buffer \n "
" filled with a set pattern (0xcdcdcdcd). \n "
" -b, -- write to the VM state rather than the virtual disk \n "
" -c, -- write compressed data with bdrv_write_compressed \n "
" -p, -- use bdrv_pwrite to write the file \n "
" -P, -- use different pattern to fill file \n "
" -C, -- report statistics in a machine parsable format \n "
" -q, -- quiet mode, do not show I/O statistics \n "
" -z, -- write zeroes using bdrv_co_write_zeroes \n "
" \n " ) ;
}
static int write_f ( BlockDriverState * bs , int argc , char * * argv ) ;
static const cmdinfo_t write_cmd = {
. name = " write " ,
. altname = " w " ,
. cfunc = write_f ,
. argmin = 2 ,
. argmax = - 1 ,
. args = " [-bcCpqz] [-P pattern ] off len " ,
. oneline = " writes a number of bytes at a specified offset " ,
. help = write_help ,
} ;
static int write_f ( BlockDriverState * bs , int argc , char * * argv )
{
struct timeval t1 , t2 ;
int Cflag = 0 , pflag = 0 , qflag = 0 , bflag = 0 , Pflag = 0 , zflag = 0 ;
int cflag = 0 ;
int c , cnt ;
char * buf = NULL ;
int64_t offset ;
int count ;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0 ;
int pattern = 0xcd ;
while ( ( c = getopt ( argc , argv , " bcCpP:qz " ) ) ! = EOF ) {
switch ( c ) {
case ' b ' :
bflag = 1 ;
break ;
case ' c ' :
cflag = 1 ;
break ;
case ' C ' :
Cflag = 1 ;
break ;
case ' p ' :
pflag = 1 ;
break ;
case ' P ' :
Pflag = 1 ;
pattern = parse_pattern ( optarg ) ;
if ( pattern < 0 ) {
return 0 ;
}
break ;
case ' q ' :
qflag = 1 ;
break ;
case ' z ' :
zflag = 1 ;
break ;
default :
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & write_cmd ) ;
2013-06-05 16:19:31 +04:00
}
}
if ( optind ! = argc - 2 ) {
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & write_cmd ) ;
2013-06-05 16:19:31 +04:00
}
if ( bflag + pflag + zflag > 1 ) {
printf ( " -b, -p, or -z cannot be specified at the same time \n " ) ;
return 0 ;
}
if ( zflag & & Pflag ) {
printf ( " -z and -P cannot be specified at the same time \n " ) ;
return 0 ;
}
offset = cvtnum ( argv [ optind ] ) ;
if ( offset < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
optind + + ;
count = cvtnum ( argv [ optind ] ) ;
if ( count < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
if ( ! pflag ) {
if ( offset & 0x1ff ) {
printf ( " offset % " PRId64 " is not sector aligned \n " ,
offset ) ;
return 0 ;
}
if ( count & 0x1ff ) {
printf ( " count %d is not sector aligned \n " ,
count ) ;
return 0 ;
}
}
if ( ! zflag ) {
buf = qemu_io_alloc ( bs , count , pattern ) ;
}
gettimeofday ( & t1 , NULL ) ;
if ( pflag ) {
cnt = do_pwrite ( bs , buf , offset , count , & total ) ;
} else if ( bflag ) {
cnt = do_save_vmstate ( bs , buf , offset , count , & total ) ;
} else if ( zflag ) {
cnt = do_co_write_zeroes ( bs , offset , count , & total ) ;
} else if ( cflag ) {
cnt = do_write_compressed ( bs , buf , offset , count , & total ) ;
} else {
cnt = do_write ( bs , buf , offset , count , & total ) ;
}
gettimeofday ( & t2 , NULL ) ;
if ( cnt < 0 ) {
printf ( " write failed: %s \n " , strerror ( - cnt ) ) ;
goto out ;
}
if ( qflag ) {
goto out ;
}
/* Finally, report back -- -C gives a parsable format */
t2 = tsub ( t2 , t1 ) ;
print_report ( " wrote " , & t2 , offset , count , total , cnt , Cflag ) ;
out :
if ( ! zflag ) {
qemu_io_free ( buf ) ;
}
return 0 ;
}
static void
writev_help ( void )
{
printf (
" \n "
" writes a range of bytes from the given offset source from multiple buffers \n "
" \n "
" Example: \n "
" 'write 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file \n "
" \n "
" Writes into a segment of the currently open file, using a buffer \n "
" filled with a set pattern (0xcdcdcdcd). \n "
" -P, -- use different pattern to fill file \n "
" -C, -- report statistics in a machine parsable format \n "
" -q, -- quiet mode, do not show I/O statistics \n "
" \n " ) ;
}
static int writev_f ( BlockDriverState * bs , int argc , char * * argv ) ;
static const cmdinfo_t writev_cmd = {
. name = " writev " ,
. cfunc = writev_f ,
. argmin = 2 ,
. argmax = - 1 ,