5 * By thePowersGang (John Hodge)
12 typedef struct sIOCache_Ent tIOCache_Ent;
31 tIOCache_WriteCallback Write;
34 tIOCache_Ent *Entries;
38 tShortSpinlock glIOCache_Caches;
39 tIOCache *gIOCache_Caches = NULL;
40 int giIOCache_NumCaches = 0;
44 * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
45 * \brief Creates a new IO Cache
47 tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
49 tIOCache *ret = malloc( sizeof(tIOCache) );
55 ret->SectorSize = SectorSize;
56 ret->Mode = IOCACHE_WRITEBACK;
59 ret->CacheSize = CacheSize;
64 SHORTLOCK( &glIOCache_Caches );
65 ret->Next = gIOCache_Caches;
66 gIOCache_Caches = ret;
67 SHORTREL( &glIOCache_Caches );
74 * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
75 * \brief Read from a cached sector
77 int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
81 ENTER("pCache XSector pBuffer", Cache, Sector, Buffer);
84 if(!Cache || !Buffer) {
90 Mutex_Acquire( &Cache->Lock );
91 if(Cache->CacheSize == 0) {
92 Mutex_Release( &Cache->Lock );
98 for( ent = Cache->Entries; ent; ent = ent->Next )
100 // Have we found what we are looking for?
101 if( ent->Num == Sector ) {
102 memcpy(Buffer, ent->Data, Cache->SectorSize);
103 ent->LastAccess = now();
104 Mutex_Release( &Cache->Lock );
108 // It's a sorted list, so as soon as we go past `Sector` we know
110 if(ent->Num > Sector) break;
113 Mutex_Release( &Cache->Lock );
119 * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
120 * \brief Cache a sector
122 int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
124 tIOCache_Ent *ent, *prev;
126 tIOCache_Ent *oldest = NULL, *oldestPrev;
129 if(!Cache || !Buffer)
133 Mutex_Acquire( &Cache->Lock );
134 if(Cache->CacheSize == 0) {
135 Mutex_Release( &Cache->Lock );
140 prev = (tIOCache_Ent*)&Cache->Entries;
141 for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
143 // Is it already here?
144 if( ent->Num == Sector ) {
145 Mutex_Release( &Cache->Lock );
149 // Check if we have found the oldest entry
150 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
156 if(ent->Num > Sector)
160 // Create the new entry
161 new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
164 new->LastAccess = now();
165 new->LastWrite = 0; // Zero is special, it means unmodified
166 memcpy(new->Data, Buffer, Cache->SectorSize);
168 // Have we reached the maximum cached entries?
169 if( Cache->CacheUsed == Cache->CacheSize )
171 tIOCache_Ent *savedPrev = prev;
172 oldestPrev = (tIOCache_Ent*)&Cache->Entries;
173 // If so, search for the least recently accessed entry
174 for( ; ent; prev = ent, ent = ent->Next )
176 // Check if we have found the oldest entry
177 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
182 // Remove from list, write back and free
183 oldestPrev->Next = oldest->Next;
184 if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
185 Cache->Write(Cache->ID, oldest->Num, oldest->Data);
188 // Decrement the used count
200 Mutex_Release( &Cache->Lock );
207 * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
208 * \brief Read from a cached sector
210 int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
215 if(!Cache || !Buffer)
218 Mutex_Acquire( &Cache->Lock );
219 if(Cache->CacheSize == 0) {
220 Mutex_Release( &Cache->Lock );
225 for( ent = Cache->Entries; ent; ent = ent->Next )
227 // Have we found what we are looking for?
228 if( ent->Num == Sector ) {
229 memcpy(ent->Data, Buffer, Cache->SectorSize);
230 ent->LastAccess = ent->LastWrite = now();
232 if(Cache->Mode == IOCACHE_WRITEBACK) {
233 Cache->Write(Cache->ID, Sector, Buffer);
237 Mutex_Release( &Cache->Lock );
240 // It's a sorted list, so as soon as we go past `Sector` we know
242 if(ent->Num > Sector) break;
245 Mutex_Release( &Cache->Lock );
250 * \fn void IOCache_Flush( tIOCache *Cache )
251 * \brief Flush a cache
253 void IOCache_Flush( tIOCache *Cache )
257 if( Cache->Mode == IOCACHE_VIRTUAL ) return;
260 Mutex_Acquire( &Cache->Lock );
261 if(Cache->CacheSize == 0) {
262 Mutex_Release( &Cache->Lock );
267 for( ent = Cache->Entries; ent; ent = ent->Next )
269 Cache->Write(Cache->ID, ent->Num, ent->Data);
273 Mutex_Release( &Cache->Lock );
277 * \fn void IOCache_Destroy( tIOCache *Cache )
278 * \brief Destroy a cache
280 void IOCache_Destroy( tIOCache *Cache )
282 tIOCache_Ent *ent, *prev = NULL;
285 Mutex_Acquire( &Cache->Lock );
286 if(Cache->CacheSize == 0) {
287 Mutex_Release( &Cache->Lock );
292 for(ent = Cache->Entries;
294 prev = ent, ent = ent->Next, free(prev) )
296 if( Cache->Mode != IOCACHE_VIRTUAL )
298 Cache->Write(Cache->ID, ent->Num, ent->Data);
303 Cache->CacheSize = 0;
305 Mutex_Release( &Cache->Lock );
308 SHORTLOCK( &glIOCache_Caches );
311 tIOCache *prev = (tIOCache*)&gIOCache_Caches;
312 for(ent = gIOCache_Caches;
314 prev = ent, ent = ent->Next )
317 prev->Next = ent->Next;
322 SHORTREL( &glIOCache_Caches );