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

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