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

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