

/* 
 * Library for Posix async read operations with hints.
 * Author: Don Capps
 * Company: Iozone
 * Date: 4/24/1998
 *
 * Two models are supported.  First model is a replacement for read() where the async
 * operations are performed and the requested data is bcopy()-ed back into the users 
 * buffer. The second model is a new version of read() where the caller does not 
 * supply the address of the buffer but instead is returned an address to the
 * location of the data. The second model eliminates a bcopy from the path.
 *
 * To use model #1:
 * 1. Call async_init(&pointer_on_stack,fd,direct_flag);
 *	The fd is the file descriptor for the async operations.
 *	The direct_flag sets VX_DIRECT 
 *
 * 2. Call async_read(gc, fd, ubuffer, offset, size, stride, max, depth)
 *    	Where:
 *	gc ............	is the pointer on the stack
 *	fd ............	is the file descriptor
 *	ubuffer .......	is the address of the user buffer.
 *	offset ........	is the offset in the file to begin reading
 *	size ..........	is the size of the transfer.
 *	stride ........	is the distance, in size units, to space the async reads.
 *	max ...........	is the max size of the file to be read.
 *	depth .........	is the number of async operations to perform.
 *
 * 3. Call end_async(gc) when finished.
 *	Where:
 *	gc ............ is the pointer on the stack.
 *
 * To use model #2:
 * 1. Call async_init(&pointer_on_stack,fd,direct_flag);
 *	The fd is the file descriptor for the async operations.
 *	The direct_flag sets VX_DIRECT 
 * 2. Call async_read(gc, fd, &ubuffer, offset, size, stride, max, depth)
 *    	Where:
 *	gc ............	is the pointer on the stack
 *	fd ............	is the file descriptor
 *	ubuffer .......	is the address of a pointer that will be filled in 
 *                      by the async library.
 *	offset ........	is the offset in the file to begin reading
 *	size ..........	is the size of the transfer.
 *	stride ........	is the distance, in size units, to space the async reads.
 *	max ...........	is the max size of the file to be read.
 *	depth .........	is the number of async operations to perform.
 *
 * 3. Call async_release(gc) when finished with the data that was returned.
 *    This allows the async library to reuse the memory that was filled in
 *    and returned to the user.
 *
 * 4. Call end_async(gc) when finished.
 *	Where:
 *	gc ............ is the pointer on the stack.
 *
 * To use model #1: (WRITES)
 * 1. Call async_init(&pointer_on_stack,fd,direct_flag);
 *	The fd is the file descriptor for the async operations.
 *
 * 2. Call async_write(gc, fd, ubuffer, size, offset, depth)
 *    	Where:
 *	gc ............	is the pointer on the stack
 *	fd ............	is the file descriptor
 *	ubuffer .......	is the address of the user buffer.
 *	size ..........	is the size of the transfer.
 *	offset ........	is the offset in the file to begin reading
 *	depth .........	is the number of async operations to perform.
 *
 * 4. Call end_async(gc) when finished.
 *	Where:
 *	gc ............ is the pointer on the stack.
 *
 * Notes:
 *	The intended use is to replace calls to read() with calls to
 *	async_read() and allow the user to make suggestions on 
 *	what kind of async read-ahead would be nice to have.
 *	The first transfer requested is guarenteed to be complete
 *	before returning to the caller. The async operations will
 *	be started and will also be guarenteed to have completed
 *	if the next call specifies its first request to be one
 *	that was previously performed with an async operation.
 *	
 *	The async_read_no_copy() function allows the async operations
 *	to return the data to the user and not have to perform 
 *	a bcopy of the data back into the user specified buffer 
 *	location. This model is faster but assumes that the user
 *	application has been modified to work with this model.
 *
 * 	The async_write() is intended to enhance the performance of 
 *	initial writes to a file. This is the slowest case in the write
 *	path as it must perform meta-data allocations and wait.
 */

#include <sys/types.h>
#include <aio.h>
#if defined(solaris) || defined(linux) || defined(SCO_Unixware_gcc)
#else
#include <sys/timers.h>
#endif
#include <sys/errno.h>
#include <unistd.h>
#ifndef bsd4_4
#include <malloc.h>
#endif
#ifdef VXFS
#include <sys/fs/vx_ioctl.h>
#endif

#if defined(OSFV5) || defined(linux)
#include <string.h>
#endif

#if defined(linux)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#endif

#if ((defined(solaris) && defined(__LP64__)) || defined(__s390x__))
/* If we are building for 64-bit Solaris, all functions that return pointers
 * must be declared before they are used; otherwise the compiler will assume
 * that they return ints and the top 32 bits of the pointer will be lost,
 * causing segmentation faults.  The following includes take care of this.
 * It should be safe to add these for all other OSs too, but we're only
 * doing it for Solaris now in case another OS turns out to be a special case.
 */
#include <stdio.h>
#include <stdlib.h>
#include <strings.h> /* For the BSD string functions */
#endif

void mbcopy(char *source, char *dest, size_t len);


#if !defined(solaris) && !defined(off64_t) && !defined(_OFF64_T) && !defined(__off64_t_defined) && !defined(SCO_Unixware_gcc)
typedef long long off64_t;
#endif
#if defined(OSFV5)
#include <string.h>
#endif


extern long long page_size;
extern int one;
/*
 * Internal cache entrys. Each entry on the global
 * cache, pointed to by async_init(gc) will be of
 * this structure type.
 */
char version[] = "Libasync Version $Revision: 3.21 $";
struct cache_ent {
	struct aiocb myaiocb;			/* For use in small file mode */
#ifdef _LARGEFILE64_SOURCE 
#if defined(__CrayX1__)
	aiocb64_t myaiocb64;		/* For use in large file mode */
#else
	struct aiocb64 myaiocb64;		/* For use in large file mode */
#endif 
#endif 
	long long fd;				/* File descriptor */
	long long size;				/* Size of the transfer */
	struct cache_ent *forward;		/* link to next element on cache list */
	struct cache_ent *back;			/* link to previous element on the cache list */
	long long direct;			/* flag to indicate if the buffer should be */
						/* de-allocated by library */
	char *real_address;			/* Real address to free */
	
