file-cache.c revision 8da8fa22f337c7e9f9ec6183e22f351811478387
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch/* Copyright (c) 2004 Timo Sirainen */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "lib.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "buffer.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "mmap-util.h"
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch#include "file-cache.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
833bed942977673526c72e79bccc09314fc57104Phil Carmody#include <sys/stat.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
38af46387e565053adf6c47f7f6871676d685de8Stephan Boschstruct file_cache {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch int fd;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_t *page_bitmask;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch void *mmap_base;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch size_t mmap_length;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch size_t read_highwater;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch};
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Boschstruct file_cache *file_cache_new(int fd)
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch{
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch struct file_cache *cache;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch cache = i_new(struct file_cache, 1);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch cache->fd = fd;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return cache;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid file_cache_free(struct file_cache **_cache)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct file_cache *cache = *_cache;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *_cache = NULL;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (cache->mmap_base != NULL) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_error("munmap_anon() failed: %m");
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(cache->page_bitmask);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_free(cache);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid file_cache_set_fd(struct file_cache *cache, int fd)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch cache->fd = fd;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch file_cache_invalidate(cache, 0, cache->mmap_length);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic int file_cache_set_size(struct file_cache *cache, uoff_t size)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t page_size = mmap_get_page_size();
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch uoff_t diff = size % page_size;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch void *new_base;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (diff != 0)
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch size += page_size - diff;
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch i_assert((size % page_size) == 0);
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch if (size <= cache->mmap_length)
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch return 0;
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch if (size > (size_t)-1) {
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch i_error("file_cache_set_size(%"PRIuUOFF_T"): size too large",
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* grow mmaping */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (cache->mmap_base == NULL) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch cache->mmap_base = mmap_anon(size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (cache->mmap_base == MAP_FAILED) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_error("mmap_anon(%"PRIuUOFF_T") failed: %m", size);
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch cache->mmap_length = 0;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch } else {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size, MREMAP_MAYMOVE);
7c7117e542b6a44c1db7fc91c0180bdace6dbce7Stephan Bosch if (new_base == MAP_FAILED) {
7c7117e542b6a44c1db7fc91c0180bdace6dbce7Stephan Bosch i_error("mremap_anon(%"PRIuUOFF_T") failed: %m", size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen cache->mmap_base = new_base;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen cache->mmap_length = size;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return 0;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen}
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen size_t page_size = mmap_get_page_size();
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t poffset, psize, dest_offset, dest_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned char *bits, *dest;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch ssize_t ret;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (size > SSIZE_T_MAX) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* make sure our calculations won't overflow. most likely
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch we'll be reading less data, but allow it anyway so caller
dc05b1fb4b7a2b4d91248078311458cb4cbad9a1Stephan Bosch doesn't have to deal with any extra checks. */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size = SSIZE_T_MAX;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (offset >= (uoff_t)-1 - size)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size = (uoff_t)-1 - offset;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (offset + size > cache->mmap_length &&
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch offset + size - cache->mmap_length > 1024*1024) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* growing more than a megabyte, make sure that the
201c3b9375760bafbc180629b4c6ad71ed554aecStephan Bosch file is large enough so we don't allocate memory
201c3b9375760bafbc180629b4c6ad71ed554aecStephan Bosch more than needed */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct stat st;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (fstat(cache->fd, &st) < 0) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (errno != ESTALE)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_error("fstat(file_cache) failed: %m");
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (offset + size > (uoff_t)st.st_size) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (offset >= (uoff_t)st.st_size)
a5886aec87fbfd767a110e6168ce96411acfe798Stephan Bosch return 0;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size = (uoff_t)st.st_size - offset;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (file_cache_set_size(cache, offset + size) < 0)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch poffset = offset / page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch psize = (offset + size + page_size-1) / page_size - poffset;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(psize > 0);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch (poffset + psize + CHAR_BIT - 1) /
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch CHAR_BIT);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest_offset = poffset * page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest = PTR_OFFSET(cache->mmap_base, dest_offset);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest_size = page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch while (psize > 0) {
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch if (bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) {
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch /* page is already in cache */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest_offset += page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (dest_offset <= cache->read_highwater) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch psize--; poffset++;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest += page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch continue;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* this is the last partially cached block.
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen use the caching only if we don't want to
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen read past read_highwater */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (offset + size <= cache->read_highwater) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen i_assert(psize == 1);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* mark the block noncached again and
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch read it */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch bits[poffset / CHAR_BIT] &=
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch ~(1 << (poffset % CHAR_BIT));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest_offset -= page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch ret = pread(cache->fd, dest, dest_size, dest_offset);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (ret <= 0) {
e1a4ea6ad3e799ef8df7395e765c0ae9218e6c5dStephan Bosch if (ret < 0)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* EOF. mark the last block as cached even if it
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch isn't completely. read_highwater tells us how far
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch we've actually made. */
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (dest_offset == cache->read_highwater) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch i_assert(poffset ==
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch cache->read_highwater / page_size);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch bits[poffset / CHAR_BIT] |=
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch 1 << (poffset % CHAR_BIT);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch return dest_offset <= offset ? 0 :
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch dest_offset - offset < size ?
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch dest_offset - offset : size;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch dest += ret;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch dest_offset += ret;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (cache->read_highwater < dest_offset) {
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch unsigned int high_poffset =
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch cache->read_highwater / page_size;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch /* read_highwater needs to be updated. if we didn't
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch just read that block, we can't trust anymore that
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch we have it cached */
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch bits[high_poffset / CHAR_BIT] &=
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch ~(1 << (high_poffset % CHAR_BIT));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch cache->read_highwater = dest_offset;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if ((size_t)ret != dest_size) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* partial read - probably EOF but make sure. */
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch dest_size -= ret;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch continue;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch dest_size = page_size;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch psize--; poffset++;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Boschconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *size_r = cache->read_highwater;
9ec9b6f85c8fbe67bfac523a5e3d33d34f72dddcStephan Bosch return cache->mmap_base;
9ec9b6f85c8fbe67bfac523a5e3d33d34f72dddcStephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch uoff_t offset)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t page_size = mmap_get_page_size();
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned char *bits;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned int first_page, last_page;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch i_assert(size < (uoff_t)-1 && offset < (uoff_t)-1 - size);
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch if (file_cache_set_size(cache, offset + size) < 0) {
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch /* couldn't grow mapping. just make sure the written memory
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch area is invalidated then. */
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch file_cache_invalidate(cache, offset, size);
38af46387e565053adf6c47f7f6871676d685de8Stephan Bosch return;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (cache->read_highwater < offset + size) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch unsigned int page = cache->read_highwater / page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch bits = buffer_get_space_unsafe(cache->page_bitmask,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch page / CHAR_BIT, 1);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *bits &= ~(1 << (page % CHAR_BIT));
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch cache->read_highwater = offset + size;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* mark fully written pages cached */
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (size >= page_size) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch first_page = offset / page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch last_page = (offset + size) / page_size;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if ((offset % page_size) != 0)
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch first_page++;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch last_page / CHAR_BIT + 1);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch for (; first_page < last_page; first_page++) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch bits[first_page / CHAR_BIT] |=
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch 1 << (first_page % CHAR_BIT);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
e1a4ea6ad3e799ef8df7395e765c0ae9218e6c5dStephan Bosch}
e1a4ea6ad3e799ef8df7395e765c0ae9218e6c5dStephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch{
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch size_t page_size = mmap_get_page_size();
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch unsigned char *bits, mask;
e4b70fb422bf53ddf017948de26b5ea5a1a262fbStephan Bosch unsigned int i;
e4b70fb422bf53ddf017948de26b5ea5a1a262fbStephan Bosch
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch if (offset >= cache->read_highwater || size == 0)
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Bosch return;
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Bosch
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch if (size > cache->read_highwater - offset)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size = cache->read_highwater - offset;
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size = (offset + size + page_size-1) / page_size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch offset /= page_size;
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch i_assert(size > offset);
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch size -= offset;
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (size != 1) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch /* tell operating system that we don't need the memory anymore
833bed942977673526c72e79bccc09314fc57104Phil Carmody and it may free it. don't bother to do it for single pages,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch there's a good chance that they get re-read back
immediately. */
(void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size),
size * page_size, MADV_DONTNEED);
}
bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT,
1 + (size + CHAR_BIT - 1) / CHAR_BIT);
/* set the first byte */
for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) {
mask |= 1 << i;
size--;
}
*bits++ &= ~mask;
/* set the middle bytes */
memset(bits, 0, size / CHAR_BIT);
bits += size / CHAR_BIT;
size %= CHAR_BIT;
/* set the last byte */
if (size > 0) {
mask = 0;
for (i = 0, mask = 0; i < size; i++)
mask |= 1 << i;
*bits &= ~mask;
}
}