/* rawaio.c - perform asynchronous reads              */
/* this program attempts to generate num_streams read    */
/* requests asynchronously then waits to collect them    */
/* through an aiowait() function call. This function     */
/* can be passed a timeval parameter if the user need    */
/* only wait to specified time. A parameter of NULL will */
/* get the process to wait indefinitely.                 */

#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef __SOLARIS__
#include <sys/asynch.h>
#else
#include <aio.h>
#endif /* __SOLARIS__ */
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>

#include "global.h"
#include "timer.h"
#include "options.h"
#include "since.h"

// #define DEBUG

int aioReadMeg(char*, int);

int how_big;
int dev_fd;

struct streamdata {		/* data structure for each stream */
	int fileno;		/* The filenumber for this stream */
	int pos;		/* The position in the file       */
        char buf[MAX_BUF_SIZE]; /* This stream's buffer.          */
#ifdef __SOLARIS__
        off64_t offset;         /* The file's offset.             */
        aio_result_t result;    /* The result of the aioread      */
#else
        off_t offset;
#endif /* __SOLARIS__ */
} streamdata[MAX_STREAMS];

int main(int argc, char *argv[]) {
	int i = 0, j = 0;
	int ctr = 0, errctr = 0;
#ifdef __SOLARIS__
	aio_result_t *rp;
#else
        struct aiocb *rp;
        int rv = 0;
#endif /* __SOLARIS__ */

	/* see options.c */
	extern int block_size, num_streams, bit_rate, num_blocks;

	/* see options.c */
        parseArgs(argc, argv);
        if(validateParams() != VALIDATION_SUCCESS) {
                printUsage();
                exit(1);
        }   

        how_big = num_blocks;
        
        /* Seed the random number generator */
        srandom(1);

	/* Open the device */
#ifdef __SOLARIS__
        dev_fd = open64(RAWDEV, O_RDONLY | O_LARGEFILE, NULL);
#else
        dev_fd = open(RAWDEV, O_RDONLY, NULL);
#endif /* __SOLARIS__ */
        if(dev_fd < 1) { 
                printf("open error");
                exit(1);
        }

	for (i = 0; i < num_streams; i++) {
		streamdata[i].fileno = i;
                /* Each stream starts at a random position within it's buffer area for a more realistic simulation */
                streamdata[i].pos = random() % how_big;
                /* Each stream's buffer area is spaced across the whole disk.
                 * The disks we're using have DISKSIZE MEGS, so we space across that. */
		streamdata[i].offset = ( (unsigned long long) i * ((DISKSIZE / 100) / num_streams)  ) * ONE_MEGA * (unsigned long long)how_big;
#ifdef DEBUG		
		printf("Initialized stream: %d\n",i);
#endif
	}

#ifdef DEBUG
	printf("how_big: %d, block_size: %d, file_size: %d\n",
		how_big, block_size, how_big * block_size);
#endif
        
	/* See timer.c */
	init_timer(block_size, bit_rate);

        /* See since.c */
        init_since();

	for (i = 0; i < how_big; i++) {
                now_since();
		for(j = 0; j < num_streams; j++) {
#ifdef DEBUG
                	printf("Requesting chunk %d in stream %d.\n",i, j);
#endif
                	if(aioReadMeg(streamdata[j].buf, j) < 0) {
                       		fprintf(stderr, "Exiting because of stream %d.\n", j);
                        	exit(1);
                	}
#ifdef DEBUG
                	fprintf (stderr, "Stream %d block %d.\n", j, i);
#endif
		}

		for(j = 0; j < num_streams; j++) {
#ifdef __SOLARIS__
                        rp = aiowait(NULL);
                        if(rp == (aio_result_t *)-1) {
                                fprintf(stderr, "Fatal: aiowait returned -1.");
                                exit(1);
                        }
#ifdef DEBUG
                        printf("return: %d, error: %d\n", rp->aio_return, rp->aio_errno);
#endif /* DEBUG */
#else /* __SOLARIS__ */
                        rv = aio_waitcomplete(&rp, NULL);
                        if(rv == -1) {
                                perror("Fatal: aiowait returned -1");
                                exit(1);
                        }
#ifdef DEBUG
                        printf("return: %d\n", rv);
#endif /* DEBUG */
#endif /* __SOLARIS__ */
                }

                for(j = 0; j < num_streams; j++) {
#ifdef __SOLARIS__
                        if(streamdata[j].result.aio_return != block_size) {
                                fprintf(stderr, "Fatal error: result (%d)not equal block_size in stream %d\n", streamdata[j].result.aio_return, j);
                                exit(1);
                        }
#endif /* __SOLARIS__ */
                        streamdata[j].pos += 1;
#ifdef DEBUG
                        printf("Chunk %d in stream %d returned from its read.\n",i, j);
#endif
                }
                since();
		++ctr;
		if(sleep_end_quantum_increment()) {
			++errctr;
		}
		printf("Period %d, %d errors\n",ctr,errctr);
        }
        end_since();
        printf("Total errors: %d\n", errctr);
	exit(0);
} /* end main */





int aioReadMeg(char *buf, int fn) {
        int rv;
	//streamdata* ns;

#ifdef __SOLARIS__
        off64_t offset;
#else
	off_t offset;
        struct aiocb iocb;
#endif /* __SOLARIS__ */

	extern int block_size;

        offset = streamdata[fn].offset + ONE_MEGA * streamdata[fn].pos;
#ifdef DEBUG
        fprintf(stderr, "Commencing read %d, %d, %d, 0x%llX\n", fn, (int)&buf, block_size, offset);
#endif
#ifdef __SOLARIS__
        rv = aioread64(dev_fd, buf, block_size, offset, SEEK_SET, &(streamdata[fn].result));
#else
        memset(&iocb, NULL, sizeof(struct aiocb));
        iocb.aio_nbytes = block_size;
        iocb.aio_fildes = dev_fd;
        iocb.aio_offset = offset;
        iocb.aio_buf = buf;
        rv = aio_read(&iocb);        
#endif /* __SOLARIS__ */
        if(rv != 0) {
                perror("Can't aioread");
                exit(1);
        }
        if(++streamdata[fn].pos >= 100) {
                streamdata[fn].pos = 0;
        }

#ifdef DEBUG
        fprintf(stderr, "Initiated aioread.\n");
#endif
        return rv;
}
