8f735419f1d9f65d4a823d8a316ec0a8c9402917
[tpg/acess2.git] / Kernel / drv / iocache.c
1 /*
2  * Acess2 Kernel
3  * - IO Cache
4  * 
5  * By thePowersGang (John Hodge)
6  * 
7  * TODO: Convert to use spare physical pages instead
8  */
9 #define DEBUG   0
10 #include <acess.h>
11 #include <iocache.h>
12
13 // === TYPES ===
14 typedef struct sIOCache_Ent     tIOCache_Ent;
15 typedef struct sIOCache_PageInfo        tIOCache_PageInfo;
16
17 // === STRUCTURES ===
18 struct sIOCache_Ent
19 {
20         tIOCache_Ent    *Next;
21         Uint64  Num;
22         Sint64  LastAccess;
23         Sint64  LastWrite;
24         Uint8   Data[];
25 };
26
27 struct sIOCache_PageInfo
28 {
29         tIOCache_PageInfo       *GlobalNext;
30         tIOCache_PageInfo       *CacheNext;
31         tIOCache        *Owner;
32         tPAddr  BasePhys;
33         Uint64  BaseOffset;
34 };
35
36 struct sIOCache
37 {
38         tIOCache        *Next;
39          int    SectorSize;
40         tMutex  Lock;
41          int    Mode;
42         Uint32  ID;
43         tIOCache_WriteCallback  Write;
44          int    CacheSize;
45          int    CacheUsed;
46         tIOCache_Ent    *Entries;
47 };
48
49 // === GLOBALS ===
50 tShortSpinlock  glIOCache_Caches;
51 tIOCache        *gIOCache_Caches = NULL;
52  int    giIOCache_NumCaches = 0;
53 tIOCache_PageInfo       *gIOCache_GlobalPages;
54
55 // === CODE ===
56 /**
57  * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
58  * \brief Creates a new IO Cache
59  */
60 tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
61 {
62         tIOCache        *ret = calloc( 1, sizeof(tIOCache) );
63         
64         // Sanity Check
65         if(!ret)        return NULL;
66         
67         // Fill Structure
68         ret->SectorSize = SectorSize;
69         ret->Mode = IOCACHE_WRITEBACK;
70         ret->ID = ID;
71         ret->Write = Write;
72         ret->CacheSize = CacheSize;
73         
74         // Append to list
75         SHORTLOCK( &glIOCache_Caches );
76         ret->Next = gIOCache_Caches;
77         gIOCache_Caches = ret;
78         SHORTREL( &glIOCache_Caches );
79         
80         // Return
81         return ret;
82 }
83
84 /**
85  * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
86  * \brief Read from a cached sector
87  */
88 int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
89 {
90         
91         ENTER("pCache XSector pBuffer", Cache, Sector, Buffer);
92         
93         // Sanity Check!
94         if(!Cache || !Buffer) {
95                 LEAVE('i', -1);
96                 return -1;
97         }
98         
99         // Lock
100         Mutex_Acquire( &Cache->Lock );
101         if(Cache->CacheSize == 0) {
102                 Mutex_Release( &Cache->Lock );
103                 LEAVE('i', -1);
104                 return -1;
105         }
106
107         #if IOCACHE_USE_PAGES
108         tIOCache_PageInfo       *page;
109         size_t  offset = (Sector*Cache->SectorSize) % PAGE_SIZE;
110         Uint64  wanted_base = (Sector*Cache->SectorSize) & ~(PAGE_SIZE-1);
111         for( page = Cache->Pages; page; page = page->CacheNext )
112         {
113                 void    *tmp;
114                 if(page->BaseOffset < WantedBase)       continue;
115                 if(page->BaseOffset > WantedBase)       break;
116                 tmp = MM_MapTemp( page->BasePhys );
117                 memcpy( Buffer, tmp + offset, Cache->SectorSize ); 
118                 MM_FreeTemp( tmp );
119         }
120         #else   
121         tIOCache_Ent    *ent;
122         // Search the list
123         for( ent = Cache->Entries; ent; ent = ent->Next )
124         {
125                 // Have we found what we are looking for?
126                 if( ent->Num == Sector ) {
127                         memcpy(Buffer, ent->Data, Cache->SectorSize);
128                         ent->LastAccess = now();
129                         Mutex_Release( &Cache->Lock );
130                         LEAVE('i', 1);
131                         return 1;
132                 }
133                 // It's a sorted list, so as soon as we go past `Sector` we know
134                 // it's not there
135                 if(ent->Num > Sector)   break;
136         }
137         #endif
138         
139         Mutex_Release( &Cache->Lock );
140         LEAVE('i', 0);
141         return 0;
142 }
143
144 /**
145  * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
146  * \brief Cache a sector
147  */
148 int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
149 {
150         tIOCache_Ent    *ent, *prev;
151         tIOCache_Ent    *new;
152         tIOCache_Ent    *oldest = NULL, *oldestPrev;
153         
154         // Sanity Check!
155         if(!Cache || !Buffer)
156                 return -1;
157         
158         // Lock
159         Mutex_Acquire( &Cache->Lock );
160         if(Cache->CacheSize == 0) {
161                 Mutex_Release( &Cache->Lock );
162                 return -1;
163         }
164         
165         // Search the list
166         prev = (tIOCache_Ent*)&Cache->Entries;
167         for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
168         {
169                 // Is it already here?
170                 if( ent->Num == Sector ) {
171                         Mutex_Release( &Cache->Lock );
172                         return 0;
173                 }
174                 
175                 // Check if we have found the oldest entry
176                 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
177                         oldest = ent;
178                         oldestPrev = prev;
179                 }
180                 
181                 // Here we go!
182                 if(ent->Num > Sector)
183                         break;
184         }
185         
186         // Create the new entry
187         new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
188         new->Next = ent;
189         new->Num = Sector;
190         new->LastAccess = now();
191         new->LastWrite = 0;     // Zero is special, it means unmodified
192         memcpy(new->Data, Buffer, Cache->SectorSize);
193         
194         // Have we reached the maximum cached entries?
195         if( Cache->CacheUsed == Cache->CacheSize )
196         {
197                 tIOCache_Ent    *savedPrev = prev;
198                 oldestPrev = (tIOCache_Ent*)&Cache->Entries;
199                 // If so, search for the least recently accessed entry
200                 for( ; ent; prev = ent, ent = ent->Next )
201                 {       
202                         // Check if we have found the oldest entry
203                         if( !oldest || oldest->LastAccess > ent->LastAccess ) {
204                                 oldest = ent;
205                                 oldestPrev = prev;
206                         }
207                 }
208                 if( !oldest ) {
209                         Log_Error("IOCache", "Cache full, but also empty");
210                         return -1;
211                 }
212                 // Remove from list, write back and free
213                 oldestPrev->Next = oldest->Next;
214                 if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
215                         Cache->Write(Cache->ID, oldest->Num, oldest->Data);
216                 free(oldest);
217                 
218                 // Decrement the used count
219                 Cache->CacheUsed --;
220                 
221                 // Restore `prev`
222                 prev = savedPrev;
223         }
224         
225         // Append to list
226         prev->Next = new;
227         Cache->CacheUsed ++;
228         
229         // Release Spinlock
230         Mutex_Release( &Cache->Lock );
231         
232         // Return success
233         return 1;
234 }
235
236 /**
237  * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
238  * \brief Read from a cached sector
239  */
240 int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
241 {
242         tIOCache_Ent    *ent;
243         
244         // Sanity Check!
245         if(!Cache || !Buffer)
246                 return -1;
247         // Lock
248         Mutex_Acquire( &Cache->Lock );
249         if(Cache->CacheSize == 0) {
250                 Mutex_Release( &Cache->Lock );
251                 return -1;
252         }
253         
254         // Search the list
255         for( ent = Cache->Entries; ent; ent = ent->Next )
256         {
257                 // Have we found what we are looking for?
258                 if( ent->Num == Sector ) {
259                         memcpy(ent->Data, Buffer, Cache->SectorSize);
260                         ent->LastAccess = ent->LastWrite = now();
261                         
262                         if(Cache->Mode == IOCACHE_WRITEBACK) {
263                                 Cache->Write(Cache->ID, Sector, Buffer);
264                                 ent->LastWrite = 0;
265                         }
266                         
267                         Mutex_Release( &Cache->Lock );
268                         return 1;
269                 }
270                 // It's a sorted list, so as soon as we go past `Sector` we know
271                 // it's not there
272                 if(ent->Num > Sector)   break;
273         }
274         
275         Mutex_Release( &Cache->Lock );
276         return 0;
277 }
278
279 /**
280  * \fn void IOCache_Flush( tIOCache *Cache )
281  * \brief Flush a cache
282  */
283 void IOCache_Flush( tIOCache *Cache )
284 {
285         tIOCache_Ent    *ent;
286         
287         if( Cache->Mode == IOCACHE_VIRTUAL )    return;
288         
289         // Lock
290         Mutex_Acquire( &Cache->Lock );
291         if(Cache->CacheSize == 0) {
292                 Mutex_Release( &Cache->Lock );
293                 return;
294         }
295         
296         // Write All
297         for( ent = Cache->Entries; ent; ent = ent->Next )
298         {
299                 Cache->Write(Cache->ID, ent->Num, ent->Data);
300                 ent->LastWrite = 0;
301         }
302         
303         Mutex_Release( &Cache->Lock );
304 }
305
306 /**
307  * \fn void IOCache_Destroy( tIOCache *Cache )
308  * \brief Destroy a cache
309  */
310 void IOCache_Destroy( tIOCache *Cache )
311 {
312         tIOCache_Ent    *ent, *prev = NULL;
313         
314         // Lock
315         Mutex_Acquire( &Cache->Lock );
316         if(Cache->CacheSize == 0) {
317                 Mutex_Release( &Cache->Lock );
318                 return;
319         }
320         
321         // Free All
322         for(ent = Cache->Entries;
323                 ent;
324                 prev = ent, ent = ent->Next, free(prev) )
325         {
326                 if( Cache->Mode != IOCACHE_VIRTUAL )
327                 {
328                         Cache->Write(Cache->ID, ent->Num, ent->Data);
329                         ent->LastWrite = 0;
330                 }
331         }
332         
333         Cache->CacheSize = 0;
334         
335         Mutex_Release( &Cache->Lock );
336         
337         // Remove from list
338         SHORTLOCK( &glIOCache_Caches );
339         {
340                 tIOCache        *cache;
341                 tIOCache        *prev_cache = (tIOCache*)&gIOCache_Caches;
342                 for(cache = gIOCache_Caches;
343                         cache;
344                         prev_cache = cache, cache = cache->Next )
345                 {
346                         if(cache == Cache) {
347                                 prev_cache->Next = cache->Next;
348                                 break;
349                         }
350                 }
351         }
352         SHORTREL( &glIOCache_Caches );
353         
354         free(Cache);
355 }

UCC git Repository :: git.ucc.asn.au