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 = calloc( 1, sizeof(tIOCache) );
55 ret->SectorSize = SectorSize;
56 ret->Mode = IOCACHE_WRITEBACK;
59 ret->CacheSize = CacheSize;
62 SHORTLOCK( &glIOCache_Caches );
63 ret->Next = gIOCache_Caches;
64 gIOCache_Caches = ret;
65 SHORTREL( &glIOCache_Caches );
72 * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
73 * \brief Read from a cached sector
75 int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
79 ENTER("pCache XSector pBuffer", Cache, Sector, Buffer);
82 if(!Cache || !Buffer) {
88 Mutex_Acquire( &Cache->Lock );
89 if(Cache->CacheSize == 0) {
90 Mutex_Release( &Cache->Lock );
96 for( ent = Cache->Entries; ent; ent = ent->Next )
98 // Have we found what we are looking for?
99 if( ent->Num == Sector ) {
100 memcpy(Buffer, ent->Data, Cache->SectorSize);
101 ent->LastAccess = now();
102 Mutex_Release( &Cache->Lock );
106 // It's a sorted list, so as soon as we go past `Sector` we know
108 if(ent->Num > Sector) break;
111 Mutex_Release( &Cache->Lock );
117 * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
118 * \brief Cache a sector
120 int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
122 tIOCache_Ent *ent, *prev;
124 tIOCache_Ent *oldest = NULL, *oldestPrev;
127 if(!Cache || !Buffer)
131 Mutex_Acquire( &Cache->Lock );
132 if(Cache->CacheSize == 0) {
133 Mutex_Release( &Cache->Lock );
138 prev = (tIOCache_Ent*)&Cache->Entries;
139 for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
141 // Is it already here?
142 if( ent->Num == Sector ) {
143 Mutex_Release( &Cache->Lock );
147 // Check if we have found the oldest entry
148 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
154 if(ent->Num > Sector)
158 // Create the new entry
159 new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
162 new->LastAccess = now();
163 new->LastWrite = 0; // Zero is special, it means unmodified
164 memcpy(new->Data, Buffer, Cache->SectorSize);
166 // Have we reached the maximum cached entries?
167 if( Cache->CacheUsed == Cache->CacheSize )
169 tIOCache_Ent *savedPrev = prev;
170 oldestPrev = (tIOCache_Ent*)&Cache->Entries;
171 // If so, search for the least recently accessed entry
172 for( ; ent; prev = ent, ent = ent->Next )
174 // Check if we have found the oldest entry
175 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
180 // Remove from list, write back and free
181 oldestPrev->Next = oldest->Next;
182 if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
183 Cache->Write(Cache->ID, oldest->Num, oldest->Data);
186 // Decrement the used count
198 Mutex_Release( &Cache->Lock );
205 * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
206 * \brief Read from a cached sector
208 int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
213 if(!Cache || !Buffer)
216 Mutex_Acquire( &Cache->Lock );
217 if(Cache->CacheSize == 0) {
218 Mutex_Release( &Cache->Lock );
223 for( ent = Cache->Entries; ent; ent = ent->Next )
225 // Have we found what we are looking for?
226 if( ent->Num == Sector ) {
227 memcpy(ent->Data, Buffer, Cache->SectorSize);
228 ent->LastAccess = ent->LastWrite = now();
230 if(Cache->Mode == IOCACHE_WRITEBACK) {
231 Cache->Write(Cache->ID, Sector, Buffer);
235 Mutex_Release( &Cache->Lock );
238 // It's a sorted list, so as soon as we go past `Sector` we know
240 if(ent->Num > Sector) break;
243 Mutex_Release( &Cache->Lock );
248 * \fn void IOCache_Flush( tIOCache *Cache )
249 * \brief Flush a cache
251 void IOCache_Flush( tIOCache *Cache )
255 if( Cache->Mode == IOCACHE_VIRTUAL ) return;
258 Mutex_Acquire( &Cache->Lock );
259 if(Cache->CacheSize == 0) {
260 Mutex_Release( &Cache->Lock );
265 for( ent = Cache->Entries; ent; ent = ent->Next )
267 Cache->Write(Cache->ID, ent->Num, ent->Data);
271 Mutex_Release( &Cache->Lock );
275 * \fn void IOCache_Destroy( tIOCache *Cache )
276 * \brief Destroy a cache
278 void IOCache_Destroy( tIOCache *Cache )
280 tIOCache_Ent *ent, *prev = NULL;
283 Mutex_Acquire( &Cache->Lock );
284 if(Cache->CacheSize == 0) {
285 Mutex_Release( &Cache->Lock );
290 for(ent = Cache->Entries;
292 prev = ent, ent = ent->Next, free(prev) )
294 if( Cache->Mode != IOCACHE_VIRTUAL )
296 Cache->Write(Cache->ID, ent->Num, ent->Data);
301 Cache->CacheSize = 0;
303 Mutex_Release( &Cache->Lock );
306 SHORTLOCK( &glIOCache_Caches );
309 tIOCache *prev_cache = (tIOCache*)&gIOCache_Caches;
310 for(cache = gIOCache_Caches;
312 prev_cache = cache, cache = cache->Next )
315 prev_cache->Next = cache->Next;
320 SHORTREL( &glIOCache_Caches );