2 * Sherlock Library -- File Page Cache
4 * (c) 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
14 #include "pagecache.h"
18 list free_pages; /* LRU queue of free pages */
19 list locked_pages; /* List of locked pages */
20 list dirty_pages; /* List of dirty pages */
21 uns page_size; /* Bytes per page (must be a power of two) */
22 uns free_count; /* Number of free / dirty pages */
23 uns total_count; /* Total number of pages */
24 uns max_pages; /* Maximum number of free pages */
25 uns hash_size; /* Hash table size */
26 list *hash_table; /* List heads corresponding to hash buckets */
29 /* FIXME: Negations -> cast to sh_off_t */
32 pgc_open(uns page_size, uns max_pages)
34 struct page_cache *c = xmalloc(sizeof(struct page_cache));
38 init_list(&c->free_pages);
39 init_list(&c->locked_pages);
40 init_list(&c->dirty_pages);
41 c->page_size = page_size;
42 c->max_pages = max_pages;
43 c->hash_size = nextprime(c->max_pages);
44 c->hash_table = xmalloc(sizeof(list) * c->hash_size);
45 for(i=0; i<c->hash_size; i++)
46 init_list(&c->hash_table[i]);
51 pgc_close(struct page_cache *c)
54 ASSERT(EMPTY_LIST(c->locked_pages));
55 ASSERT(EMPTY_LIST(c->dirty_pages));
56 ASSERT(EMPTY_LIST(c->free_pages));
62 pgc_debug_page(struct page *p)
64 printf("\tk=%08x f=%x c=%d\n", (uns) p->key, p->flags, p->lock_count);
68 pgc_debug(struct page_cache *c)
72 printf(">> Page cache dump: pgsize=%d, pages=%d, freepages=%d of %d, hash=%d\n", c->page_size, c->total_count, c->free_count, c->max_pages, c->hash_size);
74 WALK_LIST(p, c->free_pages)
77 WALK_LIST(p, c->locked_pages)
80 WALK_LIST(p, c->dirty_pages)
85 flush_page(struct page_cache *c, struct page *p)
87 int fd = p->key & (c->page_size - 1);
88 sh_off_t pos = p->key & ~(c->page_size - 1);
91 ASSERT((p->flags & PG_FLAG_DIRTY) && !p->lock_count);
92 /* FIXME: Use pwrite() */
93 sh_seek(fd, pos, SEEK_SET);
94 s = write(fd, p->data, c->page_size);
96 die("pgc_write(%d): %m", fd);
97 if (s != (int) c->page_size)
98 die("pgc_write(%d): incomplete page (only %d of %d)", s, c->page_size);
99 p->flags &= ~PG_FLAG_DIRTY;
103 hash_page(struct page_cache *c, sh_off_t key)
105 return key % c->hash_size; /* FIXME: Use better hash function */
109 get_page(struct page_cache *c, sh_off_t key)
113 uns hash = hash_page(c, key);
116 * Return locked buffer for given page.
119 WALK_LIST(n, c->hash_table[hash])
121 p = SKIP_BACK(struct page, hn, n);
124 /* Found in the cache */
131 if (c->total_count < c->max_pages || !c->free_count)
133 /* Enough free space, expand the cache */
134 p = xmalloc(sizeof(struct page) + c->page_size);
139 /* Discard the oldest unlocked page */
140 p = HEAD(c->free_pages);
143 /* There are only dirty pages here */
144 p = HEAD(c->dirty_pages);
147 ASSERT(!p->lock_count);
155 add_tail(&c->hash_table[hash], &p->hn);
160 pgc_flush(struct page_cache *c)
165 WALK_LIST_DELSAFE(p, n, c->dirty_pages)
169 add_tail(&c->free_pages, &p->n);
174 pgc_cleanup(struct page_cache *c)
180 WALK_LIST_DELSAFE(p, n, c->free_pages)
182 ASSERT(!(p->flags & PG_FLAG_DIRTY) && !p->lock_count);
188 ASSERT(!c->free_count);
191 static inline struct page *
192 get_and_lock_page(struct page_cache *c, sh_off_t key)
194 struct page *p = get_page(c, key);
196 add_tail(&c->locked_pages, &p->n);
202 pgc_read(struct page_cache *c, int fd, sh_off_t pos)
208 ASSERT(!(pos & (c->page_size - 1)));
209 ASSERT(!(fd & ~(c->page_size - 1)));
211 p = get_and_lock_page(c, key);
212 if (!(p->flags & PG_FLAG_VALID))
214 /* FIXME: Use pread() */
215 sh_seek(fd, pos, SEEK_SET);
216 s = read(fd, p->data, c->page_size);
218 die("pgc_read(%d): %m", fd);
219 if (s != (int) c->page_size)
220 die("pgc_read(%d): incomplete page (only %d of %d)", s, c->page_size);
221 p->flags |= PG_FLAG_VALID;
227 pgc_get(struct page_cache *c, int fd, sh_off_t pos)
232 ASSERT(!(pos & (c->page_size - 1)));
233 ASSERT(!(fd & ~(c->page_size - 1)));
235 p = get_and_lock_page(c, key);
236 p->flags |= PG_FLAG_VALID | PG_FLAG_DIRTY;
241 pgc_put(struct page_cache *c, struct page *p)
243 ASSERT(p->lock_count);
247 if (p->flags & PG_FLAG_DIRTY)
249 add_tail(&c->dirty_pages, &p->n);
252 else if (c->free_count < c->max_pages)
254 add_tail(&c->free_pages, &p->n);
265 pgc_put_dirty(struct page_cache *c, struct page *p)
267 p->flags |= PG_FLAG_DIRTY;
272 pgc_read_data(struct page_cache *c, int fd, sh_off_t pos, uns *len)
275 sh_off_t page = pos & ~(c->page_size - 1);
276 uns offset = pos & (c->page_size - 1);
278 p = pgc_read(c, fd, page);
280 *len = c->page_size - offset;
281 return p->data + offset;
286 int main(int argc, char **argv)
288 struct page_cache *c = pgc_open(1024, 2);
289 struct page *p, *q, *r;
290 int fd = open("test", O_RDWR | O_CREAT | O_TRUNC, 0666);
294 p = pgc_get(c, fd, 0);
296 strcpy(p->data, "one");
299 p = pgc_get(c, fd, 1024);
301 strcpy(p->data, "two");
304 p = pgc_get(c, fd, 2048);
306 strcpy(p->data, "three");
311 p = pgc_read(c, fd, 0);
313 q = pgc_read(c, fd, 1024);
315 r = pgc_read(c, fd, 2048);
321 p = pgc_get(c, fd, 3072);