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

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