1433d6423SLionel Sambuc
2433d6423SLionel Sambuc /* File that implements the data structure, insert, lookup and remove
3433d6423SLionel Sambuc * functions for file system cache blocks.
4433d6423SLionel Sambuc *
5433d6423SLionel Sambuc * Cache blocks can be mapped into the memory of processes by the
6433d6423SLionel Sambuc * 'cache' and 'file' memory types.
7433d6423SLionel Sambuc */
8433d6423SLionel Sambuc
9433d6423SLionel Sambuc #include <assert.h>
10433d6423SLionel Sambuc #include <string.h>
11433d6423SLionel Sambuc
12433d6423SLionel Sambuc #include <minix/hash.h>
13433d6423SLionel Sambuc
14433d6423SLionel Sambuc #include "proto.h"
15433d6423SLionel Sambuc #include "vm.h"
16433d6423SLionel Sambuc #include "region.h"
17433d6423SLionel Sambuc #include "glo.h"
18433d6423SLionel Sambuc #include "cache.h"
19433d6423SLionel Sambuc
20433d6423SLionel Sambuc /* cache datastructure */
21433d6423SLionel Sambuc #define HASHSIZE 65536
22433d6423SLionel Sambuc
23433d6423SLionel Sambuc static struct cached_page *cache_hash_bydev[HASHSIZE];
24433d6423SLionel Sambuc static struct cached_page *cache_hash_byino[HASHSIZE];
25433d6423SLionel Sambuc static struct cached_page *lru_oldest = NULL, *lru_newest = NULL;
26433d6423SLionel Sambuc
27433d6423SLionel Sambuc static u32_t cached_pages = 0;
28433d6423SLionel Sambuc
lru_rm(struct cached_page * hb)29433d6423SLionel Sambuc static void lru_rm(struct cached_page *hb)
30433d6423SLionel Sambuc {
31433d6423SLionel Sambuc struct cached_page *newer = hb->newer, *older = hb->older;
32433d6423SLionel Sambuc assert(lru_newest);
33433d6423SLionel Sambuc assert(lru_oldest);
34433d6423SLionel Sambuc if(newer) {
35433d6423SLionel Sambuc assert(newer->older == hb);
36433d6423SLionel Sambuc newer->older = older;
37433d6423SLionel Sambuc }
38433d6423SLionel Sambuc if(older) {
39433d6423SLionel Sambuc assert(older->newer == hb);
40433d6423SLionel Sambuc older->newer = newer;
41433d6423SLionel Sambuc }
42433d6423SLionel Sambuc
43433d6423SLionel Sambuc if(lru_newest == hb) { assert(!newer); lru_newest = older; }
44433d6423SLionel Sambuc if(lru_oldest == hb) { assert(!older); lru_oldest = newer; }
45433d6423SLionel Sambuc
46433d6423SLionel Sambuc if(lru_newest) assert(lru_newest->newer == NULL);
47433d6423SLionel Sambuc if(lru_oldest) assert(lru_oldest->older == NULL);
48433d6423SLionel Sambuc
49433d6423SLionel Sambuc cached_pages--;
50433d6423SLionel Sambuc }
51433d6423SLionel Sambuc
lru_add(struct cached_page * hb)52433d6423SLionel Sambuc static void lru_add(struct cached_page *hb)
53433d6423SLionel Sambuc {
54433d6423SLionel Sambuc if(lru_newest) {
55433d6423SLionel Sambuc assert(lru_oldest);
56433d6423SLionel Sambuc assert(!lru_newest->newer);
57433d6423SLionel Sambuc lru_newest->newer = hb;
58433d6423SLionel Sambuc } else {
59433d6423SLionel Sambuc assert(!lru_oldest);
60433d6423SLionel Sambuc lru_oldest = hb;
61433d6423SLionel Sambuc }
62433d6423SLionel Sambuc
63433d6423SLionel Sambuc hb->older = lru_newest;
64433d6423SLionel Sambuc hb->newer = NULL;
65433d6423SLionel Sambuc lru_newest = hb;
66433d6423SLionel Sambuc
67433d6423SLionel Sambuc cached_pages++;
68433d6423SLionel Sambuc }
69433d6423SLionel Sambuc
cache_lru_touch(struct cached_page * hb)70433d6423SLionel Sambuc void cache_lru_touch(struct cached_page *hb)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc lru_rm(hb);
73433d6423SLionel Sambuc lru_add(hb);
74433d6423SLionel Sambuc }
75433d6423SLionel Sambuc
makehash(u32_t p1,u64_t p2)76433d6423SLionel Sambuc static __inline u32_t makehash(u32_t p1, u64_t p2)
77433d6423SLionel Sambuc {
78433d6423SLionel Sambuc u32_t offlo = ex64lo(p2), offhi = ex64hi(p2),
79433d6423SLionel Sambuc v = 0x12345678;
80433d6423SLionel Sambuc hash_mix(p1, offlo, offhi);
81433d6423SLionel Sambuc hash_final(offlo, offhi, v);
82433d6423SLionel Sambuc
83433d6423SLionel Sambuc return v % HASHSIZE;
84433d6423SLionel Sambuc }
85433d6423SLionel Sambuc
86433d6423SLionel Sambuc #if CACHE_SANITY
cache_sanitycheck_internal(void)87433d6423SLionel Sambuc void cache_sanitycheck_internal(void)
88433d6423SLionel Sambuc {
89433d6423SLionel Sambuc int h;
90433d6423SLionel Sambuc int n = 0;
91433d6423SLionel Sambuc int byino = 0;
92433d6423SLionel Sambuc int withino = 0;
93433d6423SLionel Sambuc int bydev_total = 0, lru_total = 0;
94433d6423SLionel Sambuc struct cached_page *cp;
95433d6423SLionel Sambuc
96433d6423SLionel Sambuc for(h = 0; h < HASHSIZE; h++) {
97433d6423SLionel Sambuc for(cp = cache_hash_bydev[h]; cp; cp = cp->hash_next_dev) {
98433d6423SLionel Sambuc assert(cp->dev != NO_DEV);
99433d6423SLionel Sambuc assert(h == makehash(cp->dev, cp->dev_offset));
100433d6423SLionel Sambuc assert(cp == find_cached_page_bydev(cp->dev, cp->dev_offset, cp->ino, cp->ino_offset));
101433d6423SLionel Sambuc if(cp->ino != VMC_NO_INODE) withino++;
102433d6423SLionel Sambuc bydev_total++;
103433d6423SLionel Sambuc n++;
104433d6423SLionel Sambuc assert(n < 1500000);
105433d6423SLionel Sambuc }
106433d6423SLionel Sambuc for(cp = cache_hash_byino[h]; cp; cp = cp->hash_next_ino) {
107433d6423SLionel Sambuc assert(cp->dev != NO_DEV);
108433d6423SLionel Sambuc assert(cp->ino != VMC_NO_INODE);
109433d6423SLionel Sambuc assert(h == makehash(cp->ino, cp->ino_offset));
110433d6423SLionel Sambuc byino++;
111433d6423SLionel Sambuc n++;
112433d6423SLionel Sambuc assert(n < 1500000);
113433d6423SLionel Sambuc }
114433d6423SLionel Sambuc }
115433d6423SLionel Sambuc
116433d6423SLionel Sambuc assert(byino == withino);
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc if(lru_newest) {
119433d6423SLionel Sambuc assert(lru_oldest);
120433d6423SLionel Sambuc assert(!lru_newest->newer);
121433d6423SLionel Sambuc assert(!lru_oldest->older);
122433d6423SLionel Sambuc } else {
123433d6423SLionel Sambuc assert(!lru_oldest);
124433d6423SLionel Sambuc }
125433d6423SLionel Sambuc
126433d6423SLionel Sambuc for(cp = lru_oldest; cp; cp = cp->newer) {
127433d6423SLionel Sambuc struct cached_page *newer = cp->newer,
128433d6423SLionel Sambuc *older = cp->older;
129433d6423SLionel Sambuc if(newer) assert(newer->older == cp);
130433d6423SLionel Sambuc if(older) assert(older->newer == cp);
131433d6423SLionel Sambuc lru_total++;
132433d6423SLionel Sambuc }
133433d6423SLionel Sambuc
134433d6423SLionel Sambuc assert(lru_total == bydev_total);
135433d6423SLionel Sambuc
136433d6423SLionel Sambuc assert(lru_total == cached_pages);
137433d6423SLionel Sambuc }
138433d6423SLionel Sambuc #endif
139433d6423SLionel Sambuc
140433d6423SLionel Sambuc #define rmhash_f(fname, nextfield) \
141433d6423SLionel Sambuc static void \
142433d6423SLionel Sambuc fname(struct cached_page *cp, struct cached_page **head) \
143433d6423SLionel Sambuc { \
144433d6423SLionel Sambuc struct cached_page *hb; \
145433d6423SLionel Sambuc if(*head == cp) { *head = cp->nextfield; return; } \
146433d6423SLionel Sambuc for(hb = *head; hb && cp != hb->nextfield; hb = hb->nextfield) ; \
147433d6423SLionel Sambuc assert(hb); assert(hb->nextfield == cp); \
148433d6423SLionel Sambuc hb->nextfield = cp->nextfield; \
149433d6423SLionel Sambuc return; \
150433d6423SLionel Sambuc }
151433d6423SLionel Sambuc
rmhash_f(rmhash_byino,hash_next_ino)152433d6423SLionel Sambuc rmhash_f(rmhash_byino, hash_next_ino)
153433d6423SLionel Sambuc rmhash_f(rmhash_bydev, hash_next_dev)
154433d6423SLionel Sambuc
155433d6423SLionel Sambuc static void addcache_byino(struct cached_page *hb)
156433d6423SLionel Sambuc {
157433d6423SLionel Sambuc int hv_ino = makehash(hb->ino, hb->ino_offset);
158433d6423SLionel Sambuc assert(hb->ino != VMC_NO_INODE);
159433d6423SLionel Sambuc hb->hash_next_ino = cache_hash_byino[hv_ino];
160433d6423SLionel Sambuc cache_hash_byino[hv_ino] = hb;
161433d6423SLionel Sambuc }
162433d6423SLionel Sambuc
163433d6423SLionel Sambuc static void
update_inohash(struct cached_page * hb,ino_t ino,u64_t ino_off)164433d6423SLionel Sambuc update_inohash(struct cached_page *hb, ino_t ino, u64_t ino_off)
165433d6423SLionel Sambuc {
166433d6423SLionel Sambuc assert(ino != VMC_NO_INODE);
167433d6423SLionel Sambuc if(hb->ino != VMC_NO_INODE) {
168433d6423SLionel Sambuc int h = makehash(hb->ino, hb->ino_offset);
169433d6423SLionel Sambuc rmhash_byino(hb, &cache_hash_byino[h]);
170433d6423SLionel Sambuc }
171433d6423SLionel Sambuc hb->ino = ino;
172433d6423SLionel Sambuc hb->ino_offset = ino_off;
173433d6423SLionel Sambuc addcache_byino(hb);
174433d6423SLionel Sambuc }
175433d6423SLionel Sambuc
176433d6423SLionel Sambuc struct cached_page *
find_cached_page_bydev(dev_t dev,u64_t dev_off,ino_t ino,u64_t ino_off,int touchlru)177433d6423SLionel Sambuc find_cached_page_bydev(dev_t dev, u64_t dev_off, ino_t ino, u64_t ino_off, int touchlru)
178433d6423SLionel Sambuc {
179433d6423SLionel Sambuc struct cached_page *hb;
180433d6423SLionel Sambuc
181433d6423SLionel Sambuc for(hb = cache_hash_bydev[makehash(dev, dev_off)]; hb; hb=hb->hash_next_dev) {
182433d6423SLionel Sambuc if(hb->dev == dev && hb->dev_offset == dev_off) {
183433d6423SLionel Sambuc if(ino != VMC_NO_INODE) {
184433d6423SLionel Sambuc if(hb->ino != ino || hb->ino_offset != ino_off) {
185433d6423SLionel Sambuc update_inohash(hb, ino, ino_off);
186433d6423SLionel Sambuc }
187433d6423SLionel Sambuc }
188433d6423SLionel Sambuc
189433d6423SLionel Sambuc if(touchlru) cache_lru_touch(hb);
190433d6423SLionel Sambuc
191433d6423SLionel Sambuc return hb;
192433d6423SLionel Sambuc }
193433d6423SLionel Sambuc }
194433d6423SLionel Sambuc
195433d6423SLionel Sambuc return NULL;
196433d6423SLionel Sambuc }
197433d6423SLionel Sambuc
find_cached_page_byino(dev_t dev,ino_t ino,u64_t ino_off,int touchlru)198433d6423SLionel Sambuc struct cached_page *find_cached_page_byino(dev_t dev, ino_t ino, u64_t ino_off, int touchlru)
199433d6423SLionel Sambuc {
200433d6423SLionel Sambuc struct cached_page *hb;
201433d6423SLionel Sambuc
202433d6423SLionel Sambuc assert(ino != VMC_NO_INODE);
203433d6423SLionel Sambuc assert(dev != NO_DEV);
204433d6423SLionel Sambuc
205433d6423SLionel Sambuc for(hb = cache_hash_byino[makehash(ino, ino_off)]; hb; hb=hb->hash_next_ino) {
206433d6423SLionel Sambuc if(hb->dev == dev && hb->ino == ino && hb->ino_offset == ino_off) {
207433d6423SLionel Sambuc if(touchlru) cache_lru_touch(hb);
208433d6423SLionel Sambuc
209433d6423SLionel Sambuc return hb;
210433d6423SLionel Sambuc }
211433d6423SLionel Sambuc }
212433d6423SLionel Sambuc
213433d6423SLionel Sambuc return NULL;
214433d6423SLionel Sambuc }
215433d6423SLionel Sambuc
addcache(dev_t dev,u64_t dev_off,ino_t ino,u64_t ino_off,int flags,struct phys_block * pb)216*e321f655SDavid van Moolenbroek int addcache(dev_t dev, u64_t dev_off, ino_t ino, u64_t ino_off, int flags,
217*e321f655SDavid van Moolenbroek struct phys_block *pb)
218433d6423SLionel Sambuc {
219433d6423SLionel Sambuc int hv_dev;
220433d6423SLionel Sambuc struct cached_page *hb;
221433d6423SLionel Sambuc
222433d6423SLionel Sambuc if(pb->flags & PBF_INCACHE) {
223433d6423SLionel Sambuc printf("VM: already in cache\n");
224433d6423SLionel Sambuc return EINVAL;
225433d6423SLionel Sambuc }
226433d6423SLionel Sambuc
227433d6423SLionel Sambuc if(!SLABALLOC(hb)) {
228433d6423SLionel Sambuc printf("VM: no memory for cache node\n");
229433d6423SLionel Sambuc return ENOMEM;
230433d6423SLionel Sambuc }
231433d6423SLionel Sambuc
232433d6423SLionel Sambuc assert(dev != NO_DEV);
233433d6423SLionel Sambuc #if CACHE_SANITY
234433d6423SLionel Sambuc assert(!find_cached_page_bydev(dev, dev_off, ino, ino_off));
235433d6423SLionel Sambuc #endif
236433d6423SLionel Sambuc
237433d6423SLionel Sambuc hb->dev = dev;
238433d6423SLionel Sambuc hb->dev_offset = dev_off;
239433d6423SLionel Sambuc hb->ino = ino;
240433d6423SLionel Sambuc hb->ino_offset = ino_off;
241*e321f655SDavid van Moolenbroek hb->flags = flags & VMSF_ONCE;
242433d6423SLionel Sambuc hb->page = pb;
243433d6423SLionel Sambuc hb->page->refcount++; /* block also referenced by cache now */
244433d6423SLionel Sambuc hb->page->flags |= PBF_INCACHE;
245433d6423SLionel Sambuc
246433d6423SLionel Sambuc hv_dev = makehash(dev, dev_off);
247433d6423SLionel Sambuc
248433d6423SLionel Sambuc hb->hash_next_dev = cache_hash_bydev[hv_dev];
249433d6423SLionel Sambuc cache_hash_bydev[hv_dev] = hb;
250433d6423SLionel Sambuc
251433d6423SLionel Sambuc if(hb->ino != VMC_NO_INODE)
252433d6423SLionel Sambuc addcache_byino(hb);
253433d6423SLionel Sambuc
254433d6423SLionel Sambuc lru_add(hb);
255433d6423SLionel Sambuc
256433d6423SLionel Sambuc return OK;
257433d6423SLionel Sambuc }
258433d6423SLionel Sambuc
rmcache(struct cached_page * cp)259433d6423SLionel Sambuc void rmcache(struct cached_page *cp)
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc struct phys_block *pb = cp->page;
262433d6423SLionel Sambuc int hv_dev = makehash(cp->dev, cp->dev_offset);
263433d6423SLionel Sambuc
264433d6423SLionel Sambuc assert(cp->page->flags & PBF_INCACHE);
265433d6423SLionel Sambuc
266433d6423SLionel Sambuc cp->page->flags &= ~PBF_INCACHE;
267433d6423SLionel Sambuc
268433d6423SLionel Sambuc rmhash_bydev(cp, &cache_hash_bydev[hv_dev]);
269433d6423SLionel Sambuc if(cp->ino != VMC_NO_INODE) {
270433d6423SLionel Sambuc int hv_ino = makehash(cp->ino, cp->ino_offset);
271433d6423SLionel Sambuc rmhash_byino(cp, &cache_hash_byino[hv_ino]);
272433d6423SLionel Sambuc }
273433d6423SLionel Sambuc
274433d6423SLionel Sambuc assert(cp->page->refcount >= 1);
275433d6423SLionel Sambuc cp->page->refcount--;
276433d6423SLionel Sambuc
277433d6423SLionel Sambuc lru_rm(cp);
278433d6423SLionel Sambuc
279433d6423SLionel Sambuc if(pb->refcount == 0) {
280433d6423SLionel Sambuc assert(pb->phys != MAP_NONE);
281433d6423SLionel Sambuc free_mem(ABS2CLICK(pb->phys), 1);
282433d6423SLionel Sambuc SLABFREE(pb);
283433d6423SLionel Sambuc }
284433d6423SLionel Sambuc
285433d6423SLionel Sambuc SLABFREE(cp);
286433d6423SLionel Sambuc }
287433d6423SLionel Sambuc
cache_freepages(int pages)288433d6423SLionel Sambuc int cache_freepages(int pages)
289433d6423SLionel Sambuc {
290433d6423SLionel Sambuc struct cached_page *cp, *newercp;
291433d6423SLionel Sambuc int freed = 0;
292433d6423SLionel Sambuc int oldsteps = 0;
293433d6423SLionel Sambuc int skips = 0;
294433d6423SLionel Sambuc
295433d6423SLionel Sambuc for(cp = lru_oldest; cp && freed < pages; cp = newercp) {
296433d6423SLionel Sambuc newercp = cp->newer;
297433d6423SLionel Sambuc assert(cp->page->refcount >= 1);
298433d6423SLionel Sambuc if(cp->page->refcount == 1) {
299433d6423SLionel Sambuc rmcache(cp);
300433d6423SLionel Sambuc freed++;
301433d6423SLionel Sambuc skips = 0;
302433d6423SLionel Sambuc } else skips++;
303433d6423SLionel Sambuc oldsteps++;
304433d6423SLionel Sambuc }
305433d6423SLionel Sambuc
306433d6423SLionel Sambuc return freed;
307433d6423SLionel Sambuc }
308433d6423SLionel Sambuc
309433d6423SLionel Sambuc /*
310433d6423SLionel Sambuc * Remove all pages that are associated with the given device.
311433d6423SLionel Sambuc */
312433d6423SLionel Sambuc void
clear_cache_bydev(dev_t dev)313433d6423SLionel Sambuc clear_cache_bydev(dev_t dev)
314433d6423SLionel Sambuc {
315433d6423SLionel Sambuc struct cached_page *cp, *ncp;
316433d6423SLionel Sambuc int h;
317433d6423SLionel Sambuc
318433d6423SLionel Sambuc for (h = 0; h < HASHSIZE; h++) {
319433d6423SLionel Sambuc for (cp = cache_hash_bydev[h]; cp != NULL; cp = ncp) {
320433d6423SLionel Sambuc ncp = cp->hash_next_dev;
321433d6423SLionel Sambuc
322433d6423SLionel Sambuc if (cp->dev == dev)
323433d6423SLionel Sambuc rmcache(cp);
324433d6423SLionel Sambuc }
325433d6423SLionel Sambuc }
326433d6423SLionel Sambuc }
327433d6423SLionel Sambuc
get_stats_info(struct vm_stats_info * vsi)328433d6423SLionel Sambuc void get_stats_info(struct vm_stats_info *vsi)
329433d6423SLionel Sambuc {
330433d6423SLionel Sambuc vsi->vsi_cached = cached_pages;
331433d6423SLionel Sambuc }
332433d6423SLionel Sambuc
333