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 + -
显示快捷键?