X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=inline;f=lib%2Fpagecache.c;h=13ad36603fc7f69ea64c0aa9e527e74c88d8a7f5;hb=8977e6ac284666f602be2a8d954842c343854e41;hp=1ffb1886c7c635e09e53b4b9c430100585902e93;hpb=b722c9ee33b155b2065605441923a3fcfec06dce;p=libucw.git diff --git a/lib/pagecache.c b/lib/pagecache.c index 1ffb1886..13ad3660 100644 --- a/lib/pagecache.c +++ b/lib/pagecache.c @@ -1,18 +1,22 @@ /* - * Sherlock Library -- File Page Cache + * UCW Library -- File Page Cache * - * (c) 1999 Martin Mares + * (c) 1999--2002 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. */ +#include "lib/lib.h" +#include "lib/pagecache.h" +#include "lib/lfs.h" + #include #include #include #include #include - -#include "lib.h" -#include "pagecache.h" -#include "lfs.h" +#include struct page_cache { list free_pages; /* LRU queue of free non-dirty pages */ @@ -27,24 +31,21 @@ struct page_cache { uns stat_miss; /* Number of cache misses */ uns stat_write; /* Number of writes */ list *hash_table; /* List heads corresponding to hash buckets */ -#ifndef SHERLOCK_HAVE_PREAD +#ifndef HAVE_PREAD sh_off_t pos; /* Current position in the file */ + int pos_fd; /* FD the position corresponds to */ #endif }; #define PAGE_NUMBER(pos) ((pos) & ~(sh_off_t)(c->page_size - 1)) #define PAGE_OFFSET(pos) ((pos) & (c->page_size - 1)) -#define KEY_PAGE(key) PAGE_NUMBER(key) -#define KEY_FD(key) PAGE_OFFSET(key) - struct page_cache * pgc_open(uns page_size, uns max_pages) { - struct page_cache *c = xmalloc(sizeof(struct page_cache)); + struct page_cache *c = xmalloc_zero(sizeof(struct page_cache)); uns i; - bzero(c, sizeof(*c)); init_list(&c->free_pages); init_list(&c->locked_pages); init_list(&c->dirty_pages); @@ -54,6 +55,9 @@ pgc_open(uns page_size, uns max_pages) c->hash_table = xmalloc(sizeof(list) * c->hash_size); for(i=0; ihash_size; i++) init_list(&c->hash_table[i]); +#ifndef HAVE_PREAD + c->pos_fd = -1; +#endif return c; } @@ -64,14 +68,14 @@ pgc_close(struct page_cache *c) ASSERT(EMPTY_LIST(c->locked_pages)); ASSERT(EMPTY_LIST(c->dirty_pages)); ASSERT(EMPTY_LIST(c->free_pages)); - free(c->hash_table); - free(c); + xfree(c->hash_table); + xfree(c); } static void pgc_debug_page(struct page *p) { - printf("\tk=%08x f=%x c=%d\n", (uns) p->key, p->flags, p->lock_count); + printf("\tp=%08x d=%d f=%x c=%d\n", (uns) p->pos, p->fd, p->flags, p->lock_count); } void @@ -98,39 +102,84 @@ pgc_debug(struct page_cache *c, int mode) static void flush_page(struct page_cache *c, struct page *p) { - int fd = KEY_FD(p->key); - sh_off_t pos = KEY_PAGE(p->key); int s; ASSERT(p->flags & PG_FLAG_DIRTY); -#ifdef SHERLOCK_HAVE_PREAD - s = pwrite(fd, p->data, c->page_size, pos); +#ifdef HAVE_PREAD + s = sh_pwrite(p->fd, p->data, c->page_size, p->pos); #else - if (c->pos != pos) - sh_seek(fd, pos, SEEK_SET); - s = write(fd, p->data, c->page_size); - c->pos += s; + if (c->pos != p->pos || c->pos_fd != (int) p->fd) + sh_seek(p->fd, p->pos, SEEK_SET); + s = write(p->fd, p->data, c->page_size); + c->pos = p->pos + s; + c->pos_fd = p->fd; #endif if (s < 0) - die("pgc_write(%d): %m", fd); + die("pgc_write(%d): %m", p->fd); if (s != (int) c->page_size) - die("pgc_write(%d): incomplete page (only %d of %d)", s, c->page_size); + die("pgc_write(%d): incomplete page (only %d of %d)", p->fd, s, c->page_size); p->flags &= ~PG_FLAG_DIRTY; c->stat_write++; } +static int +flush_cmp(const void *X, const void *Y) +{ + struct page *x = *((struct page **)X); + struct page *y = *((struct page **)Y); + + if (x->fd < y->fd) + return -1; + if (x->fd > y->fd) + return 1; + if (x->pos < y->pos) + return -1; + if (x->pos > y->pos) + return 1; + return 0; +} + +static void +flush_pages(struct page_cache *c, uns force) +{ + uns cnt = 0; + uns max = force ? ~0U : c->free_count / 2; + uns i; + struct page *p, *q, **req, **rr; + + WALK_LIST(p, c->dirty_pages) + { + cnt++; + if (cnt >= max) + break; + } + req = rr = alloca(cnt * sizeof(struct page *)); + i = cnt; + p = HEAD(c->dirty_pages); + while ((q = (struct page *) p->n.next) && i--) + { + rem_node(&p->n); + add_tail(&c->free_pages, &p->n); + *rr++ = p; + p = q; + } + qsort(req, cnt, sizeof(struct page *), flush_cmp); + for(i=0; ihash_size; + return (pos + fd) % c->hash_size; } static struct page * -get_page(struct page_cache *c, sh_off_t key) +get_page(struct page_cache *c, sh_off_t pos, uns fd) { node *n; struct page *p; - uns hash = hash_page(c, key); + uns hash = hash_page(c, pos, fd); /* * Return locked buffer for given page. @@ -139,7 +188,7 @@ get_page(struct page_cache *c, sh_off_t key) WALK_LIST(n, c->hash_table[hash]) { p = SKIP_BACK(struct page, hn, n); - if (p->key == key) + if (p->pos == pos && p->fd == fd) { /* Found in the cache */ rem_node(&p->n); @@ -161,15 +210,17 @@ get_page(struct page_cache *c, sh_off_t key) if (!p->n.next) { /* There are only dirty pages here */ - p = HEAD(c->dirty_pages); - flush_page(c, p); + flush_pages(c, 0); + p = HEAD(c->free_pages); + ASSERT(p->n.next); } ASSERT(!p->lock_count); rem_node(&p->n); rem_node(&p->hn); c->free_count--; } - p->key = key; + p->pos = pos; + p->fd = fd; p->flags = 0; p->lock_count = 0; add_tail(&c->hash_table[hash], &p->hn); @@ -180,14 +231,8 @@ void pgc_flush(struct page_cache *c) { struct page *p; - node *n; - WALK_LIST_DELSAFE(p, n, c->dirty_pages) - { - flush_page(c, p); - rem_node(&p->n); - add_tail(&c->free_pages, &p->n); - } + flush_pages(c, 1); WALK_LIST(p, c->locked_pages) if (p->flags & PG_FLAG_DIRTY) flush_page(c, p); @@ -206,17 +251,18 @@ pgc_cleanup(struct page_cache *c) { ASSERT(!(p->flags & PG_FLAG_DIRTY) && !p->lock_count); rem_node(&p->n); + rem_node(&p->hn); c->free_count--; c->total_count--; - free(p); + xfree(p); } ASSERT(!c->free_count); } static inline struct page * -get_and_lock_page(struct page_cache *c, sh_off_t key) +get_and_lock_page(struct page_cache *c, sh_off_t pos, uns fd) { - struct page *p = get_page(c, key); + struct page *p = get_page(c, pos, fd); add_tail(&c->locked_pages, &p->n); p->lock_count++; @@ -226,31 +272,29 @@ get_and_lock_page(struct page_cache *c, sh_off_t key) struct page * pgc_read(struct page_cache *c, int fd, sh_off_t pos) { - sh_off_t key; struct page *p; int s; ASSERT(!PAGE_OFFSET(pos)); - ASSERT(!PAGE_NUMBER(fd)); - key = pos | fd; - p = get_and_lock_page(c, key); + p = get_and_lock_page(c, pos, fd); if (p->flags & PG_FLAG_VALID) c->stat_hit++; else { c->stat_miss++; -#ifdef SHERLOCK_HAVE_PREAD - s = pread(fd, p->data, c->page_size, pos); +#ifdef HAVE_PREAD + s = sh_pread(fd, p->data, c->page_size, pos); #else - if (c->pos != pos) + if (c->pos != pos || c->pos_fd != (int)fd) sh_seek(fd, pos, SEEK_SET); s = read(fd, p->data, c->page_size); - c->pos += s; + c->pos = pos + s; + c->pos_fd = fd; #endif if (s < 0) die("pgc_read(%d): %m", fd); if (s != (int) c->page_size) - die("pgc_read(%d): incomplete page (only %d of %d)", s, c->page_size); + die("pgc_read(%d): incomplete page (only %d of %d)", p->fd, s, c->page_size); p->flags |= PG_FLAG_VALID; } return p; @@ -259,13 +303,22 @@ pgc_read(struct page_cache *c, int fd, sh_off_t pos) struct page * pgc_get(struct page_cache *c, int fd, sh_off_t pos) { - sh_off_t key; struct page *p; ASSERT(!PAGE_OFFSET(pos)); - ASSERT(!PAGE_NUMBER(fd)); - key = pos | fd; - p = get_and_lock_page(c, key); + p = get_and_lock_page(c, pos, fd); + p->flags |= PG_FLAG_VALID | PG_FLAG_DIRTY; + return p; +} + +struct page * +pgc_get_zero(struct page_cache *c, int fd, sh_off_t pos) +{ + struct page *p; + + ASSERT(!PAGE_OFFSET(pos)); + p = get_and_lock_page(c, pos, fd); + bzero(p->data, c->page_size); p->flags |= PG_FLAG_VALID | PG_FLAG_DIRTY; return p; } @@ -289,7 +342,8 @@ pgc_put(struct page_cache *c, struct page *p) } else { - free(p); + rem_node(&p->hn); + xfree(p); c->total_count--; } }