Kernel - Fixes from clang's scan-build tool
[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                 if( !oldest ) {
194                         Log_Error("IOCache", "Cache full, but also empty");
195                         return -1;
196                 }
197                 // Remove from list, write back and free
198                 oldestPrev->Next = oldest->Next;
199                 if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
200                         Cache->Write(Cache->ID, oldest->Num, oldest->Data);
201                 free(oldest);
202                 
203                 // Decrement the used count
204                 Cache->CacheUsed --;
205                 
206                 // Restore `prev`
207                 prev = savedPrev;
208         }
209         
210         // Append to list
211         prev->Next = new;
212         Cache->CacheUsed ++;
213         
214         // Release Spinlock
215         Mutex_Release( &Cache->Lock );
216         
217         // Return success
218         return 1;
219 }
220
221 /**
222  * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
223  * \brief Read from a cached sector
224  */
225 int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
226 {
227         tIOCache_Ent    *ent;
228         
229         // Sanity Check!
230         if(!Cache || !Buffer)
231                 return -1;
232         // Lock
233         Mutex_Acquire( &Cache->Lock );
234         if(Cache->CacheSize == 0) {
235                 Mutex_Release( &Cache->Lock );
236                 return -1;
237         }
238         
239         // Search the list
240         for( ent = Cache->Entries; ent; ent = ent->Next )
241         {
242                 // Have we found what we are looking for?
243                 if( ent->Num == Sector ) {
244                         memcpy(ent->Data, Buffer, Cache->SectorSize);
245                         ent->LastAccess = ent->LastWrite = now();
246                         
247                         if(Cache->Mode == IOCACHE_WRITEBACK) {
248                                 Cache->Write(Cache->ID, Sector, Buffer);
249                                 ent->LastWrite = 0;
250                         }
251                         
252                         Mutex_Release( &Cache->Lock );
253                         return 1;
254                 }
255                 // It's a sorted list, so as soon as we go past `Sector` we know
256                 // it's not there
257                 if(ent->Num > Sector)   break;
258         }
259         
260         Mutex_Release( &Cache->Lock );
261         return 0;
262 }
263
264 /**
265  * \fn void IOCache_Flush( tIOCache *Cache )
266  * \brief Flush a cache
267  */
268 void IOCache_Flush( tIOCache *Cache )
269 {
270         tIOCache_Ent    *ent;
271         
272         if( Cache->Mode == IOCACHE_VIRTUAL )    return;
273         
274         // Lock
275         Mutex_Acquire( &Cache->Lock );
276         if(Cache->CacheSize == 0) {
277                 Mutex_Release( &Cache->Lock );
278                 return;
279         }
280         
281         // Write All
282         for( ent = Cache->Entries; ent; ent = ent->Next )
283         {
284                 Cache->Write(Cache->ID, ent->Num, ent->Data);
285                 ent->LastWrite = 0;
286         }
287         
288         Mutex_Release( &Cache->Lock );
289 }
290
291 /**
292  * \fn void IOCache_Destroy( tIOCache *Cache )
293  * \brief Destroy a cache
294  */
295 void IOCache_Destroy( tIOCache *Cache )
296 {
297         tIOCache_Ent    *ent, *prev = NULL;
298         
299         // Lock
300         Mutex_Acquire( &Cache->Lock );
301         if(Cache->CacheSize == 0) {
302                 Mutex_Release( &Cache->Lock );
303                 return;
304         }
305         
306         // Free All
307         for(ent = Cache->Entries;
308                 ent;
309                 prev = ent, ent = ent->Next, free(prev) )
310         {
311                 if( Cache->Mode != IOCACHE_VIRTUAL )
312                 {
313                         Cache->Write(Cache->ID, ent->Num, ent->Data);
314                         ent->LastWrite = 0;
315                 }
316         }
317         
318         Cache->CacheSize = 0;
319         
320         Mutex_Release( &Cache->Lock );
321         
322         // Remove from list
323         SHORTLOCK( &glIOCache_Caches );
324         {
325                 tIOCache        *cache;
326                 tIOCache        *prev_cache = (tIOCache*)&gIOCache_Caches;
327                 for(cache = gIOCache_Caches;
328                         cache;
329                         prev_cache = cache, cache = cache->Next )
330                 {
331                         if(cache == Cache) {
332                                 prev_cache->Next = cache->Next;
333                                 break;
334                         }
335                 }
336         }
337         SHORTREL( &glIOCache_Caches );
338         
339         free(Cache);
340 }

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