2 * Sherlock Library -- File Page Cache
4 * (c) 1999--2002 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
11 #include "lib/pagecache.h"
21 list free_pages; /* LRU queue of free non-dirty pages */
22 list locked_pages; /* List of locked pages (starts with dirty ones) */
23 list dirty_pages; /* List of free dirty pages */
24 uns page_size; /* Bytes per page (must be a power of two) */
25 uns free_count; /* Number of free / dirty pages */
26 uns total_count; /* Total number of pages */
27 uns max_pages; /* Maximum number of free pages */
28 uns hash_size; /* Hash table size */
29 uns stat_hit; /* Number of cache hits */
30 uns stat_miss; /* Number of cache misses */
31 uns stat_write; /* Number of writes */
32 list *hash_table; /* List heads corresponding to hash buckets */
33 #ifndef SHERLOCK_HAVE_PREAD
34 sh_off_t pos; /* Current position in the file */
35 int pos_fd; /* FD the position corresponds to */
39 #define PAGE_NUMBER(pos) ((pos) & ~(sh_off_t)(c->page_size - 1))
40 #define PAGE_OFFSET(pos) ((pos) & (c->page_size - 1))
43 pgc_open(uns page_size, uns max_pages)
45 struct page_cache *c = xmalloc_zero(sizeof(struct page_cache));
48 init_list(&c->free_pages);
49 init_list(&c->locked_pages);
50 init_list(&c->dirty_pages);
51 c->page_size = page_size;
52 c->max_pages = max_pages;
53 c->hash_size = nextprime(c->max_pages);
54 c->hash_table = xmalloc(sizeof(list) * c->hash_size);
55 for(i=0; i<c->hash_size; i++)
56 init_list(&c->hash_table[i]);
57 #ifndef SHERLOCK_HAVE_PREAD
64 pgc_close(struct page_cache *c)
67 ASSERT(EMPTY_LIST(c->locked_pages));
68 ASSERT(EMPTY_LIST(c->dirty_pages));
69 ASSERT(EMPTY_LIST(c->free_pages));
75 pgc_debug_page(struct page *p)
77 printf("\tp=%08x d=%d f=%x c=%d\n", (uns) p->pos, p->fd, p->flags, p->lock_count);
81 pgc_debug(struct page_cache *c, int mode)
85 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);
86 printf(">> stats: %d hits, %d misses, %d writes\n", c->stat_hit, c->stat_miss, c->stat_write);
90 WALK_LIST(p, c->free_pages)
93 WALK_LIST(p, c->locked_pages)
96 WALK_LIST(p, c->dirty_pages)
102 flush_page(struct page_cache *c, struct page *p)
106 ASSERT(p->flags & PG_FLAG_DIRTY);
107 #ifdef SHERLOCK_HAVE_PREAD
108 s = sh_pwrite(p->fd, p->data, c->page_size, p->pos);
110 if (c->pos != p->pos || c->pos_fd != (int) p->fd)
111 sh_seek(p->fd, p->pos, SEEK_SET);
112 s = write(p->fd, p->data, c->page_size);
117 die("pgc_write(%d): %m", p->fd);
118 if (s != (int) c->page_size)
119 die("pgc_write(%d): incomplete page (only %d of %d)", p->fd, s, c->page_size);
120 p->flags &= ~PG_FLAG_DIRTY;
125 flush_cmp(const void *X, const void *Y)
127 struct page *x = *((struct page **)X);
128 struct page *y = *((struct page **)Y);
142 flush_pages(struct page_cache *c, uns force)
145 uns max = force ? ~0U : c->free_count / 2; /* FIXME: Needs tuning */
147 struct page *p, *q, **req, **rr;
149 WALK_LIST(p, c->dirty_pages)
155 req = rr = alloca(cnt * sizeof(struct page *));
157 p = HEAD(c->dirty_pages);
158 while ((q = (struct page *) p->n.next) && i--)
161 add_tail(&c->free_pages, &p->n);
165 qsort(req, cnt, sizeof(struct page *), flush_cmp);
167 flush_page(c, req[i]);
171 hash_page(struct page_cache *c, sh_off_t pos, uns fd)
173 return (pos + fd) % c->hash_size;
177 get_page(struct page_cache *c, sh_off_t pos, uns fd)
181 uns hash = hash_page(c, pos, fd);
184 * Return locked buffer for given page.
187 WALK_LIST(n, c->hash_table[hash])
189 p = SKIP_BACK(struct page, hn, n);
190 if (p->pos == pos && p->fd == fd)
192 /* Found in the cache */
199 if (c->total_count < c->max_pages || !c->free_count)
201 /* Enough free space, expand the cache */
202 p = xmalloc(sizeof(struct page) + c->page_size);
207 /* Discard the oldest unlocked page */
208 p = HEAD(c->free_pages);
211 /* There are only dirty pages here */
213 p = HEAD(c->free_pages);
216 ASSERT(!p->lock_count);
225 add_tail(&c->hash_table[hash], &p->hn);
230 pgc_flush(struct page_cache *c)
235 WALK_LIST(p, c->locked_pages)
236 if (p->flags & PG_FLAG_DIRTY)
243 pgc_cleanup(struct page_cache *c)
249 WALK_LIST_DELSAFE(p, n, c->free_pages)
251 ASSERT(!(p->flags & PG_FLAG_DIRTY) && !p->lock_count);
258 ASSERT(!c->free_count);
261 static inline struct page *
262 get_and_lock_page(struct page_cache *c, sh_off_t pos, uns fd)
264 struct page *p = get_page(c, pos, fd);
266 add_tail(&c->locked_pages, &p->n);
272 pgc_read(struct page_cache *c, int fd, sh_off_t pos)
277 ASSERT(!PAGE_OFFSET(pos));
278 p = get_and_lock_page(c, pos, fd);
279 if (p->flags & PG_FLAG_VALID)
284 #ifdef SHERLOCK_HAVE_PREAD
285 s = sh_pread(fd, p->data, c->page_size, pos);
287 if (c->pos != pos || c->pos_fd != (int)fd)
288 sh_seek(fd, pos, SEEK_SET);
289 s = read(fd, p->data, c->page_size);
294 die("pgc_read(%d): %m", fd);
295 if (s != (int) c->page_size)
296 die("pgc_read(%d): incomplete page (only %d of %d)", p->fd, s, c->page_size);
297 p->flags |= PG_FLAG_VALID;
303 pgc_get(struct page_cache *c, int fd, sh_off_t pos)
307 ASSERT(!PAGE_OFFSET(pos));
308 p = get_and_lock_page(c, pos, fd);
309 p->flags |= PG_FLAG_VALID | PG_FLAG_DIRTY;
314 pgc_get_zero(struct page_cache *c, int fd, sh_off_t pos)
318 ASSERT(!PAGE_OFFSET(pos));
319 p = get_and_lock_page(c, pos, fd);
320 bzero(p->data, c->page_size);
321 p->flags |= PG_FLAG_VALID | PG_FLAG_DIRTY;
326 pgc_put(struct page_cache *c, struct page *p)
328 ASSERT(p->lock_count);
332 if (p->flags & PG_FLAG_DIRTY)
334 add_tail(&c->dirty_pages, &p->n);
337 else if (c->free_count < c->max_pages)
339 add_tail(&c->free_pages, &p->n);
351 pgc_mark_dirty(struct page_cache *c, struct page *p)
353 ASSERT(p->lock_count);
354 if (!(p->flags & PG_FLAG_DIRTY))
356 p->flags |= PG_FLAG_DIRTY;
358 add_head(&c->locked_pages, &p->n);
363 pgc_read_data(struct page_cache *c, int fd, sh_off_t pos, uns *len)
366 sh_off_t page = PAGE_NUMBER(pos);
367 uns offset = PAGE_OFFSET(pos);
369 p = pgc_read(c, fd, page);
371 *len = c->page_size - offset;
372 return p->data + offset;
377 int main(int argc, char **argv)
379 struct page_cache *c = pgc_open(1024, 2);
380 struct page *p, *q, *r;
381 int fd = open("test", O_RDWR | O_CREAT | O_TRUNC, 0666);
385 p = pgc_get(c, fd, 0);
387 strcpy(p->data, "one");
390 p = pgc_get(c, fd, 1024);
392 strcpy(p->data, "two");
395 p = pgc_get(c, fd, 2048);
397 strcpy(p->data, "three");
402 p = pgc_read(c, fd, 0);
404 strcpy(p->data, "odin");
405 pgc_mark_dirty(c, p);
409 q = pgc_read(c, fd, 1024);
411 r = pgc_read(c, fd, 2048);
417 p = pgc_get(c, fd, 3072);
419 strcpy(p->data, "four");