5 * By thePowersGang (John Hodge)
11 typedef struct sIOCache_Ent tIOCache_Ent;
30 tIOCache_WriteCallback Write;
33 tIOCache_Ent *Entries;
38 tIOCache *gIOCache_Caches = NULL;
39 int giIOCache_NumCaches = 0;
43 * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
44 * \brief Creates a new IO Cache
46 tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
48 tIOCache *ret = malloc( sizeof(tIOCache) );
54 ret->SectorSize = SectorSize;
55 ret->Mode = IOCACHE_WRITEBACK;
58 ret->CacheSize = CacheSize;
63 LOCK( &glIOCache_Caches );
64 ret->Next = gIOCache_Caches;
65 gIOCache_Caches = ret;
66 RELEASE( &glIOCache_Caches );
73 * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
74 * \brief Read from a cached sector
76 int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
86 if(Cache->CacheSize == 0) {
87 RELEASE( &Cache->Lock );
92 for( ent = Cache->Entries; ent; ent = ent->Next )
94 // Have we found what we are looking for?
95 if( ent->Num == Sector ) {
96 memcpy(Buffer, ent->Data, Cache->SectorSize);
97 ent->LastAccess = now();
98 RELEASE( &Cache->Lock );
101 // It's a sorted list, so as soon as we go past `Sector` we know
103 if(ent->Num > Sector) break;
106 RELEASE( &Cache->Lock );
111 * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
112 * \brief Cache a sector
114 int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
116 tIOCache_Ent *ent, *prev;
118 tIOCache_Ent *oldest = NULL, *oldestPrev;
121 if(!Cache || !Buffer)
125 LOCK( &Cache->Lock );
126 if(Cache->CacheSize == 0) {
127 RELEASE( &Cache->Lock );
132 prev = (tIOCache_Ent*)&Cache->Entries;
133 for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
135 // Is it already here?
136 if( ent->Num == Sector ) {
137 RELEASE( &Cache->Lock );
141 // Check if we have found the oldest entry
142 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
148 if(ent->Num > Sector)
152 // Create the new entry
153 new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
156 new->LastAccess = now();
157 new->LastWrite = 0; // Zero is special, it means unmodified
158 memcpy(new->Data, Buffer, Cache->SectorSize);
160 // Have we reached the maximum cached entries?
161 if( Cache->CacheUsed == Cache->CacheSize )
163 tIOCache_Ent *savedPrev = prev;
164 oldestPrev = (tIOCache_Ent*)&Cache->Entries;
165 // If so, search for the least recently accessed entry
166 for( ; ent; prev = ent, ent = ent->Next )
168 // Check if we have found the oldest entry
169 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
174 // Remove from list, write back and free
175 oldestPrev->Next = oldest->Next;
176 if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
177 Cache->Write(Cache->ID, oldest->Num, oldest->Data);
180 // Decrement the used count
192 RELEASE( &Cache->Lock );
199 * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
200 * \brief Read from a cached sector
202 int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
207 if(!Cache || !Buffer)
210 LOCK( &Cache->Lock );
211 if(Cache->CacheSize == 0) {
212 RELEASE( &Cache->Lock );
217 for( ent = Cache->Entries; ent; ent = ent->Next )
219 // Have we found what we are looking for?
220 if( ent->Num == Sector ) {
221 memcpy(ent->Data, Buffer, Cache->SectorSize);
222 ent->LastAccess = ent->LastWrite = now();
224 if(Cache->Mode == IOCACHE_WRITEBACK) {
225 Cache->Write(Cache->ID, Sector, Buffer);
229 RELEASE( &Cache->Lock );
232 // It's a sorted list, so as soon as we go past `Sector` we know
234 if(ent->Num > Sector) break;
237 RELEASE( &Cache->Lock );
242 * \fn void IOCache_Flush( tIOCache *Cache )
243 * \brief Flush a cache
245 void IOCache_Flush( tIOCache *Cache )
249 if( Cache->Mode == IOCACHE_VIRTUAL ) return;
252 LOCK( &Cache->Lock );
253 if(Cache->CacheSize == 0) {
254 RELEASE( &Cache->Lock );
259 for( ent = Cache->Entries; ent; ent = ent->Next )
261 Cache->Write(Cache->ID, ent->Num, ent->Data);
265 RELEASE( &Cache->Lock );
269 * \fn void IOCache_Destroy( tIOCache *Cache )
270 * \brief Destroy a cache
272 void IOCache_Destroy( tIOCache *Cache )
274 tIOCache_Ent *ent, *prev = NULL;
277 LOCK( &Cache->Lock );
278 if(Cache->CacheSize == 0) {
279 RELEASE( &Cache->Lock );
284 for(ent = Cache->Entries;
286 prev = ent, ent = ent->Next, free(prev) )
288 if( Cache->Mode != IOCACHE_VIRTUAL )
290 Cache->Write(Cache->ID, ent->Num, ent->Data);
295 Cache->CacheSize = 0;
297 RELEASE( &Cache->Lock );
300 LOCK( &glIOCache_Caches );
303 tIOCache *prev = (tIOCache*)&gIOCache_Caches;
304 for(ent = gIOCache_Caches;
306 prev = ent, ent = ent->Next )
309 prev->Next = ent->Next;
314 RELEASE( &glIOCache_Caches );