Main Page | Alphabetical List | Class List | File List | Class Members | File Members

disk.cc

Go to the documentation of this file.
00001 // disk.cc 00002 // Routines to simulate a physical disk device; reading and writing 00003 // to the disk is simulated as reading and writing to a UNIX file. 00004 // See disk.h for details about the behavior of disks (and 00005 // therefore about the behavior of this simulation). 00006 // 00007 // Disk operations are asynchronous, so we have to invoke an interrupt 00008 // handler when the simulated operation completes. 00009 // 00010 // DO NOT CHANGE -- part of the machine emulation 00011 // 00012 // Copyright (c) 1992-1993 The Regents of the University of California. 00013 // All rights reserved. See copyright.h for copyright notice and limitation 00014 // of liability and disclaimer of warranty provisions. 00015 00016 #include "copyright.h" 00017 #include "disk.h" 00018 #include "system.h" 00019 00020 // We put this at the front of the UNIX file representing the 00021 // disk, to make it less likely we will accidentally treat a useful file 00022 // as a disk (which would probably trash the file's contents). 00023 #define MagicNumber 0x456789ab 00024 #define MagicSize sizeof(int) 00025 00026 #define DiskSize (MagicSize + (NumSectors * SectorSize)) 00027 00028 // dummy procedure because we can't take a pointer of a member function 00029 static void DiskDone(int arg) { ((Disk *)arg)->HandleInterrupt(); } 00030 00031 //---------------------------------------------------------------------- 00032 // Disk::Disk() 00033 // Initialize a simulated disk. Open the UNIX file (creating it 00034 // if it doesn't exist), and check the magic number to make sure it's 00035 // ok to treat it as Nachos disk storage. 00036 // 00037 // "name" -- text name of the file simulating the Nachos disk 00038 // "callWhenDone" -- interrupt handler to be called when disk read/write 00039 // request completes 00040 // "callArg" -- argument to pass the interrupt handler 00041 //---------------------------------------------------------------------- 00042 00043 Disk::Disk(char* name, VoidFunctionPtr callWhenDone, int callArg) 00044 { 00045 int magicNum; 00046 int tmp = 0; 00047 00048 DEBUG('d', "Initializing the disk, 0x%x 0x%x\n", callWhenDone, callArg); 00049 handler = callWhenDone; 00050 handlerArg = callArg; 00051 lastSector = 0; 00052 bufferInit = 0; 00053 00054 fileno = OpenForReadWrite(name, FALSE); 00055 if (fileno >= 0) { // file exists, check magic number 00056 Read(fileno, (char *) &magicNum, MagicSize); 00057 ASSERT(magicNum == MagicNumber); 00058 } else { // file doesn't exist, create it 00059 fileno = OpenForWrite(name); 00060 magicNum = MagicNumber; 00061 WriteFile(fileno, (char *) &magicNum, MagicSize); // write magic number 00062 00063 // need to write at end of file, so that reads will not return EOF 00064 Lseek(fileno, DiskSize - sizeof(int), 0); 00065 WriteFile(fileno, (char *)&tmp, sizeof(int)); 00066 } 00067 active = FALSE; 00068 } 00069 00070 //---------------------------------------------------------------------- 00071 // Disk::~Disk() 00072 // Clean up disk simulation, by closing the UNIX file representing the 00073 // disk. 00074 //---------------------------------------------------------------------- 00075 00076 Disk::~Disk() 00077 { 00078 Close(fileno); 00079 } 00080 00081 //---------------------------------------------------------------------- 00082 // Disk::PrintSector() 00083 // Dump the data in a disk read/write request, for debugging. 00084 //---------------------------------------------------------------------- 00085 00086 static void 00087 PrintSector (bool writing, int sector, char *data) 00088 { 00089 int *p = (int *) data; 00090 00091 if (writing) 00092 printf("Writing sector: %d\n", sector); 00093 else 00094 printf("Reading sector: %d\n", sector); 00095 for (unsigned int i = 0; i < (SectorSize/sizeof(int)); i++) 00096 printf("%x ", p[i]); 00097 printf("\n"); 00098 } 00099 00100 //---------------------------------------------------------------------- 00101 // Disk::ReadRequest/WriteRequest 00102 // Simulate a request to read/write a single disk sector 00103 // Do the read/write immediately to the UNIX file 00104 // Set up an interrupt handler to be called later, 00105 // that will notify the caller when the simulator says 00106 // the operation has completed. 00107 // 00108 // Note that a disk only allows an entire sector to be read/written, 00109 // not part of a sector. 00110 // 00111 // "sectorNumber" -- the disk sector to read/write 00112 // "data" -- the bytes to be written, the buffer to hold the incoming bytes 00113 //---------------------------------------------------------------------- 00114 00115 void 00116 Disk::ReadRequest(int sectorNumber, char* data) 00117 { 00118 int ticks = ComputeLatency(sectorNumber, FALSE); 00119 00120 ASSERT(!active); // only one request at a time 00121 ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors)); 00122 00123 DEBUG('d', "Reading from sector %d\n", sectorNumber); 00124 Lseek(fileno, SectorSize * sectorNumber + MagicSize, 0); 00125 Read(fileno, data, SectorSize); 00126 if (DebugIsEnabled('d')) 00127 PrintSector(FALSE, sectorNumber, data); 00128 00129 active = TRUE; 00130 UpdateLast(sectorNumber); 00131 stats->numDiskReads++; 00132 interrupt->Schedule(DiskDone, (int) this, ticks, DiskInt); 00133 } 00134 00135 void 00136 Disk::WriteRequest(int sectorNumber, char* data) 00137 { 00138 int ticks = ComputeLatency(sectorNumber, TRUE); 00139 00140 ASSERT(!active); 00141 ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors)); 00142 00143 DEBUG('d', "Writing to sector %d\n", sectorNumber); 00144 Lseek(fileno, SectorSize * sectorNumber + MagicSize, 0); 00145 WriteFile(fileno, data, SectorSize); 00146 if (DebugIsEnabled('d')) 00147 PrintSector(TRUE, sectorNumber, data); 00148 00149 active = TRUE; 00150 UpdateLast(sectorNumber); 00151 stats->numDiskWrites++; 00152 interrupt->Schedule(DiskDone, (int) this, ticks, DiskInt); 00153 } 00154 00155 //---------------------------------------------------------------------- 00156 // Disk::HandleInterrupt() 00157 // Called when it is time to invoke the disk interrupt handler, 00158 // to tell the Nachos kernel that the disk request is done. 00159 //---------------------------------------------------------------------- 00160 00161 void 00162 Disk::HandleInterrupt () 00163 { 00164 active = FALSE; 00165 (*handler)(handlerArg); 00166 } 00167 00168 //---------------------------------------------------------------------- 00169 // Disk::TimeToSeek() 00170 // Returns how long it will take to position the disk head over the correct 00171 // track on the disk. Since when we finish seeking, we are likely 00172 // to be in the middle of a sector that is rotating past the head, 00173 // we also return how long until the head is at the next sector boundary. 00174 // 00175 // Disk seeks at one track per SeekTime ticks (cf. stats.h) 00176 // and rotates at one sector per RotationTime ticks 00177 //---------------------------------------------------------------------- 00178 00179 int 00180 Disk::TimeToSeek(int newSector, int *rotation) 00181 { 00182 int newTrack = newSector / SectorsPerTrack; 00183 int oldTrack = lastSector / SectorsPerTrack; 00184 int seek = abs(newTrack - oldTrack) * SeekTime; 00185 // how long will seek take? 00186 int over = (stats->totalTicks + seek) % RotationTime; 00187 // will we be in the middle of a sector when 00188 // we finish the seek? 00189 00190 *rotation = 0; 00191 if (over > 0) // if so, need to round up to next full sector 00192 *rotation = RotationTime - over; 00193 return seek; 00194 } 00195 00196 //---------------------------------------------------------------------- 00197 // Disk::ModuloDiff() 00198 // Return number of sectors of rotational delay between target sector 00199 // "to" and current sector position "from" 00200 //---------------------------------------------------------------------- 00201 00202 int 00203 Disk::ModuloDiff(int to, int from) 00204 { 00205 int toOffset = to % SectorsPerTrack; 00206 int fromOffset = from % SectorsPerTrack; 00207 00208 return ((toOffset - fromOffset) + SectorsPerTrack) % SectorsPerTrack; 00209 } 00210 00211 //---------------------------------------------------------------------- 00212 // Disk::ComputeLatency() 00213 // Return how long will it take to read/write a disk sector, from 00214 // the current position of the disk head. 00215 // 00216 // Latency = seek time + rotational latency + transfer time 00217 // Disk seeks at one track per SeekTime ticks (cf. stats.h) 00218 // and rotates at one sector per RotationTime ticks 00219 // 00220 // To find the rotational latency, we first must figure out where the 00221 // disk head will be after the seek (if any). We then figure out 00222 // how long it will take to rotate completely past newSector after 00223 // that point. 00224 // 00225 // The disk also has a "track buffer"; the disk continuously reads 00226 // the contents of the current disk track into the buffer. This allows 00227 // read requests to the current track to be satisfied more quickly. 00228 // The contents of the track buffer are discarded after every seek to 00229 // a new track. 00230 //---------------------------------------------------------------------- 00231 00232 int 00233 Disk::ComputeLatency(int newSector, bool writing) 00234 { 00235 int rotation; 00236 int seek = TimeToSeek(newSector, &rotation); 00237 int timeAfter = stats->totalTicks + seek + rotation; 00238 00239 #ifndef NOTRACKBUF // turn this on if you don't want the track buffer stuff 00240 // check if track buffer applies 00241 if ((writing == FALSE) && (seek == 0) 00242 && (((timeAfter - bufferInit) / RotationTime) 00243 > ModuloDiff(newSector, bufferInit / RotationTime))) { 00244 DEBUG('d', "Request latency = %d\n", RotationTime); 00245 return RotationTime; // time to transfer sector from the track buffer 00246 } 00247 #endif 00248 00249 rotation += ModuloDiff(newSector, timeAfter / RotationTime) * RotationTime; 00250 00251 DEBUG('d', "Request latency = %d\n", seek + rotation + RotationTime); 00252 return(seek + rotation + RotationTime); 00253 } 00254 00255 //---------------------------------------------------------------------- 00256 // Disk::UpdateLast 00257 // Keep track of the most recently requested sector. So we can know 00258 // what is in the track buffer. 00259 //---------------------------------------------------------------------- 00260 00261 void 00262 Disk::UpdateLast(int newSector) 00263 { 00264 int rotate; 00265 int seek = TimeToSeek(newSector, &rotate); 00266 00267 if (seek != 0) 00268 bufferInit = stats->totalTicks + seek + rotate; 00269 lastSector = newSector; 00270 DEBUG('d', "Updating last sector = %d, %d\n", lastSector, bufferInit); 00271 }

Generated on Thu Sep 16 12:33:44 2004 for NachOS by doxygen 1.3.8