/** * Turn LRU deferred writes on or off. * @return -1 on error with errno set, 0 if OK. */ int setwdelay(DBM *db, bool on) { struct lru_cache *cache = db->cache; if (NULL == cache) return init_cache(db, LRU_PAGES, on); sdbm_lru_check(cache); if (on == cache->write_deferred) return 0; /* * Value is inverted. */ if (cache->write_deferred) { flush_dirtypag(db); cache->write_deferred = FALSE; } else { cache->write_deferred = TRUE; } return 0; }
/** * Mark current page as dirty. * If there are no deferred writes, the page is immediately flushed to disk. * If ``force'' is TRUE, we also ignore deferred writes and flush the page. * @return TRUE on success. */ bool dirtypag(DBM *db, bool force) { struct lru_cache *cache = db->cache; long n; sdbm_lru_check(cache); n = (db->pagbuf - cache->arena) / DBM_PBLKSIZ; g_assert(n >= 0 && n < cache->pages); g_assert(db->pagbno == cache->numpag[n]); if (cache->write_deferred && !force) { if (cache->dirty[n]) cache->whits++; /* Was already dirty -> write cache hit */ else cache->wmisses++; cache->dirty[n] = TRUE; return TRUE; } /* * Flush current page to the kernel. If they are forcing the flush, * make sure we ask the kernel to synchronize the data as well. */ if (flushpag(db, db->pagbuf, db->pagbno)) { cache->dirty[n] = FALSE; if G_UNLIKELY(force) fd_fdatasync(db->pagf); return TRUE; }
/** * Flush all the dirty pages to disk. * * @return the amount of pages successfully flushed as a positive number * if everything was fine, 0 if there was nothing to flush, and -1 if there * were I/O errors (errno is set). */ ssize_t flush_dirtypag(DBM *db) { struct lru_cache *cache = db->cache; int n; ssize_t amount = 0; int saved_errno = 0; long pages; sdbm_lru_check(cache); pages = MIN(cache->pages, cache->next); for (n = 0; n < pages; n++) { if (cache->dirty[n]) { long num = cache->numpag[n]; if (writebuf(db, num, n)) { amount++; } else { saved_errno = errno; } } } if (saved_errno != 0) { errno = saved_errno; return -1; } return amount; }
/** * Close (i.e. free) the LRU page cache. * * @attention * This does not attempt to flush any remaining dirty pages. */ void lru_close(DBM *db) { struct lru_cache *cache = db->cache; if (cache) { sdbm_lru_check(cache); if (common_stats) log_lrustats(db); free_cache(cache); cache->magic = 0; WFREE(cache); } db->cache = NULL; }
static void log_lrustats(DBM *db) { struct lru_cache *cache = db->cache; unsigned long raccesses = cache->rhits + cache->rmisses; unsigned long waccesses = cache->whits + cache->wmisses; sdbm_lru_check(cache); s_info("sdbm: \"%s\" LRU cache size = %ld page%s, %s writes, %s DB", sdbm_name(db), cache->pages, plural(cache->pages), cache->write_deferred ? "deferred" : "synchronous", db->is_volatile ? "volatile" : "persistent"); s_info("sdbm: \"%s\" LRU read cache hits = %.2f%% on %lu request%s", sdbm_name(db), cache->rhits * 100.0 / MAX(raccesses, 1), raccesses, plural(raccesses)); s_info("sdbm: \"%s\" LRU write cache hits = %.2f%% on %lu request%s", sdbm_name(db), cache->whits * 100.0 / MAX(waccesses, 1), waccesses, plural(waccesses)); }
/** * Close the LRU page cache. */ void lru_close(DBM *db) { struct lru_cache *cache = db->cache; if (cache) { sdbm_lru_check(cache); if (!db->is_volatile && !(db->flags & DBM_BROKEN)) flush_dirtypag(db); if (common_stats) log_lrustats(db); free_cache(cache); cache->magic = 0; WFREE(cache); } db->cache = NULL; }
/** * Set the page cache size. * @return 0 if OK, -1 on failure with errno set. */ int setcache(DBM *db, long pages) { struct lru_cache *cache = db->cache; bool wdelay; sdbm_lru_check(cache); if (pages <= 0) { errno = EINVAL; return -1; } if (NULL == cache) return init_cache(db, pages, FALSE); /* * Easiest case: the size identical. */ if (pages == cache->pages) return 0; /* * Cache size is changed. * * This means the arena will be reallocated, so we must invalidate the * current db->pagbuf pointer, which lies within the old arena. It is * sufficient to reset db->pagbno, forcing a reload from the upper layers. * Note than when the cache size is enlarged, the old page is still cached * so reloading will be just a matter of recomputing db->pagbuf. We could * do so here, but cache size changes should only be infrequent. * * We also reset all the cache statistics, since a different cache size * will imply a different set of hit/miss ratio. */ db->pagbno = -1; /* Current page address will become invalid */ db->pagbuf = NULL; if (common_stats) { s_info("sdbm: \"%s\" LRU cache size %s from %ld page%s to %ld", sdbm_name(db), pages > cache->pages ? "increased" : "decreased", cache->pages, plural(cache->pages), pages); log_lrustats(db); } cache->rhits = cache->rmisses = 0; cache->whits = cache->wmisses = 0; /* * Straightforward: the size is increased. */ if (pages > cache->pages) { char *new_arena = vmm_alloc(pages * DBM_PBLKSIZ); if (NULL == new_arena) return -1; memmove(new_arena, cache->arena, cache->pages * DBM_PBLKSIZ); vmm_free(cache->arena, cache->pages * DBM_PBLKSIZ); cache->arena = new_arena; cache->dirty = wrealloc(cache->dirty, cache->pages, pages); cache->numpag = wrealloc(cache->numpag, cache->pages * sizeof(long), pages * sizeof(long)); cache->pages = pages; return 0; } /* * Difficult: the size is decreased. * * The current page buffer could point in a cache area that is going * to disappear, and the internal data structures must forget about * all the old indices that are greater than the new limit. * * We do not try to optimize anything here, as this call should happen * only infrequently: we flush the current cache (in case there are * deferred writes), destroy the LRU cache data structures, recreate a * new one and invalidate the current DB page. */ wdelay = cache->write_deferred; flush_dirtypag(db); free_cache(cache); return setup_cache(cache, pages, wdelay); }