X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=lib%2Fpagecache.c;h=13ad36603fc7f69ea64c0aa9e527e74c88d8a7f5;hb=88714d18176f047eb4d298bb3f22520217671513;hp=67c1e71399835dc79c36ec258aae20b027bec7c7;hpb=302d2509b53224e828558f3f328be444adee77d6;p=libucw.git diff --git a/lib/pagecache.c b/lib/pagecache.c index 67c1e713..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,7 +31,7 @@ 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 @@ -36,16 +40,12 @@ struct page_cache { #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); @@ -55,7 +55,7 @@ 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 SHERLOCK_HAVE_PREAD +#ifndef HAVE_PREAD c->pos_fd = -1; #endif return c; @@ -68,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 @@ -102,40 +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 || c->pos_fd != fd) - sh_seek(fd, pos, SEEK_SET); - s = write(fd, p->data, c->page_size); - c->pos = pos + s; - c->pos_fd = fd; + 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. @@ -144,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); @@ -166,16 +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_pages(c, 0); + p = HEAD(c->free_pages); ASSERT(p->n.next); - flush_page(c, p); } 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); @@ -186,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); @@ -215,15 +254,15 @@ pgc_cleanup(struct page_cache *c) 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++; @@ -233,23 +272,20 @@ 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 || c->pos_fd != fd) + 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 = pos + s; @@ -258,7 +294,7 @@ pgc_read(struct page_cache *c, int fd, sh_off_t pos) 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; @@ -267,13 +303,10 @@ 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; } @@ -281,13 +314,10 @@ pgc_get(struct page_cache *c, int fd, sh_off_t pos) struct page * pgc_get_zero(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); bzero(p->data, c->page_size); p->flags |= PG_FLAG_VALID | PG_FLAG_DIRTY; return p; @@ -313,7 +343,7 @@ pgc_put(struct page_cache *c, struct page *p) else { rem_node(&p->hn); - free(p); + xfree(p); c->total_count--; } } @@ -343,12 +373,6 @@ pgc_read_data(struct page_cache *c, int fd, sh_off_t pos, uns *len) return p->data + offset; } -sh_off_t -pgc_page_pos(struct page_cache *c, struct page *p) -{ - return PAGE_NUMBER(p->key); -} - #ifdef TEST int main(int argc, char **argv)