/** * @file * * @brief Test Program for Evaluating NFS Read Throughput * @ingroup libfs * * This test code allows for evaluating NFS read performance * under various scenarios: * - synchronous reads with various buffer sizes (select * 'num_readers' == 0, see below). * - pseudo 'read-ahead' using multiple threads that issue * NFS reads from the same file (but from different offsets) * in parallel. * Rationale: each NFS read request is synchronous, i.e., the * caller sends a request to the server and waits for the * reply to come back. Performance enhancement can be expected * by requesting multiple blocks in parallel rather than * sequentially. * * rtems_interval * nfsTestRead(char *file_name, int chunk_size, int num_readers); * * 1) creates 'num_readers' threads, each opening 'file_name' for * reading on a separate file descriptor. * 2) creates message queues for communicating with reader threads * * 3) read file using nfsTestReadBigbuf() until EOF is reached * * 4) releases resources. * * RETURNS: Time elapsed during step 3 in ms. This is measured * using the system clock so make sure the test file * is big enough. * * nfsTestReadBigbuf() synchronously reads a block of * 'num_readers * chunk_size' (which may be bigger than * the UDP limit of 8k) using 'num_reader' threads to * retrieve the various pieces of the big block in parallel. * This speeds up things since several RPC calls can * be in the works at once. * * NOTES: * - if 'num_readers' == 0 this corresponds to an 'ordinary' * NFS read. 'num_readers' == 1 schedules a single reader * thread (== ordinary NFS read + message passing overhead). * - no actual processing on the data is done; they are simply * thrown away. A real, performance-critical application could * pipeline 'reader' and 'cruncher' threads. * - read is not completely asynchronous; synchronization is still * performed at 'big block' boundaries (num_readers * chunk_size). */ /* * Author: Till Straumann , 2006 * * Authorship * ---------- * This software (NFS-2 client implementation for RTEMS) was created by * Till Straumann , 2002-2007, * Stanford Linear Accelerator Center, Stanford University. * * Acknowledgement of sponsorship * ------------------------------ * The NFS-2 client implementation for RTEMS was produced by * the Stanford Linear Accelerator Center, Stanford University, * under Contract DE-AC03-76SFO0515 with the Department of Energy. * * Government disclaimer of liability * ---------------------------------- * Neither the United States nor the United States Department of Energy, * nor any of their employees, makes any warranty, express or implied, or * assumes any legal liability or responsibility for the accuracy, * completeness, or usefulness of any data, apparatus, product, or process * disclosed, or represents that its use would not infringe privately owned * rights. * * Stanford disclaimer of liability * -------------------------------- * Stanford University makes no representations or warranties, express or * implied, nor assumes any liability for the use of this software. * * Stanford disclaimer of copyright * -------------------------------- * Stanford University, owner of the copyright, hereby disclaims its * copyright and all other rights in this software. Hence, anyone may * freely use it for any purpose without restriction. * * Maintenance of notices * ---------------------- * In the interest of clarity regarding the origin and status of this * SLAC software, this and all the preceding Stanford University notices * are to remain affixed to any copy or derivative of this software made * or distributed by the recipient and are to be affixed to any copy of * software made or distributed by the recipient that contains a copy or * derivative of this software. * * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include unsigned nfsTestReaderPri = 80; struct nfsTestReq_ { unsigned offset; /* file offset */ int size; /* IN: block size to read (must be < 8192), OUT: bytes actually read */ void *buf; /* data buffer address */ }; /* Queue for sending requests to parallel reader tasks */ rtems_id nfsTestRQ = 0; /* Queue to pickup replies from parallel reader tasks */ rtems_id nfsTestAQ = 0; /* Reader task; opens its own file descriptor * and works on requests: * - obtain request from request queue. * - lseek to the requested file offset * - NFS read into buffer * - queue reply. * * Note that this implementation is very simple * - no full error checking. * - file is opened/closed by thread * it's main purpose is running quick tests. */ static rtems_task nfsTestReader(rtems_task_argument arg) { int fd = open((char*)arg,O_RDONLY); unsigned long s; struct nfsTestReq_ r; rtems_status_code sc; if ( fd < 0 ) { perror("nfsReader: opening file"); goto cleanup; } do { s = sizeof(r); sc = rtems_message_queue_receive(nfsTestRQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if ( RTEMS_SUCCESSFUL != sc ) { rtems_error(sc, "(Error) reading from message queue"); goto cleanup; } if ( !r.buf ) { /* They send a NULL buffer as a shutdown request */ break; } #ifdef DEBUG printf("Reader: reading offset %u, size %i to %p ... ", r.offset, r.size, r.buf); #endif /* seek to requested offset */ lseek(fd, r.offset, SEEK_SET); r.size = read(fd, r.buf, r.size); #ifdef DEBUG printf("got %i\n",r.size); #endif rtems_message_queue_send(nfsTestAQ, &r, sizeof(r)); } while (1) ; cleanup: if ( fd >= 0 ) close(fd); rtems_task_delete(RTEMS_SELF); } /* helper to create and start a reader task */ static rtems_id taskSpawn(char *filenm, int inst) { rtems_status_code sc; rtems_id tid; sc = rtems_task_create( rtems_build_name('n','t','t','0'+inst), nfsTestReaderPri, 1400, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &tid); if ( RTEMS_SUCCESSFUL != sc ) { rtems_error(sc,"(Error) Creating nfs reader task %i",inst); return 0; } sc = rtems_task_start(tid, nfsTestReader, (rtems_task_argument)filenm); if ( RTEMS_SUCCESSFUL != sc ) { rtems_error(sc,"(Error) Staritng nfs reader task %i",inst); rtems_task_delete(tid); return 0; } return tid; } /* * Read nrd*sz bytes into 'buf' from file offset 'off' * using 'nrd' parallel reader tasks to do the job. * This helper routine schedules 'nrd' requests to * the reader tasks and waits for all requests to * finish. * * RETURNS: number of bytes read or -1 (error). * * CAVEATS: * - assumes read requests always return 'sz' bytes * unless the end of file is reached. * THIS ASSUMPTION SHOULD NOT BE MADE WHEN WRITING * ANY 'REAL' CODE. */ static int nfsTestReadBigbuf(char *buf, int off, int sz, int nrd) { int i,rval=0; struct nfsTestReq_ r; r.buf = buf; r.size = sz; r.offset = off; /* send out parallel requests */ for (i=0; i= 0 ) { rval += r.size; } } } return rval; } /* Main test routine * * Read file 'fname' usint 'nrd' parallel reader tasks, * each operating on chunks of 'sz' bytes. * * RETURNS: time elapsed in milliseconds. This is measured * using the system clock. Hence, for the result * to be meaningful, the file must be big enough. * */ rtems_interval nfsTestRead(char *fnam, int sz, int nrd) { int i; unsigned off; rtems_interval now=-1, then, tickspsec; rtems_status_code sc; int fd=-1; char *buf=0; if ( nrd < 0 ) nrd = 0; if ( sz < 0 || sz > 8192 ) { fprintf(stderr,"\n"); return -1; } nfsTestRQ = nfsTestAQ = 0; /* Allocate buffer */ if ( ! (buf=malloc(sz*(nrd ? nrd : 1))) ) { perror("allocating buffer"); goto cleanup; } /* Don't bother proceeding if we can't open the file for reading */ if ( (fd=open(fnam,O_RDONLY)) < 0 ) { perror("opening file"); goto cleanup; } if ( nrd ) { close(fd); fd = -1; } /* Create request queue */ if ( nrd ) { sc = rtems_message_queue_create( rtems_build_name('n','t','r','q'), nrd, sizeof(struct nfsTestReq_), RTEMS_DEFAULT_ATTRIBUTES, & nfsTestRQ ); if ( RTEMS_SUCCESSFUL != sc ) { rtems_error(sc, "(Error) creating request queue"); nfsTestRQ = 0; goto cleanup; } /* Spawn reader tasks */ for ( i=0; i 0 ) { #ifdef DEBUG printf("bigbuf got %i\n", i); #endif off += i; } } else { while ( (i = read(fd, buf, sz)) > 0 ) /* nothing else to do */; if ( i < 0 ) { perror("reading"); goto cleanup; } } now = rtems_clock_get_ticks_since_boot(); now = (now-then)*1000; ticksspec = rtems_clock_get_ticks_per_second(); now /= tickspsec; /* time in ms */ cleanup: if ( fd >= 0 ) close(fd); if ( nfsTestRQ ) { /* request tasks to shutdown by sending NULL buf request */ struct nfsTestReq_ r; r.buf = 0; for ( i=0; i