/*
 * fsPageOps.c --
 *
 *	The has the page-in, page-out, and page-copy routines used
 *	by the VM system.
 *
 * Copyright 1987 Regents of the University of California
 * All rights reserved.
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /r3/kupfer/spriteserver/src/sprited/fs/RCS/fsPageOps.c,v 1.3 92/01/21 15:02:59 kupfer Exp $ SPRITE (Berkeley)";
#endif not lint


#include <sprite.h>
#include <ckalloc.h>

#include <fs.h>
#include <fsNameOps.h>
#include <fsStat.h>
#include <fscache.h>
#include <fsdm.h>
#include <fsio.h>
#include <fslcl.h>
#include <fsprefix.h>
#include <fsrmt.h>
#include <fsutil.h>
#include <fsutilTrace.h>
#include <net.h>
#include <rpc.h>
#include <vm.h>


/*
 *----------------------------------------------------------------------
 *
 * Fs_PageRead --
 *
 *	Read in a virtual memory page.  This routine bypasses the cache.
 *
 * Results:
 *	A return status, SUCCESS if successful.
 *
 * Side effects:
 *	The page is filled with data read from the indicate offset.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_PageRead(streamPtr, pageAddr, offset, numBytes, pageType)
    Fs_Stream	*streamPtr;	/* Swap file stream. */
    Address	pageAddr;	/* Pointer to page. */
    int		offset;		/* Offset in file. */
    int		numBytes;	/* Number of bytes in page. */
    Fs_PageType	pageType;	/* CODE HEAP or SWAP */
{
    ReturnStatus		status = SUCCESS;
    Fs_IOParam			io;	/* Write parameter block */
    register Fs_IOParam		*ioPtr = &io;
    Fs_IOReply			reply;	/* Return length, signal */
    Boolean			retry;	/* Error retry flag */

    FsSetIOParam(ioPtr, pageAddr, numBytes, offset, 0);
    reply.length = 0;
    reply.flags = 0;
    reply.signal = 0;
    reply.code = 0;
    /*
     * We currently copy initialized
     * HEAP pages into the cache.  Also, for non-SWAP
     * pages a client should still check its cache to make sure it
     * didn't just generate the data.
     */
    if ((pageType == FS_SWAP_PAGE) || (pageType == FS_SHARED_PAGE)) {
	ioPtr->flags |= FS_SWAP;
    } else if (pageType == FS_HEAP_PAGE) {
	ioPtr->flags |= FS_HEAP;
    } else if (pageType == FS_CODE_PAGE) {
	ioPtr->flags &= ~(FS_SWAP | FS_HEAP);
    }

    do {
	retry = FALSE;
	status = (fsio_StreamOpTable[streamPtr->ioHandlePtr->fileID.type].pageRead) (streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
#ifdef lint
	status = Fsio_FileRead(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
	status = FsrmtFilePageRead(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
	status = FspdevPseudoStreamRead(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
	status = Fsrmt_Read(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
#endif /* lint */

	if (status == RPC_TIMEOUT || status == FS_STALE_HANDLE ||
	    status == RPC_SERVICE_DISABLED) {
	    /*
	     * The server is down so we wait for it.  This blocks
	     * the user process doing the page fault.
	     */
	    Net_HostPrint(streamPtr->ioHandlePtr->fileID.serverID,
		    "Fs_PageRead waiting\n");
	    status = Fsutil_WaitForRecovery(streamPtr->ioHandlePtr, status);
	    if (status == SUCCESS) {
		retry = TRUE;
	    } else {
		printf("Fs_PageRead recovery failed <%x>\n", status);
	    }
	} else if (status == FS_WOULD_BLOCK) {
	    /*
	     * The remote server is so hosed that it can't
	     * deliver us a block.  There is no good way
	     * to wait.  Retry immediately?  Pound pound pound?
	     */
	    retry = TRUE;
	    printf("Fs_PageRead: blocked, waiting 1 min\n");
	    (void)Sync_WaitTime(time_OneMinute);
	} else if (status != SUCCESS) {
	    printf("Fs_PageRead: Read failed <%x>\n", status);
	}
    } while (retry);

    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_PageWrite --
 *
 *	Write out a virtual memory page.  This is a simplified form of
 *	Fs_Write that invokes the stream's pageWrite operation.  The
 *	stream-specific procedure that is invoked can be the regular
 *	write routine (as it is for local files)  or something more fancy
 *	(as is true for remote files).  This passes the FS_SWAP flag down
 *	to the pageWrite routine so a regular write routine can detect
 *	the difference.
 *
 * Results:
 *	A return status, SUCCESS if successful.
 *
 * Side effects:
 *	The data in the buffer is written to the file at the indicated offset.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_PageWrite(streamPtr, pageAddr, offset, numBytes, toDisk)
    Fs_Stream	*streamPtr;	/* Swap file stream. */
    Address	pageAddr;	/* Pointer to page. */
    int		offset;		/* Offset in file. */
    int		numBytes;	/* Number of bytes in page. */
    Boolean	toDisk;		/* TRUE to write through to disk. */
{
    ReturnStatus		status = SUCCESS;
    Fs_IOParam			io;	/* Write parameter block */
    register Fs_IOParam		*ioPtr = &io;
    Fs_IOReply			reply;	/* Return length, signal */

    FsSetIOParam(ioPtr, pageAddr, numBytes, offset, FS_SWAP |
	    (toDisk? FS_WRITE_TO_DISK : 0));
    reply.length = 0;
    reply.flags = 0;
    reply.signal = 0;
    reply.code = 0;

    status = (fsio_StreamOpTable[streamPtr->ioHandlePtr->fileID.type].pageWrite) (streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
#ifdef lint
    status = Fsio_FileWrite(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
    status = FsrmtFilePageWrite(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
    status = FspdevPseudoStreamWrite(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
    status = Fsrmt_Write(streamPtr, ioPtr, (Sync_RemoteWaiter *)NIL, &reply);
#endif /* lint */

    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_PageCopy --
 *
 *	Copy the file system blocks in the source swap file to the destination
 *	swap file.  NOTE:  This is still specific to local and remote files.
 *	This means that only Sprite files can be used for swap space.
 *
 * Results:
 *	A return status, SUCCESS if successful.
 *
 * Side effects:
 *	Appropriate blocks in the source file are copied to the dest file.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_PageCopy(srcStreamPtr, destStreamPtr, offset, numBytes)
    Fs_Stream	*srcStreamPtr;	/* File to copy blocks from. */
    Fs_Stream	*destStreamPtr;	/* File to copy blocks to. */
    int		offset;		/* Offset in file. */
    int		numBytes;	/* Number of bytes in page. */
{
    int				lastBlock;
    register	Fs_HandleHeader	*srcHdrPtr;
    register	Fs_HandleHeader	*destHdrPtr;
    ReturnStatus		status = SUCCESS;
    int				i;
    Boolean			retry;
    Address			swapPageCopy = (Address) NIL;
    Boolean			swapPageAllocated = FALSE;

    srcHdrPtr = srcStreamPtr->ioHandlePtr;
    destHdrPtr = destStreamPtr->ioHandlePtr;
    lastBlock = (unsigned int) (offset + numBytes - 1) / FS_BLOCK_SIZE;

    /*
     * Copy all blocks in the page.
     */
    for (i = (unsigned int) offset / FS_BLOCK_SIZE; i <= lastBlock; i++) {
	do {
	    retry = FALSE;
	    if (srcHdrPtr->fileID.serverID != destHdrPtr->fileID.serverID) {
		/*
		 * The swap files are on different machines, so we need to read
		 * from one and write to the other.
		 */
		if (!swapPageAllocated) {
		    swapPageCopy = (Address) ckalloc(Vm_GetPageSize());
		}
		swapPageAllocated = TRUE;
		status =
			Fs_PageRead(srcStreamPtr, swapPageCopy, offset,
			numBytes, FS_SWAP_PAGE);
		if (status != SUCCESS) {
		    /* Fs_PageRead handles recovery itself, so we don't here. */
		    break;
		}
		status =
			Fs_PageWrite(destStreamPtr, swapPageCopy, offset,
			numBytes, TRUE);
	    } else {
		status = (fsio_StreamOpTable[srcHdrPtr->fileID.type].blockCopy)
			(srcHdrPtr, destHdrPtr, i);
#ifdef lint
		status = Fsio_FileBlockCopy(srcHdrPtr, destHdrPtr, i);
		status = Fsrmt_BlockCopy(srcHdrPtr, destHdrPtr, i);
#endif /* lint */
	    }
	    if (status != SUCCESS) {
		if (status == RPC_TIMEOUT || status == RPC_SERVICE_DISABLED ||
			status == FS_STALE_HANDLE) {
		    /*
		     * The server is down so we wait for it.  This just blocks
		     * the user process doing the page fault.
		     */
		    Net_HostPrint(srcHdrPtr->fileID.serverID,
			    "Fs_PageCopy waiting for recovery\n");
		    status = Fsutil_WaitForRecovery(srcStreamPtr->ioHandlePtr,
				status);
		    if (status == SUCCESS) {
			retry = TRUE;
		    } else {
			printf("Fs_PageCopy, recovery failed <%x>\n", status);
			if (swapPageAllocated) {
			    ckfree(swapPageCopy);
			}
			return(status);
		    }
		} else {
		    printf("Fs_PageCopy: Copy failed <%x>\n", status);
		    if (swapPageAllocated) {
			ckfree(swapPageCopy);
		    }
		    return(status);
		}
	    }
	} while (retry);

    }
    if (swapPageAllocated) {
	ckfree(swapPageCopy);
    }
    return(status);
}
