Kernel - Planning for IOCache to use pages for cache instead of heap
[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         tIOCache_Ent    *ent;
91         
92         ENTER("pCache XSector pBuffer", Cache, Sector, Buffer);
93         
94         // Sanity Check!
95         if(!Cache || !Buffer) {
96                 LEAVE('i', -1);
97                 return -1;
98         }
99         
100         // Lock
101         Mutex_Acquire( &Cache->Lock );
102         if(Cache->CacheSize == 0) {
103                 Mutex_Release( &Cache->Lock );
104                 LEAVE('i', -1);
105                 return -1;
106         }
107         
108         // Search the list
109         for( ent = Cache->Entries; ent; ent = ent->Next )
110         {
111                 // Have we found what we are looking for?
112                 if( ent->Num == Sector ) {
113                         memcpy(Buffer, ent->Data, Cache->SectorSize);
114                         ent->LastAccess = now();
115                         Mutex_Release( &Cache->Lock );
116                         LEAVE('i', 1);
117                         return 1;
118                 }
119                 // It's a sorted list, so as soon as we go past `Sector` we know
120                 // it's not there
121                 if(ent->Num > Sector)   break;
122         }
123         
124         Mutex_Release( &Cache->Lock );
125         LEAVE('i', 0);
126         return 0;
127 }
128
129 /**
130  * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
131  * \brief Cache a sector
132  */
133 int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
134 {
135         tIOCache_Ent    *ent, *prev;
136         tIOCache_Ent    *new;
137         tIOCache_Ent    *oldest = NULL, *oldestPrev;
138         
139         // Sanity Check!
140         if(!Cache || !Buffer)
141                 return -1;
142         
143         // Lock
144         Mutex_Acquire( &Cache->Lock );
145         if(Cache->CacheSize == 0) {
146                 Mutex_Release( &Cache->Lock );
147                 return -1;
148         }
149         
150         // Search the list
151         prev = (tIOCache_Ent*)&Cache->Entries;
152         for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
153         {
154                 // Is it already here?
155                 if( ent->Num == Sector ) {
156                         Mutex_Release( &Cache->Lock );
157                         return 0;
158                 }
159                 
160                 // Check if we have found the oldest entry
161                 if( !oldest || oldest->LastAccess > ent->LastAccess ) {
162                         oldest = ent;
163                         oldestPrev = prev;
164                 }
165                 
166                 // Here we go!
167                 if(ent->Num > Sector)
168                         break;
169         }
170         
171         // Create the new entry
172         new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
173         new->Next = ent;
174         new->Num = Sector;
175         new->LastAccess = now();
176         new->LastWrite = 0;     // Zero is special, it means unmodified
177         memcpy(new->Data, Buffer, Cache->SectorSize);
178         
179         // Have we reached the maximum cached entries?
180         if( Cache->CacheUsed == Cache->CacheSize )
181         {
182                 tIOCache_Ent    *savedPrev = prev;
183                 oldestPrev = (tIOCache_Ent*)&Cache->Entries;
184                 // If so, search for the least recently accessed entry
185                 for( ; ent; prev = ent, ent = ent->Next )
186                 {       
187                         // Check if we have found the oldest entry
188                         if( !oldest || oldest->LastAccess > ent->LastAccess ) {
189                                 oldest = ent;
190                                 oldestPrev = prev;
191                         }
192                 }
193                 // Remove from list, write back and free
194                 oldestPrev->Next = oldest->Next;
195                 if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
196                         Cache->Write(Cache->ID, oldest->Num, oldest->Data);
197                 free(oldest);
198                 
199                 // Decrement the used count
200                 Cache->CacheUsed --;
201                 
202                 // Restore `prev`
203                 prev = savedPrev;
204         }
205         
206         // Append to list
207         prev->Next = new;
208         Cache->CacheUsed ++;
209         
210         // Release Spinlock
211         Mutex_Release( &Cache->Lock );
212         
213         // Return success
214         return 1;
215 }
216
217 /**
218  * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
219  * \brief Read from a cached sector
220  */
221 int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
222 {
223         tIOCache_Ent    *ent;
224         
225         // Sanity Check!
226         if(!Cache || !Buffer)
227                 return -1;
228         // Lock
229         Mutex_Acquire( &Cache->Lock );
230         if(Cache->CacheSize == 0) {
231                 Mutex_Release( &Cache->Lock );
232                 return -1;
233         }
234         
235         // Search the list
236         for( ent = Cache->Entries; ent; ent = ent->Next )
237         {
238                 // Have we found what we are looking for?
239                 if( ent->Num == Sector ) {
240                         memcpy(ent->Data, Buffer, Cache->SectorSize);
241                         ent->LastAccess = ent->LastWrite = now();
242                         
243                         if(Cache->Mode == IOCACHE_WRITEBACK) {
244                                 Cache->Write(Cache->ID, Sector, Buffer);
245                                 ent->LastWrite = 0;
246                         }
247                         
248                         Mutex_Release( &Cache->Lock );
249                         return 1;
250                 }
251                 // It's a sorted list, so as soon as we go past `Sector` we know
252                 // it's not there
253                 if(ent->Num > Sector)   break;
254         }
255         
256         Mutex_Release( &Cache->Lock );
257         return 0;
258 }
259
260 /**
261  * \fn void IOCache_Flush( tIOCache *Cache )
262  * \brief Flush a cache
263  */
264 void IOCache_Flush( tIOCache *Cache )
265 {
266         tIOCache_Ent    *ent;
267         
268         if( Cache->Mode == IOCACHE_VIRTUAL )    return;
269         
270         // Lock
271         Mutex_Acquire( &Cache->Lock );
272         if(Cache->CacheSize == 0) {
273                 Mutex_Release( &Cache->Lock );
274                 return;
275         }
276         
277         // Write All
278         for( ent = Cache->Entries; ent; ent = ent->Next )
279         {
280                 Cache->Write(Cache->ID, ent->Num, ent->Data);
281                 ent->LastWrite = 0;
282         }
283         
284         Mutex_Release( &Cache->Lock );
285 }
286
287 /**
288  * \fn void IOCache_Destroy( tIOCache *Cache )
289  * \brief Destroy a cache
290  */
291 void IOCache_Destroy( tIOCache *Cache )
292 {
293         tIOCache_Ent    *ent, *prev = NULL;
294         
295         // Lock
296         Mutex_Acquire( &Cache->Lock );
297         if(Cache->CacheSize == 0) {
298                 Mutex_Release( &Cache->Lock );
299                 return;
300         }
301         
302         // Free All
303         for(ent = Cache->Entries;
304                 ent;
305                 prev = ent, ent = ent->Next, free(prev) )
306         {
307                 if( Cache->Mode != IOCACHE_VIRTUAL )
308                 {
309                         Cache->Write(Cache->ID, ent->Num, ent->Data);
310                         ent->LastWrite = 0;
311                 }
312         }
313         
314         Cache->CacheSize = 0;
315         
316         Mutex_Release( &Cache->Lock );
317         
318         // Remove from list
319         SHORTLOCK( &glIOCache_Caches );
320         {
321                 tIOCache        *cache;
322                 tIOCache        *prev_cache = (tIOCache*)&gIOCache_Caches;
323                 for(cache = gIOCache_Caches;
324                         cache;
325                         prev_cache = cache, cache = cache->Next )
326                 {
327                         if(cache == Cache) {
328                                 prev_cache->Next = cache->Next;
329                                 break;
330                         }
331                 }
332         }
333         SHORTREL( &glIOCache_Caches );
334         
335         free(Cache);
336 }

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