xref: /minix3/minix/servers/vm/cache.c (revision e321f6558257ab50a8b750451c96d1a3ca19e300)
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