diskdevice-model.c

来自「一个用在mips体系结构中的操作系统」· C语言 代码 · 共 1,659 行 · 第 1/4 页

C
1,659
字号
/* * Copyright (C) 1996-1998 by the Board of Trustees *    of Leland Stanford Junior University. *  * This file is part of the SimOS distribution.  * See LICENSE file for terms of the license.  * *//* @TITLE "diskdevice-model.c: Simulate a disk device"*//*  * diskdevice-model - simulating an HP97560 disk devices, and the bus. *    THIS FILE IS INCLUDED BY DISKDEVICE.C. * * exported functions: *   DiskDeviceInit *   DiskDeviceDone *   DiskDeviceTransfer *   DiskDeviceStart *   DiskDeviceFinish *   DiskDeviceSync *   DiskDeviceShape * * David Kotz 1993-94, Song Bac Toh 1994 * *  There is a Dartmouth College Technical Report that describes this code. * The number is PCS-TR94-220.  It can be found for ftp at cs.dartmouth.edu. *//* $Id: diskdevice-model.c,v 1.15 1998/02/10 00:36:33 bosch Exp $ */#include <math.h>#ifndef SOLO#include "simtypes.h"#include "simos_interface.h"#include "cpu_interface.h"#endif#ifdef DFK# include "diskdevice-dfk.h"  /* only used when in context of DFK's program */#else# include "modularize.h"      /* miscellaneous stuff */#endif /* DFK */#include "diskevent.h"	      /* link to diskevent.c */#include "diskdevice.h"	      /* defines interface to this module */#include "diskdevices.h"      /* defines details about different devices *//* debugging *//* you probably want to turn on tracing in diskevent.c too *//* #define DEBUG */ #ifdef DEBUG# define DEBUG_TRACE(x) {CYCLE_COUNTING_OFF; printf x; printf("\r"); CYCLE_COUNTING_ON;}#else# define DEBUG_TRACE(x) {}#endif /* DEBUG */static boolean first_init = TRUE;/* @SUBTITLE "Per-disk status structure" *//* structure that contains information about status of the disk */typedef struct diskstatus{    boolean done;	      /* is the device done being used? */    /* when the current request started and finished */    TICS startTime;	      /* when request started (NEVER if none) */    TICS finishTime;	      /* when previous request finished */    boolean reading;	      /* TRUE if reading, FALSE if writing */    /* a way to communicate a new request from DiskDeviceXfer to Controller */    ulong newSector;	      /* the first sector to transfer  */    ulong newLastSector;      /* the last sector to transfer */    boolean newReading;	      /* TRUE if reading, FALSE if writing */    boolean newHaveBuffer;    /* does the host have the buffer yet? */    /* state of the disk - which track are heads at now? */    boolean track;	      /* current track */    /* There is no need to keep track of the disk rotational position since     * we are assuming that disk head is at position 0 at time 0.     * The range for disk head position is 0~1 with (position 0 == position 1)     */     boolean activeBusXfer;    /* is this bus actively using the bus? */    boolean activeRead;	      /* is a read diskxfer happening now? */    boolean cancelPrefetch;   /* should we cancel an outstanding prefetch? */    DDEvent *prefetchReq;     /* a cancellable EndDiskXfer request */    boolean needMove;	      /* do we need DiskMove before restarting xfers */    TICS    wantBus;	      /* after what time would we like to use bus */    boolean haveBuffer;	      /* do we have the buffer at host end of bus? */    boolean wantSync;	      /* host wants to know when write-behind done */    TICS    restartController; /* when should Controller() be run again *//* Each variable below contains a logical sector number. *  firstCachedSector always contains the sector of the lowest-numbered * sector in the cache. *  nextDiskXfer and nextBusXfer always contain the number of the * sector to be transferred next. *  currentDiskXfer and currentBusXfer contains the number of the * sector currently being transferred. *   * Some invariants: *  When reading,  *    FCS <= CBX <= NBX <= CDX <= NDX *    FCS is not actually in the cache unless FCS < NBX *  When writing,  *    FCS <= CDX <= NDX <= CBX <= NBX *    FCS is not actually in the cache unless FCS < NDX *  NBX-CBX == 0 or 1 *    if 0, no bus transfer is active *    if 1, sector CBX is going across the bus *  NDX-CDX == 0 or 1 *    if 0, no disk transfer is active *    if 1, sector CBX is going across the bus */    ulong firstCachedSector;  /* sector at head of cache */    ulong currentDiskXfer;    /* sector currently being transferred */    ulong nextDiskXfer;	      /* sector to be transferred next */    ulong currentBusXfer;     /* sector currently being transferred */    ulong nextBusXfer;	      /* sector to be transferred next *//* macros to find state of disk cache  */#define CACHE_SLOTS_USED(disk) ((dstatus[disk].reading ?     \				dstatus[disk].nextDiskXfer \				:dstatus[disk].nextBusXfer) \    	    	    	        - dstatus[disk].firstCachedSector)#define CACHE_EMPTY(disk)      (CACHE_SLOTS_USED(disk) <= 0)#define CACHE_FULL(disk)       (CACHE_SLOTS_USED(disk) >= CACHE_SLOTS)    ulong firstSector;	      /* first sector involved in current request */    ulong lastSector;	      /* last sector involved in current request */    int wait_thread;	      /* what thread (STID) is waiting on request */    int busId;		      /* which bus do we use (for tracing)? */    int *busOwner;	      /* (*busOwner == BUS_FREE_ ==> bus is free */    NCQUEUE *busq;	      /* place to queue disks waiting for bus */    FILE *datafile;	      /* Unix file to read/write the data from/to */} diskstatus;/* data structure keeping the status of the disks */PRIVATE struct diskstatus dstatus[NO_OF_DISKS];/* Records to go on the busq;  * basically, an event to schedule when bus is free  */typedef struct busitem {  int disk;  REQUESTCODE event;} busItem;/* @SUBTITLE "Utility functions" */PRIVATE TICS MoveTime_t(int disk, ulong lsector, TICS eventTime);PRIVATE TICS MoveNextTime_t(int disk, ulong lsector, TICS eventTime); PRIVATE ulong Logical2Physical(ulong lsector);PRIVATE TICS RotDelay_t(int disk, ulong lsector, TICS eventTime);PRIVATE TICS SeekTime_t(ulong dcylinders, ulong dtracks, TICS eventTime);PRIVATE void SanityCheck(int disk, ulong lsector, ulong nsectors);PRIVATE boolean GrabBus(int disk, TICS *grabTime, TICS eventTime);PRIVATE void WaitForBus(int disk, REQUESTCODE event, TICS eventTime);PRIVATE void PassBus(int disk, TICS eventTime);/* Print out the requests scheduled (in diskevent.c) */extern void DDPrintRequests(void);/* @SUBTITLE "Event-handler functions" */PRIVATE void DiskMove(int disk, TICS now);PRIVATE void StartDiskXfer(int disk, TICS now);PRIVATE void EndDiskXfer(int disk, TICS now);PRIVATE void ConsiderBusXfer(int disk, TICS now);PRIVATE void StartBusXfer(int disk, TICS now);PRIVATE void EndBusXfer(int disk, TICS now);PRIVATE void SendCommand(int disk, TICS now);PRIVATE void EndCommand(int disk, TICS now);PRIVATE void Controller(int disk, TICS now);PRIVATE void SendSyncCommand(int disk, TICS now);PRIVATE void EndSyncCommand(int disk, TICS now);PRIVATE void SendBufferMesg(int disk, TICS eventTime);PRIVATE void EndBufferMesg(int disk, TICS eventTime);PRIVATE void SendDoneMessage(int disk, TICS now);PRIVATE void EndDoneMessage(int disk, TICS now);/* possible types of requests */struct dd_handlers DDhandlers[] = {    { NULL, "NOP" },    { DiskMove, "DiskMove" },    { StartDiskXfer, "StartDiskXfer" },    { EndDiskXfer, "EndDiskXfer"},    { ConsiderBusXfer, "ConsiderBusXfer" },    { StartBusXfer, "StartBusXfer" },    { EndBusXfer, "EndBusXfer" },    { SendCommand, "SendCommand" },    { EndCommand, "EndCommand" },    { Controller, "Controller" },    { SendSyncCommand, "SendSyncCommand" },    { EndSyncCommand, "EndSyncCommand" },    { SendBufferMesg, "SendBufferMesg" },    { EndBufferMesg, "EndBufferMesg" },    { SendDoneMessage, "SendDoneMessage" },    { EndDoneMessage, "EndDoneMessage" }};/* ======================================================================== *//* @SUBTITLE "DiskDeviceInit: initialize a disk" *//* The caller must provide a pointer to a boolean, which   * we use to tell when that bus is free.  We also need a NCQUEUE, with room  * for as many disks as are attached to that bus, where disks can wait * for service on the bus.   Presumably, several disks share * the same values; thus, this is how we handle bus contention. * It must not be deallocated until after DiskDeviceDone is called. * The busId is used for debugging and tracing, nothing functional. */voidDiskDeviceInit(int disk, int busId, int *busOwner, NCQUEUE *busWaitq,	       char *diskFileName){    struct diskstatus *ds = &(dstatus[disk]);      if (first_init) {	/* initialize the disk event subsystem, once only */	DDEventInit();	first_init = FALSE;    }    /* initialize the disk status */    ds->track = 0;	      /* physical track of the disk */    ds->activeRead = FALSE;    ds->activeBusXfer =  FALSE;    ds->done = FALSE;    ds->wantBus = NEVER;    ds->needMove = FALSE;    ds->cancelPrefetch = FALSE;    ds->firstSector   = 0;    ds->lastSector   = 0;    ds->reading = TRUE;                ds->busId = busId;    ds->busq = busWaitq;    ds->busOwner = busOwner;    ds->firstCachedSector = 0; /* Initially cache is empty */    ds->currentDiskXfer = 0;  /* Initially the two numbers are equal - */    ds->nextDiskXfer = 0;     /* no disk transfer. */    ds->currentBusXfer = 0;   /* Initially the two numbers are equal - */    ds->nextBusXfer = 0;      /* no bus transfer. */    ds->haveBuffer = FALSE;    ds->wantSync = FALSE;    ds->prefetchReq = NULL;    /* use these to pass parameters to Controller() */    ds->newSector = 0;	      /* no pending transfer or move request */    ds->newLastSector = 0;    /* no request is being processed */    ds->newReading = FALSE;    ds->newHaveBuffer = FALSE;    /* use this flag to determine whether we are the first time through       the controller (NEVER == first time through) */    ds->restartController = NEVER;    /* use this flag to check whether a DiskDevice* is finished */    ds->startTime = NEVER;    ds->finishTime = 0;    /* decide whether to use some Unix file as a pretend "disk" */    if (diskFileName == NULL)      ds->datafile = NULL;    /* do not use any disk file; dummy data only */    else {	/* try to open the given file as a place for this "disk"s data */	/* upon error in fopen(), ds->datafile will also be assigned NULL */	ds->datafile = fopen(diskFileName, "r+");	/* file does not exist, try to create the file */	if (ds->datafile == NULL) {	    ds->datafile = fopen(diskFileName, "w+");	    /* can't even create it, fail it */	    INVARIANT3(ds->datafile != NULL,		       "DiskDeviceInit: disk file %s cannot be opened\n",		       diskFileName);	}    }    INDEX_TIME_EVENT(EV_DISKSTATE, disk, DISKSTATE_IDLE, 0);}/* @SUBTITLE "DiskDeviceDone: cleanup" */voidDiskDeviceDone(int disk){    struct diskstatus *ds = &(dstatus[disk]);      INVARIANT3(!ds->activeBusXfer, 	       "DiskDeviceDone: disk %d bus transfer still active\n", disk);    INVARIANT3(ds->wantBus == NEVER, 	       "DiskDeviceDone: disk %d still wants the bus\n", disk);    INVARIANT3(ds->startTime == NEVER,	       "DiskDeviceDone %d: previous DiskDeviceStart not finished!\n",	       disk);    INVARIANT3(ds->currentDiskXfer >= ds->currentBusXfer,	       "DiskDeviceDone %d: needs Sync!\n", disk);    DEBUG_TRACE(("DiskDeviceDone \t%d\t@%s\n", disk, time_print(GetTime())));    ds->firstCachedSector = 0;  /* reset the cache */    ds->currentDiskXfer = ds->nextDiskXfer = 0;       ds->currentBusXfer = ds->nextBusXfer = 0;    if (ds->datafile != NULL)      fclose(ds->datafile);		/* close the datafile */    ds->done = TRUE;    /* record the final idle period */    if (ds->finishTime > 0)   /* this disk was used at some point */      SUM_ARRAY_METRIC(stats.diskidle, disk, 		       (GetTime() - ds->finishTime) * MSECperTIC);    INDEX_TIME_EVENT(EV_DISKIDLE, disk, 0, ds->finishTime);    INDEX_TIME_EVENT(EV_DISKSTATE, disk, DISKSTATE_IDLE, ds->finishTime);}/* @SUBTITLE "DiskDeviceStart: start a disk transaction" *//* Like DiskDeviceTransfer, but split into two parts: the first part * starts the disk moving to the appropriate location, and the second supplies * the buffer needed to complete the transaction. Must be followed by a  * corresponding Finish. */ voidDiskDeviceStart(int disk, ulong sector, ulong nsectors, boolean write){        TICS eventTime;    struct diskstatus *ds = &(dstatus[disk]);    /* check whether the caller of this function is insane */    SanityCheck(disk, sector, nsectors);      /* record the preceding idle period */    if (ds->finishTime > 0)   /* this disk has been used before */      SUM_ARRAY_METRIC(stats.diskidle, disk, 		       (GetTime() - ds->finishTime) * MSECperTIC);    INDEX_TIME_EVENT(EV_DISKIDLE, disk, 0, ds->finishTime);    /* record the event of reading or writing */    INDEX_EVENT(write ? EV_DISKWRITE : EV_DISKREAD, disk, sector);    SUM_ARRAY_METRIC(write ? stats.diskwrite : stats.diskread, disk, 1.0);    INDEX_EVENT(EV_DISKBUSY, disk, 0);      eventTime = GetTime();    ds->startTime = eventTime; /* start the disk */      DEBUG_TRACE(("DiskDeviceStart \t%d\t@%s: sector:%lu  nsectors:%lu  R/W:%d\n",		 disk, time_print(eventTime), sector, nsectors, write));    ds->newReading = !write;	    ds->newSector = sector;    /* go through all sectors */    ds->newLastSector = sector + nsectors - 1;    ds->newHaveBuffer = FALSE; /* we don't have the buffer yet */    DEBUG_TRACE(("\t"));    SendCommand(disk, eventTime); /* schedules EndCommand */    DEBUG_TRACE(("\tDiskDeviceStart \t%d\t@%s  suspending %d/%d\n",		 disk, time_print(eventTime), CURR_PROCESSOR, MY_TID));    /* now wait for EndCommand to wake us up */    ds->wait_thread = MY_STID;    thread_suspend(MY_TID);}/* @SUBTITLE "DiskDeviceFinish: finish a disk transaction" *//* See DiskDeviceStart.  This, the second call,  * BLOCKs until the transfer is complete.  There should not have been * any other requests since the corresponding Start.  Ideally, we should * not be late in calling DiskDeviceFinish, ie, not after the disk movement * has finished. */ voidDiskDeviceFinish(int disk, UserData buffer){        struct diskstatus *ds = &(dstatus[disk]);    TICS eventTime = GetTime();    ulong xferSize = ds->lastSector - ds->firstSector + 1;    INVARIANT3(ds->startTime != NEVER,	       "DiskDeviceFinish %d: no corresponding DiskDeviceStart\n",	       disk);    DEBUG_TRACE(("DiskDeviceFinish \t%d\n", disk));    /* process the request */    ds->newHaveBuffer = TRUE; /* EndBufferMesg set hasBuffer */    DEBUG_TRACE(("\t"));    SendBufferMesg(disk, eventTime);    DEBUG_TRACE(("\tDiskDeviceFinish \t%d\t@%s  suspending %d/%d\n",		 disk, time_print(eventTime), CURR_PROCESSOR, MY_TID));    ds->wait_thread = MY_STID;    thread_suspend(MY_TID);   /* send the thread to sleep */    ds->finishTime = GetTime();    AVG_ARRAY_METRIC(stats.diskwait, disk, 		     (ds->finishTime - ds->startTime) * MSECperTIC);    STDDEV_ARRAY_METRIC(stats.diskwait_sd, stats.diskwait, disk, 			(ds->finishTime - ds->startTime) * MSECperTIC);    ds->startTime = NEVER;    /* reset the disk */    /* actually read and write the data to vdisk */    if (ds->datafile != NULL) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?