	volatile void *oldbuf;			/* Used for firewall to prevent in flight */
						/* accidents */
	int oldfd;				/* Used for firewall to prevent in flight */
						/* accidents */
	size_t oldsize;				/* Used for firewall to prevent in flight */
						/* accidents */
};

/*
 * Head of the cache list
 */
struct cache {
	struct cache_ent *head;		/* Head of cache list */
	struct cache_ent *tail;		/* tail of cache list */
	struct cache_ent *inuse_head;	/* head of in-use list */
	long long count;		/* How many elements on the cache list */
	struct cache_ent *w_head;		/* Head of cache list */
	struct cache_ent *w_tail;		/* tail of cache list */
	long long w_count;		/* How many elements on the write list */
	};

long long max_depth;
extern int errno;
struct cache_ent *alloc_cache();
struct cache_ent *incache();
void async_init();
void end_async();
int async_read();
void takeoff_cache();
void del_cache();
void async_release();
void putoninuse();
void takeoffinuse();
struct cache_ent *allocate_write_buffer();
size_t async_write();
void async_wait_for_write();
void async_put_on_write_queue();
void async_write_finish();

/* On Solaris _LP64 will be defined by <sys/types.h> if we're compiling
 * as a 64-bit binary.  Make sure that __LP64__ gets defined in this case,
 * too -- it should be defined on the compiler command line, but let's
 * not rely on this.
 */
#if defined(_LP64)
#if !defined(__LP64__)
#define __LP64__
#endif
#endif


/***********************************************/
/* Initialization routine to setup the library */
/***********************************************/
void
async_init(gc,fd,flag)
struct cache **gc;
int fd;
int flag;
{
#ifdef VXFS
	if(flag)
		ioctl(fd,VX_SETCACHE,VX_DIRECT);
#endif
	if(*gc)
	{
		printf("Warning calling async_init two times ?\n");
		return;
	}
	*gc=(struct cache *)malloc((size_t)sizeof(struct cache));
	if(*gc == 0)
	{
		printf("Malloc failed\n");
		exit(174);
	}
	bzero(*gc,sizeof(struct cache));
#if defined(__AIX__) || defined(SCO_Unixware_gcc)
	max_depth=500;
#else
	max_depth=sysconf(_SC_AIO_MAX);
#endif
}

/***********************************************/
/* Tear down routine to shutdown the library   */
/***********************************************/
void
end_async(gc)
struct cache *gc;
{
	del_cache(gc);
	async_write_finish(gc);
	free((void *)gc);
}

/*************************************************************************
 * This routine is a generic async reader assist funtion. It takes
 * the same calling parameters as read() but also extends the
 * interface to include:
 * stride ..... For the async reads, what is the distance, in size units, 
 * 		to space the reads. Note: Stride of 0 indicates that
 *		you do not want any read-ahead.
 * max    ..... What is the maximum file offset for this operation.
 * depth  ..... How much read-ahead do you want.
 * 
 * The calls to this will guarentee to complete the read() operation
 * before returning to the caller. The completion may occur in two
 * ways. First the operation may be completed by calling aio_read()
 * and then waiting for it to complete. Second  the operation may be 
 * completed by copying the data from a cache of previously completed 
 * async operations. 
 * In the event the read to be satisfied is not in the cache then a 
 * series of async operations will be scheduled and then the first 
 * async read will be completed. In the event that the read() can be 
 * satisfied from the cache then the data is copied back to the 
 * user buffer and a series of async reads will be initiated.  If a 
 * read is issued and the cache contains data and the read can not 
 * be satisfied from the cache, then the cache is discarded, and 
 * a new cache is constructed.
 * Note: All operations are aio_read(). The series will be issued
 * as asyncs in the order requested. After all are in flight
 * then the code will wait for the manditory first read.
 *************************************************************************/

