summaryrefslogtreecommitdiffstats
path: root/services/nfsclient/nfsTest.c
diff options
context:
space:
mode:
Diffstat (limited to 'services/nfsclient/nfsTest.c')
-rw-r--r--services/nfsclient/nfsTest.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/services/nfsclient/nfsTest.c b/services/nfsclient/nfsTest.c
new file mode 100644
index 00000000..18bd0a3c
--- /dev/null
+++ b/services/nfsclient/nfsTest.c
@@ -0,0 +1,379 @@
+/* Test program for evaluating NFS read throughput */
+
+/* Author: Till Straumann <strauman@slac.stanford.edu>, 2006 */
+
+/* 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).
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (NFS-2 client implementation for RTEMS) was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 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 <rtems.h>
+#include <rtems/error.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+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<nrd; i++) {
+ rtems_message_queue_send(nfsTestRQ, &r, sizeof(r));
+ r.offset += sz;
+ r.buf += sz;
+ }
+ /* wait for answers */
+ for (i=0; i<nrd; i++) {
+ unsigned long s = sizeof(r);
+ rtems_message_queue_receive(nfsTestAQ, &r, &s, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if ( r.size < 0 ) {
+ fprintf(stderr,"A reader failed\n");
+ rval = -1;
+ } else {
+ /* FIXME sanity checks:
+ * - catch case where any-but-last read returns < sz
+ */
+ if ( rval >= 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<nrd; i++ ) {
+ if ( ! taskSpawn(fnam, i) )
+ goto cleanup;
+ }
+
+ /* Create reply queue */
+ sc = rtems_message_queue_create(
+ rtems_build_name('n','t','a','q'),
+ nrd,
+ sizeof(struct nfsTestReq_),
+ RTEMS_DEFAULT_ATTRIBUTES,
+ & nfsTestAQ );
+
+ if ( RTEMS_SUCCESSFUL != sc ) {
+ rtems_error(sc, "(Error) creating reply queue");
+ nfsTestAQ = 0;
+ goto cleanup;
+ }
+ }
+
+ /* Timed main loop */
+ then = rtems_clock_get_ticks_since_boot();
+
+ if ( nrd ) {
+ off = 0;
+ while ((i = nfsTestReadBigbuf(buf, off, sz, nrd)) > 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<nrd; i++ ) {
+ rtems_message_queue_send( nfsTestRQ, &r, sizeof(r) );
+ }
+ /* cheat: instead of proper synchronization with shutdown we simply
+ * delay for a second...
+ */
+ rtems_task_wake_after( tickspsec );
+ rtems_message_queue_delete( nfsTestRQ );
+ }
+ if ( nfsTestAQ )
+ rtems_message_queue_delete( nfsTestAQ );
+ free(buf);
+ return now;
+}