int 
async_read(gc, fd, ubuffer, offset, size, stride, max, depth)
struct cache *gc;
long long fd;
char *ubuffer;
off64_t offset;
long long size;
long long stride;
off64_t max;
long long depth;
{
	off64_t a_offset,r_offset;
	long long a_size;
	struct cache_ent *ce,*first_ce=0;
	long long i;
	ssize_t retval=0;
	ssize_t ret;
	long long start = 0;
	long long del_read=0;

	a_offset=offset;
	a_size = size;
	/*
	 * Check to see if it can be completed from the cache
	 */
	if((ce=(struct cache_ent *)incache(gc,fd,offset,size)))
	{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		while((ret=aio_error(&ce->myaiocb))== EINPROGRESS)
#else
		while((ret=aio_error64(&ce->myaiocb64))== EINPROGRESS)
#endif
#else
		while((ret=aio_error(&ce->myaiocb))== EINPROGRESS)
#endif
		{
			;
		}
		if(ret)
		{
			printf("aio_error 1: ret %d %d\n",ret,errno);
		}
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		retval=aio_return(&ce->myaiocb);
#else
#if defined(__CrayX1__)
		retval=aio_return64((aiocb64_t *)&ce->myaiocb64);
#else
		retval=aio_return64((struct aiocb64 *)&ce->myaiocb64);
#endif

#endif
#else
		retval=aio_return(&ce->myaiocb);
#endif
		if(retval > 0)
		{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			mbcopy((char *)ce->myaiocb.aio_buf,(char *)ubuffer,(size_t)retval);
#else
			mbcopy((char *)ce->myaiocb64.aio_buf,(char *)ubuffer,(size_t)retval);
#endif
#else
			mbcopy((char *)ce->myaiocb.aio_buf,(char *)ubuffer,(size_t)retval);
#endif
		}
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		if(retval < ce->myaiocb.aio_nbytes)
#else
		if(retval < ce->myaiocb64.aio_nbytes)
#endif
#else
		if(retval < ce->myaiocb.aio_nbytes)
#endif
		{
			printf("aio_return error1: ret %d %d\n",retval,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			printf("aio_return error1: fd %d offset %ld buffer %lx size %d Opcode %d\n",
				ce->myaiocb.aio_fildes,
				ce->myaiocb.aio_offset,
				(long)(ce->myaiocb.aio_buf),
				ce->myaiocb.aio_nbytes,
				ce->myaiocb.aio_lio_opcode
#else
			printf("aio_return error1: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				ce->myaiocb64.aio_fildes,
				ce->myaiocb64.aio_offset,
				(long)(ce->myaiocb64.aio_buf),
				ce->myaiocb64.aio_nbytes,
				ce->myaiocb64.aio_lio_opcode
#endif
#else
			printf("aio_return error1: fd %d offset %d buffer %lx size %d Opcode %d\n",
				ce->myaiocb.aio_fildes,
				ce->myaiocb.aio_offset,
				(long)(ce->myaiocb.aio_buf),
				ce->myaiocb.aio_nbytes,
				ce->myaiocb.aio_lio_opcode
#endif
				);
		}
		ce->direct=0;
		takeoff_cache(gc,ce);
	}else
	{
		/*
		 * Clear the cache and issue the first request async()
		 */
		del_cache(gc);
		del_read++;
		first_ce=alloc_cache(gc,fd,offset,size,(long long)LIO_READ);
again:
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		ret=aio_read(&first_ce->myaiocb);
#else
		ret=aio_read64(&first_ce->myaiocb64);
#endif
#else
		ret=aio_read(&first_ce->myaiocb);
#endif
		if(ret!=0)
		{
			if(errno==EAGAIN)
				goto again;
			else
				printf("error returned from aio_read(). Ret %d errno %d\n",ret,errno);
		}
	}
	if(stride==0)	 /* User does not want read-ahead */
		goto out;
	if(a_offset<0)	/* Before beginning of file */
		goto out;
	if(a_offset+size>max)	/* After end of file */
		goto out;
	if(depth >=(max_depth-1))
		depth=max_depth-1;
	if(depth==0)
		goto out;
	if(gc->count > 1)
		start=depth-1;
	for(i=start;i<depth;i++)	/* Issue read-aheads for the depth specified */
	{
		r_offset=a_offset+((i+1)*(stride*a_size));
		if(r_offset<0)
			continue;
		if(r_offset+size > max)
			continue;
		if((ce=incache(gc,fd,r_offset,a_size)))
			continue;
		ce=alloc_cache(gc,fd,r_offset,a_size,(long long)LIO_READ);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		ret=aio_read(&ce->myaiocb);
#else
		ret=aio_read64(&ce->myaiocb64);
#endif
#else
		ret=aio_read(&ce->myaiocb);
#endif
		if(ret!=0)
		{
			takeoff_cache(gc,ce);
			break;
		}
	}			
out:
	if(del_read)	/* Wait for the first read to complete */
	{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		while((ret=aio_error(&first_ce->myaiocb))== EINPROGRESS)
#else
		while((ret=aio_error64(&first_ce->myaiocb64))== EINPROGRESS)
#endif
#else
		while((ret=aio_error(&first_ce->myaiocb))== EINPROGRESS)
#endif
		{
			;
		}
		if(ret)
			printf("aio_error 2: ret %d %d\n",ret,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		retval=aio_return(&first_ce->myaiocb);
#else
		retval=aio_return64(&first_ce->myaiocb64);
#endif
#else
		retval=aio_return(&first_ce->myaiocb);
#endif
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		if(retval < first_ce->myaiocb.aio_nbytes)
#else
		if(retval < first_ce->myaiocb64.aio_nbytes)
#endif
#else
		if(retval < first_ce->myaiocb.aio_nbytes)
#endif
		{
			printf("aio_return error2: ret %d %d\n",retval,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			printf("aio_return error2: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				first_ce->myaiocb.aio_fildes,
				first_ce->myaiocb.aio_offset,
				(long)(first_ce->myaiocb.aio_buf),
				first_ce->myaiocb.aio_nbytes,
				first_ce->myaiocb.aio_lio_opcode
#else
			printf("aio_return error2: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				first_ce->myaiocb64.aio_fildes,
				first_ce->myaiocb64.aio_offset,
				(long)(first_ce->myaiocb64.aio_buf),
				first_ce->myaiocb64.aio_nbytes,
				first_ce->myaiocb64.aio_lio_opcode
#endif
#else
			printf("aio_return error2: fd %d offset %d buffer %lx size %d Opcode %d\n",
				first_ce->myaiocb.aio_fildes,
				first_ce->myaiocb.aio_offset,
				(long)(first_ce->myaiocb.aio_buf),
				first_ce->myaiocb.aio_nbytes,
				first_ce->myaiocb.aio_lio_opcode
#endif
				);
		}
		if(retval > 0)
		{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			mbcopy((char *)first_ce->myaiocb.aio_buf,(char *)ubuffer,(size_t)retval);
#else
			mbcopy((char *)first_ce->myaiocb64.aio_buf,(char *)ubuffer,(size_t)retval);
#endif
#else
			mbcopy((char *)first_ce->myaiocb.aio_buf,(char *)ubuffer,(size_t)retval);
#endif
		}
		first_ce->direct=0;
		takeoff_cache(gc,first_ce);
	}
	return((int)retval);	
}

/************************************************************************
 * This routine allocates a cache_entry. It contains the 
 * aiocb block as well as linkage for use in the cache mechanism.
 * The space allocated here will be released after the cache entry
 * has been consumed. The routine takeoff_cache() will be called
 * after the data has been copied to user buffer or when the
 * cache is purged. The routine takeoff_cache() will also release
 * all memory associated with this cache entry.
 ************************************************************************/

struct cache_ent *
alloc_cache(gc,fd,offset,size,op)
struct cache *gc;
long long fd,size,op;
off64_t offset;
{
	struct cache_ent *ce;
	long temp;
	ce=(struct cache_ent *)malloc((size_t)sizeof(struct cache_ent));
	if(ce == (struct cache_ent *)0)
	{
		printf("Malloc failed\n");
		exit(175);
	}
	bzero(ce,sizeof(struct cache_ent));
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	ce->myaiocb.aio_fildes=(int)fd;
	ce->myaiocb.aio_offset=(off64_t)offset;
	ce->real_address = (char *)malloc((size_t)(size+page_size));
	temp=(long)ce->real_address;
	temp = (temp+page_size) & ~(page_size-1);
	ce->myaiocb.aio_buf=(volatile void *)temp;
	if(ce->myaiocb.aio_buf == 0)
#else
	ce->myaiocb64.aio_fildes=(int)fd;
	ce->myaiocb64.aio_offset=(off64_t)offset;
	ce->real_address = (char *)malloc((size_t)(size+page_size));
	temp=(long)ce->real_address;
	temp = (temp+page_size) & ~(page_size-1);
	ce->myaiocb64.aio_buf=(volatile void *)temp;
	if(ce->myaiocb64.aio_buf == 0)
#endif
#else
	ce->myaiocb.aio_fildes=(int)fd;
	ce->myaiocb.aio_offset=(off_t)offset;
	ce->real_address = (char *)malloc((size_t)(size+page_size));
	temp=(long)ce->real_address;
	temp = (temp+page_size) & ~(page_size-1);
	ce->myaiocb.aio_buf=(volatile void *)temp;
	if(ce->myaiocb.aio_buf == 0)
#endif
	{
		printf("Malloc failed\n");
		exit(176);
	}
	/*bzero(ce->myaiocb.aio_buf,(size_t)size);*/
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	ce->myaiocb.aio_reqprio=0;
	ce->myaiocb.aio_nbytes=(size_t)size;
	ce->myaiocb.aio_sigevent.sigev_notify=SIGEV_NONE;
	ce->myaiocb.aio_lio_opcode=(int)op;
#else
	ce->myaiocb64.aio_reqprio=0;
	ce->myaiocb64.aio_nbytes=(size_t)size;
	ce->myaiocb64.aio_sigevent.sigev_notify=SIGEV_NONE;
	ce->myaiocb64.aio_lio_opcode=(int)op;
#endif
#else
	ce->myaiocb.aio_reqprio=0;
	ce->myaiocb.aio_nbytes=(size_t)size;
	ce->myaiocb.aio_sigevent.sigev_notify=SIGEV_NONE;
	ce->myaiocb.aio_lio_opcode=(int)op;
#endif
	ce->fd=(int)fd;
	ce->forward=0;
	ce->back=gc->tail;
	if(gc->tail)
		gc->tail->forward = ce;
	gc->tail= ce;
	if(!gc->head)
		gc->head=ce;
	gc->count++;
	return(ce);
}

/************************************************************************
 * This routine checks to see if the requested data is in the
 * cache. 
*************************************************************************/
struct cache_ent *
incache(gc,fd,offset,size)
struct cache *gc;
long long fd,size;
off64_t offset;
{
	struct cache_ent *move;
	if(gc->head==0)
	{
		return(0);
	}
	move=gc->head;
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	while(move)
	{
		if((move->fd == fd) && (move->myaiocb.aio_offset==(off64_t)offset) &&
			((size_t)size==move->myaiocb.aio_nbytes))
			{
				return(move);
			}
		move=move->forward;
	}
#else
	while(move)
	{
		if((move->fd == fd) && (move->myaiocb64.aio_offset==(off64_t)offset) &&
			((size_t)size==move->myaiocb64.aio_nbytes))
			{
				return(move);
			}
		move=move->forward;
	}
#endif
#else
	while(move)
	{
		if((move->fd == fd) && (move->myaiocb.aio_offset==(off_t)offset) &&
			((size_t)size==move->myaiocb.aio_nbytes))
			{
				return(move);
			}
		move=move->forward;
	}
#endif
	return(0);
}

/************************************************************************
 * This routine removes a specific cache entry from the cache, and
 * releases all memory associated witht the cache entry (if not direct).
*************************************************************************/

void
takeoff_cache(gc,ce)
struct cache *gc;
struct cache_ent *ce;
{
	struct cache_ent *move;
	long long found;
	move=gc->head;
	if(move==ce) /* Head of list */
	{

		gc->head=ce->forward;
		if(gc->head)
			gc->head->back=0;
		else
			gc->tail = 0;
		if(!ce->direct)
		{
			free((void *)(ce->real_address));
			free((void *)ce);
		}
		gc->count--;
		return;
	}
	found=0;
	while(move)
	{
		if(move==ce)
		{
			if(move->forward)
			{
				move->forward->back=move->back;
			}
			if(move->back)
			{
				move->back->forward=move->forward;
			}
			found=1;
			break;
		}
		else
		{
			move=move->forward;
		}
	}
	if(gc->head == ce)
		gc->tail = ce;
	if(!found)
		printf("Internal Error in takeoff cache\n");
	move=gc->head;
	if(!ce->direct)
	{
		free((void *)(ce->real_address));
		free((void *)ce);
	}
	gc->count--;
}

/************************************************************************
 * This routine is used to purge the entire cache. This is called when
 * the cache contains data but the incomming read was not able to 
 * be satisfied from the cache. This indicates that the previous
 * async read-ahead was not correct and a new pattern is emerging. 
 ************************************************************************/
void
del_cache(gc)
struct cache *gc;
{
	struct cache_ent *ce;
	ssize_t ret;
	ce=gc->head;
	while(1)
	{
		ce=gc->head;
		if(ce==0)
			return;
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		while((ret = aio_cancel(0,&ce->myaiocb))==AIO_NOTCANCELED)
#else
		while((ret = aio_cancel64(0,&ce->myaiocb64))==AIO_NOTCANCELED)
#endif
#else
		while((ret = aio_cancel(0,&ce->myaiocb))==AIO_NOTCANCELED)
#endif
			; 

#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		ret = aio_return(&ce->myaiocb);
#else
		ret = aio_return64(&ce->myaiocb64);
#endif
#else
		ret = aio_return(&ce->myaiocb);
#endif
		ce->direct=0;
		takeoff_cache(gc,ce);	  /* remove from cache */
	}
}

/************************************************************************
 * Like its sister async_read() this function performs async I/O for 
 * all buffers but it differs in that it expects the caller to 
 * request a pointer to the data to be returned instead of handing
 * the function a location to put the data. This will allow the
 * async I/O to be performed and does not require any bcopy to be
 * done to put the data back into the location specified by the caller.
 ************************************************************************/
int
async_read_no_copy(gc, fd, ubuffer, offset, size, stride, max, depth)
struct cache *gc;
long long fd;
char **ubuffer;
off64_t offset;
long long size;
long long stride;
off64_t max;
long long depth;
{
	off64_t a_offset,r_offset;
	long long a_size;
	struct cache_ent *ce,*first_ce=0;
	long long i;
	ssize_t retval=0;
	ssize_t ret;
	long long del_read=0;
	long long start=0;

	a_offset=offset;
	a_size = size;
	/*
	 * Check to see if it can be completed from the cache
	 */
	if((ce=(struct cache_ent *)incache(gc,fd,offset,size)))
	{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		while((ret=aio_error(&ce->myaiocb))== EINPROGRESS)
#else
		while((ret=aio_error64(&ce->myaiocb64))== EINPROGRESS)
#endif
#else
		while((ret=aio_error(&ce->myaiocb))== EINPROGRESS)
#endif
		{
			;
		}
		if(ret)
			printf("aio_error 3: ret %d %d\n",ret,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		if(ce->oldbuf != ce->myaiocb.aio_buf ||
			ce->oldfd != ce->myaiocb.aio_fildes ||
			ce->oldsize != ce->myaiocb.aio_nbytes) 
#else
		if(ce->oldbuf != ce->myaiocb64.aio_buf ||
			ce->oldfd != ce->myaiocb64.aio_fildes ||
			ce->oldsize != ce->myaiocb64.aio_nbytes) 
#endif
#else
		if(ce->oldbuf != ce->myaiocb.aio_buf ||
			ce->oldfd != ce->myaiocb.aio_fildes ||
			ce->oldsize != ce->myaiocb.aio_nbytes) 
#endif
			printf("It changed in flight\n");
			
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		retval=aio_return(&ce->myaiocb);
#else
		retval=aio_return64(&ce->myaiocb64);
#endif
#else
		retval=aio_return(&ce->myaiocb);
#endif
		if(retval > 0)
		{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			*ubuffer=(char *)ce->myaiocb.aio_buf;
#else
			*ubuffer=(char *)ce->myaiocb64.aio_buf;
#endif
#else
			*ubuffer=(char *)ce->myaiocb.aio_buf;
#endif
		}else
			*ubuffer=0;
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		if(retval < ce->myaiocb.aio_nbytes)
#else
		if(retval < ce->myaiocb64.aio_nbytes)
#endif
#else
		if(retval < ce->myaiocb.aio_nbytes)
#endif
		{
			printf("aio_return error4: ret %d %d\n",retval,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			printf("aio_return error4: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				ce->myaiocb.aio_fildes,
				ce->myaiocb.aio_offset,
				(long)(ce->myaiocb.aio_buf),
				ce->myaiocb.aio_nbytes,
				ce->myaiocb.aio_lio_opcode
#else
			printf("aio_return error4: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				ce->myaiocb64.aio_fildes,
				ce->myaiocb64.aio_offset,
				(long)(ce->myaiocb64.aio_buf),
				ce->myaiocb64.aio_nbytes,
				ce->myaiocb64.aio_lio_opcode
#endif
#else
			printf("aio_return error4: fd %d offset %d buffer %lx size %d Opcode %d\n",
				ce->myaiocb.aio_fildes,
				ce->myaiocb.aio_offset,
				(long)(ce->myaiocb.aio_buf),
				ce->myaiocb.aio_nbytes,
				ce->myaiocb.aio_lio_opcode
#endif
				);
		}
		ce->direct=1;
		takeoff_cache(gc,ce); /* do not delete buffer*/
		putoninuse(gc,ce);
	}else
	{
		/*
		 * Clear the cache and issue the first request async()
		 */
		del_cache(gc);
		del_read++;
		first_ce=alloc_cache(gc,fd,offset,size,(long long)LIO_READ); /* allocate buffer */
		/*printf("allocated buffer/read %x offset %d\n",first_ce->myaiocb.aio_buf,offset);*/
again:
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		first_ce->oldbuf=first_ce->myaiocb.aio_buf;
		first_ce->oldfd=first_ce->myaiocb.aio_fildes;
		first_ce->oldsize=first_ce->myaiocb.aio_nbytes;
		ret=aio_read(&first_ce->myaiocb);
#else
		first_ce->oldbuf=first_ce->myaiocb64.aio_buf;
		first_ce->oldfd=first_ce->myaiocb64.aio_fildes;
		first_ce->oldsize=first_ce->myaiocb64.aio_nbytes;
		ret=aio_read64(&first_ce->myaiocb64);
#endif
#else
		first_ce->oldbuf=first_ce->myaiocb.aio_buf;
		first_ce->oldfd=first_ce->myaiocb.aio_fildes;
		first_ce->oldsize=first_ce->myaiocb.aio_nbytes;
		ret=aio_read(&first_ce->myaiocb);
#endif
		if(ret!=0)
		{
			if(errno==EAGAIN)
				goto again;
			else
				printf("error returned from aio_read(). Ret %d errno %d\n",ret,errno);
		}
	}
	if(stride==0)	 /* User does not want read-ahead */
		goto out;
	if(a_offset<0)	/* Before beginning of file */
		goto out;
	if(a_offset+size>max)	/* After end of file */
		goto out;
	if(depth >=(max_depth-1))
		depth=max_depth-1;
	if(depth==0)
		goto out;
	if(gc->count > 1)
		start=depth-1;
	for(i=start;i<depth;i++)	/* Issue read-aheads for the depth specified */
	{
		r_offset=a_offset+((i+1)*(stride*a_size));
		if(r_offset<0)
			continue;
		if(r_offset+size > max)
			continue;
		if((ce=incache(gc,fd,r_offset,a_size)))
			continue;
		ce=alloc_cache(gc,fd,r_offset,a_size,(long long)LIO_READ);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		ce->oldbuf=ce->myaiocb.aio_buf;
		ce->oldfd=ce->myaiocb.aio_fildes;
		ce->oldsize=ce->myaiocb.aio_nbytes;
		ret=aio_read(&ce->myaiocb);
#else
		ce->oldbuf=ce->myaiocb64.aio_buf;
		ce->oldfd=ce->myaiocb64.aio_fildes;
		ce->oldsize=ce->myaiocb64.aio_nbytes;
		ret=aio_read64(&ce->myaiocb64);
#endif
#else
		ce->oldbuf=ce->myaiocb.aio_buf;
		ce->oldfd=ce->myaiocb.aio_fildes;
		ce->oldsize=ce->myaiocb.aio_nbytes;
		ret=aio_read(&ce->myaiocb);
#endif
		if(ret!=0)
		{
			takeoff_cache(gc,ce);
			break;
		}
	}			
out:
	if(del_read)	/* Wait for the first read to complete */
	{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		while((ret=aio_error(&first_ce->myaiocb))== EINPROGRESS)
#else
		while((ret=aio_error64(&first_ce->myaiocb64))== EINPROGRESS)
#endif
#else
		while((ret=aio_error(&first_ce->myaiocb))== EINPROGRESS)
#endif
		{
			;
		}
		if(ret)
			printf("aio_error 4: ret %d %d\n",ret,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		if(first_ce->oldbuf != first_ce->myaiocb.aio_buf ||
			first_ce->oldfd != first_ce->myaiocb.aio_fildes ||
			first_ce->oldsize != first_ce->myaiocb.aio_nbytes) 
			printf("It changed in flight2\n");
		retval=aio_return(&first_ce->myaiocb);
#else
		if(first_ce->oldbuf != first_ce->myaiocb64.aio_buf ||
			first_ce->oldfd != first_ce->myaiocb64.aio_fildes ||
			first_ce->oldsize != first_ce->myaiocb64.aio_nbytes) 
			printf("It changed in flight2\n");
		retval=aio_return64(&first_ce->myaiocb64);
#endif
#else
		if(first_ce->oldbuf != first_ce->myaiocb.aio_buf ||
			first_ce->oldfd != first_ce->myaiocb.aio_fildes ||
			first_ce->oldsize != first_ce->myaiocb.aio_nbytes) 
			printf("It changed in flight2\n");
		retval=aio_return(&first_ce->myaiocb);
#endif
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		if(retval < first_ce->myaiocb.aio_nbytes)
#else
		if(retval < first_ce->myaiocb64.aio_nbytes)
#endif
#else
		if(retval < first_ce->myaiocb.aio_nbytes)
#endif
		{
			printf("aio_return error5: ret %d %d\n",retval,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			printf("aio_return error5: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				first_ce->myaiocb.aio_fildes,
				first_ce->myaiocb.aio_offset,
				(long)(first_ce->myaiocb.aio_buf),
				first_ce->myaiocb.aio_nbytes,
				first_ce->myaiocb.aio_lio_opcode
#else
			printf("aio_return error5: fd %d offset %lld buffer %lx size %d Opcode %d\n",
				first_ce->myaiocb64.aio_fildes,
				first_ce->myaiocb64.aio_offset,
				(long)(first_ce->myaiocb64.aio_buf),
				first_ce->myaiocb64.aio_nbytes,
				first_ce->myaiocb64.aio_lio_opcode
#endif
#else
			printf("aio_return error5: fd %d offset %ld buffer %lx size %d Opcode %d\n",
				first_ce->myaiocb.aio_fildes,
				first_ce->myaiocb.aio_offset,
				(long)(first_ce->myaiocb.aio_buf),
				first_ce->myaiocb.aio_nbytes,
				first_ce->myaiocb.aio_lio_opcode
#endif
				);
		}
		if(retval > 0)
		{
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			*ubuffer=(char *)first_ce->myaiocb.aio_buf;
#else
			*ubuffer=(char *)first_ce->myaiocb64.aio_buf;
#endif
#else
			*ubuffer=(char *)first_ce->myaiocb.aio_buf;
#endif
		}else
			*ubuffer=(char *)0;
		first_ce->direct=1;	 /* do not delete the buffer */
		takeoff_cache(gc,first_ce);
		putoninuse(gc,first_ce);
	}
	return((int)retval);	
}

/************************************************************************
 * The caller is now finished with the data that was provided so
 * the library is now free to return the memory to the pool for later
 * reuse.
 ************************************************************************/
void
async_release(gc)
struct cache *gc;
{
	takeoffinuse(gc);
}


/************************************************************************
 * Put the buffer on the inuse list. When the user is finished with 
 * the buffer it will call back into async_release and the items on the 
 * inuse list will be deallocated.
 ************************************************************************/
void
putoninuse(gc,entry)
struct cache *gc;
struct cache_ent *entry;
{
	if(gc->inuse_head)
		entry->forward=gc->inuse_head;
	else
		entry->forward=0;
	gc->inuse_head=entry;
}

/************************************************************************
 * This is called when the application is finished with the data that
 * was provided. The memory may now be returned to the pool.
 ************************************************************************/
void
takeoffinuse(gc)
struct cache *gc;
{
	struct cache_ent *ce;
	if(gc->inuse_head==0)
		printf("Takeoffinuse error\n");
	ce=gc->inuse_head;
	gc->inuse_head=gc->inuse_head->forward;
	
	if(gc->inuse_head !=0)
		printf("Error in take off inuse\n");
	free((void*)(ce->real_address));
	free(ce);
}

/*************************************************************************
 * This routine is a generic async writer assist funtion. It takes
 * the same calling parameters as write() but also extends the
 * interface to include:
 * 
 * offset ..... offset in the file.
 * depth  ..... How much read-ahead do you want.
 * 
 *************************************************************************/
size_t
async_write(gc,fd,buffer,size,offset,depth)
struct cache *gc;
long long fd,size;
char *buffer;
off64_t offset;
long long depth;
{
	struct cache_ent *ce;
	size_t ret;
	ce=allocate_write_buffer(gc,fd,offset,size,(long long)LIO_WRITE,depth,0LL,(char *)0,(char *)0);
	ce->direct=0;	 /* not direct. Lib supplies buffer and must free it */
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	mbcopy(buffer,(char *)(ce->myaiocb.aio_buf),(size_t)size);
#else
	mbcopy(buffer,(char *)(ce->myaiocb64.aio_buf),(size_t)size);
#endif
#else
	mbcopy(buffer,(char *)(ce->myaiocb.aio_buf),(size_t)size);
#endif
	async_put_on_write_queue(gc,ce);
	/*
	printf("asw: fd %d offset %lld, size %d\n",ce->myaiocb64.aio_fildes,
		ce->myaiocb64.aio_offset,
		ce->myaiocb64.aio_nbytes);
	*/	

again:
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	ret=aio_write(&ce->myaiocb);
#else
	ret=aio_write64(&ce->myaiocb64);
#endif
#else
	ret=aio_write(&ce->myaiocb);
#endif
	if(ret==-1)
	{
		if(errno==EAGAIN)
		{
			async_wait_for_write(gc);
			goto again;
		}
		if(errno==0)
		{
			/* Compensate for bug in async library */
			async_wait_for_write(gc);
			goto again;
		}
		else
		{
			printf("Error in aio_write: ret %d errno %d count %lld\n",ret,errno,gc->w_count);
			/*
			printf("aio_write_no_copy: fd %d buffer %x offset %lld size %d\n",
				ce->myaiocb64.aio_fildes,
				ce->myaiocb64.aio_buf,
				ce->myaiocb64.aio_offset,
				ce->myaiocb64.aio_nbytes);
			*/
			exit(177);
		}
	} 
	return((ssize_t)size);
}

/*************************************************************************
 * Allocate a write aiocb and write buffer of the size specified. Also 
 * put some extra buffer padding so that VX_DIRECT can do its job when
 * needed.
 *************************************************************************/

struct cache_ent *
allocate_write_buffer(gc,fd,offset,size,op,w_depth,direct,buffer,free_addr)
struct cache *gc;
long long fd,size,op;
off64_t offset;
long long w_depth;
long long direct;
char *buffer,*free_addr;
{
	struct cache_ent *ce;
	long temp;
	if(fd==0LL)
	{
		printf("Setting up write buffer insane\n");
		exit(178);
	}
	if(gc->w_count > w_depth)
		async_wait_for_write(gc);
	ce=(struct cache_ent *)malloc((size_t)sizeof(struct cache_ent));
	if(ce == (struct cache_ent *)0)
	{
		printf("Malloc failed 1\n");
		exit(179);
	}
	bzero(ce,sizeof(struct cache_ent));
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	ce->myaiocb.aio_fildes=(int)fd;
	ce->myaiocb.aio_offset=(off64_t)offset;
	if(!direct)
	{
		ce->real_address = (char *)malloc((size_t)(size+page_size));
		temp=(long)ce->real_address;
		temp = (temp+page_size) & ~(page_size-1);
		ce->myaiocb.aio_buf=(volatile void *)temp;
	}else
	{
		ce->myaiocb.aio_buf=(volatile void *)buffer;
		ce->real_address=(char *)free_addr;
	}
	if(ce->myaiocb.aio_buf == 0)
#else
	ce->myaiocb64.aio_fildes=(int)fd;
	ce->myaiocb64.aio_offset=(off64_t)offset;
	if(!direct)
	{
		ce->real_address = (char *)malloc((size_t)(size+page_size));
		temp=(long)ce->real_address;
		temp = (temp+page_size) & ~(page_size-1);
		ce->myaiocb64.aio_buf=(volatile void *)temp;
	}
	else
	{
		ce->myaiocb64.aio_buf=(volatile void *)buffer;
		ce->real_address=(char *)free_addr;
	}
	if(ce->myaiocb64.aio_buf == 0)
#endif
#else
	ce->myaiocb.aio_fildes=(int)fd;
	ce->myaiocb.aio_offset=(off_t)offset;
	if(!direct)
	{
		ce->real_address = (char *)malloc((size_t)(size+page_size));
		temp=(long)ce->real_address;
		temp = (temp+page_size) & ~(page_size-1);
		ce->myaiocb.aio_buf=(volatile void *)temp;
	}
	else
	{
		ce->myaiocb.aio_buf=(volatile void *)buffer;
		ce->real_address=(char *)free_addr;
	}
	if(ce->myaiocb.aio_buf == 0)
#endif
	{
		printf("Malloc failed 2\n");
		exit(180);
	}
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	ce->myaiocb.aio_reqprio=0;
	ce->myaiocb.aio_nbytes=(size_t)size;
	ce->myaiocb.aio_sigevent.sigev_notify=SIGEV_NONE;
	ce->myaiocb.aio_lio_opcode=(int)op;
#else
	ce->myaiocb64.aio_reqprio=0;
	ce->myaiocb64.aio_nbytes=(size_t)size;
	ce->myaiocb64.aio_sigevent.sigev_notify=SIGEV_NONE;
	ce->myaiocb64.aio_lio_opcode=(int)op;
#endif
#else
	ce->myaiocb.aio_reqprio=0;
	ce->myaiocb.aio_nbytes=(size_t)size;
	ce->myaiocb.aio_sigevent.sigev_notify=SIGEV_NONE;
	ce->myaiocb.aio_lio_opcode=(int)op;
#endif
	ce->fd=(int)fd;
	return(ce);
}

/*************************************************************************
 * Put it on the outbound queue.
 *************************************************************************/

void
async_put_on_write_queue(gc,ce)
struct cache *gc;
struct cache_ent *ce;
{
	ce->forward=0;
	ce->back=gc->w_tail;
	if(gc->w_tail)
		gc->w_tail->forward = ce;
	gc->w_tail= ce;
	if(!gc->w_head)
		gc->w_head=ce;
	gc->w_count++;
	return;
}

/*************************************************************************
 * Cleanup all outstanding writes
 *************************************************************************/
void
async_write_finish(gc)
struct cache *gc;
{
	while(gc->w_head)
	{
		/*printf("async_write_finish: Waiting for buffer %x to finish\n",gc->w_head->myaiocb64.aio_buf);*/
		async_wait_for_write(gc);
	}
}

/*************************************************************************
 * Wait for an I/O to finish
 *************************************************************************/

void
async_wait_for_write(gc)
struct cache *gc;
{
	struct cache_ent *ce;
	size_t ret,retval;
	if(gc->w_head==0)
		return;
	ce=gc->w_head;
	gc->w_head=ce->forward;
	gc->w_count--;
	ce->forward=0;
	if(ce==gc->w_tail)
		gc->w_tail=0;
	/*printf("Wait for buffer %x  offset %lld  size %d to finish\n",
		ce->myaiocb64.aio_buf,
		ce->myaiocb64.aio_offset,
		ce->myaiocb64.aio_nbytes);
	printf("write count %lld \n",gc->w_count);
	*/
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	while((ret=aio_error(&ce->myaiocb))== EINPROGRESS)
#else
	while((ret=aio_error64(&ce->myaiocb64))== EINPROGRESS)
#endif
#else
	while((ret=aio_error(&ce->myaiocb))== EINPROGRESS)
#endif
	{
		;
	}
	if(ret)
	{
		printf("aio_error 5: ret %d %d\n",ret,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
		printf("fd %d offset %lld size %d\n",
			ce->myaiocb.aio_fildes,
			ce->myaiocb.aio_offset,
			ce->myaiocb.aio_nbytes);
#else
		printf("fd %d offset %lld size %d\n",
			ce->myaiocb64.aio_fildes,
			ce->myaiocb64.aio_offset,
			ce->myaiocb64.aio_nbytes);
#endif
#else
		printf("fd %d offset %lld size %d\n",
			ce->myaiocb.aio_fildes,
			ce->myaiocb.aio_offset,
			ce->myaiocb.aio_nbytes);
#endif
		exit(181);
	}

#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	retval=aio_return(&ce->myaiocb);
#else
#if defined(__CrayX1__)
	retval=aio_return64((aiocb64_t *)&ce->myaiocb64);
#else
	retval=aio_return64((struct aiocb64 *)&ce->myaiocb64);
#endif

#endif
#else
	retval=aio_return(&ce->myaiocb);
#endif
	if((int)retval < 0)
	{
		printf("aio_return error: %d\n",errno);
	}

	if(!ce->direct)
	{
		/* printf("Freeing buffer %x\n",ce->real_address);*/
		free((void *)(ce->real_address));
		free((void *)ce);
	}

}

/*************************************************************************
 * This routine is a generic async writer assist funtion. It takes
 * the same calling parameters as write() but also extends the
 * interface to include:
 * 
 * offset ..... offset in the file.
 * depth  ..... How much read-ahead do you want.
 * free_addr .. address of memory to free after write is completed.
 * 
 *************************************************************************/
size_t
async_write_no_copy(gc,fd,buffer,size,offset,depth,free_addr)
struct cache *gc;
long long fd,size;
char *buffer;
off64_t offset;
long long depth;
char *free_addr;
{
	struct cache_ent *ce;
	size_t ret;
	long long direct = 1;
	ce=allocate_write_buffer(gc,fd,offset,size,(long long)LIO_WRITE,depth,direct,buffer,free_addr);
	ce->direct=0;	/* have library de-allocate the buffer */
	async_put_on_write_queue(gc,ce);
	/*
	printf("awnc: fd %d offset %lld, size %d\n",ce->myaiocb64.aio_fildes,
		ce->myaiocb64.aio_offset,
		ce->myaiocb64.aio_nbytes);
	*/

again:
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
	ret=aio_write(&ce->myaiocb);
#else
	ret=aio_write64(&ce->myaiocb64);
#endif
#else
	ret=aio_write(&ce->myaiocb);
#endif
	if(ret==-1)
	{
		if(errno==EAGAIN)
		{
			async_wait_for_write(gc);
			goto again;
		}
		if(errno==0)
		{
			/* Compensate for bug in async library */
			async_wait_for_write(gc);
			goto again;
		}
		else
		{
			printf("Error in aio_write: ret %d errno %d\n",ret,errno);
#ifdef _LARGEFILE64_SOURCE 
#ifdef __LP64__
			printf("aio_write_no_copy: fd %d buffer %lx offset %lld size %d\n",
				ce->myaiocb.aio_fildes,
				(long)(ce->myaiocb.aio_buf),
				ce->myaiocb.aio_offset,
				ce->myaiocb.aio_nbytes);
#else
			printf("aio_write_no_copy: fd %d buffer %lx offset %lld size %d\n",
				ce->myaiocb64.aio_fildes,
				(long)(ce->myaiocb64.aio_buf),
				ce->myaiocb64.aio_offset,
				ce->myaiocb64.aio_nbytes);
#endif
#else
			printf("aio_write_no_copy: fd %d buffer %lx offset %ld size %d\n",
				ce->myaiocb.aio_fildes,
				(long)(ce->myaiocb.aio_buf),
				ce->myaiocb.aio_offset,
				ce->myaiocb.aio_nbytes);
#endif
			exit(182);
		}
	} 
	else	
	{
		return((ssize_t)size);
	}
}

void mbcopy(source, dest, len)
char *source,*dest;
size_t len;
{
	int i;
	for(i=0;i<len;i++)
		*dest++=*source++;
}
