file-cache.c revision 8da8fa22f337c7e9f9ec6183e22f351811478387
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2004 Timo Sirainen */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#include "buffer.h"
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen#include "mmap-util.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "file-cache.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <sys/stat.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstruct file_cache {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen int fd;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen buffer_t *page_bitmask;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen void *mmap_base;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen size_t mmap_length;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen size_t read_highwater;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen};
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstruct file_cache *file_cache_new(int fd)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen{
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen struct file_cache *cache;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen cache = i_new(struct file_cache, 1);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cache->fd = fd;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return cache;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid file_cache_free(struct file_cache **_cache)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen{
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen struct file_cache *cache = *_cache;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen *_cache = NULL;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (cache->mmap_base != NULL) {
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen i_error("munmap_anon() failed: %m");
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen }
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen buffer_free(cache->page_bitmask);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_free(cache);
2c25e1360d4b5cc55eda969a3a7204d950de5a8fTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cache->fd = fd;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainenstatic int file_cache_set_size(struct file_cache *cache, uoff_t size)
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size_t page_size = mmap_get_page_size();
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen uoff_t diff = size % page_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen void *new_base;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen if (diff != 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size += page_size - diff;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert((size % page_size) == 0);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (size <= cache->mmap_length)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return 0;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (size > (size_t)-1) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_error("file_cache_set_size(%"PRIuUOFF_T"): size too large",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* grow mmaping */
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (cache->mmap_base == NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cache->mmap_base = mmap_anon(size);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (cache->mmap_base == MAP_FAILED) {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen i_error("mmap_anon(%"PRIuUOFF_T") failed: %m", size);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen cache->mmap_length = 0;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen return -1;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen }
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen } else {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen size, MREMAP_MAYMOVE);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (new_base == MAP_FAILED) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen i_error("mremap_anon(%"PRIuUOFF_T") failed: %m", size);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return -1;
af81f402ddc897c74c1e85abd02879612ce44882Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cache->mmap_base = new_base;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen cache->mmap_length = size;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return 0;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen size_t page_size = mmap_get_page_size();
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen size_t poffset, psize, dest_offset, dest_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned char *bits, *dest;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ssize_t ret;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (size > SSIZE_T_MAX) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* make sure our calculations won't overflow. most likely
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen we'll be reading less data, but allow it anyway so caller
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen doesn't have to deal with any extra checks. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size = SSIZE_T_MAX;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (offset >= (uoff_t)-1 - size)
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen size = (uoff_t)-1 - offset;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (offset + size > cache->mmap_length &&
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen offset + size - cache->mmap_length > 1024*1024) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* growing more than a megabyte, make sure that the
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen file is large enough so we don't allocate memory
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen more than needed */
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen struct stat st;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen if (fstat(cache->fd, &st) < 0) {
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen if (errno != ESTALE)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_error("fstat(file_cache) failed: %m");
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen return -1;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen }
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (offset + size > (uoff_t)st.st_size) {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen if (offset >= (uoff_t)st.st_size)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen return 0;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen size = (uoff_t)st.st_size - offset;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen }
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen }
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen if (file_cache_set_size(cache, offset + size) < 0)
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen return -1;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen poffset = offset / page_size;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen i_assert(psize > 0);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen (poffset + psize + CHAR_BIT - 1) /
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen CHAR_BIT);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen dest_offset = poffset * page_size;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen dest_size = page_size;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen while (psize > 0) {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen if (bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen /* page is already in cache */
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen dest_offset += page_size;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen if (dest_offset <= cache->read_highwater) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen psize--; poffset++;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen dest += page_size;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen continue;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* this is the last partially cached block.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen use the caching only if we don't want to
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen read past read_highwater */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (offset + size <= cache->read_highwater) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(psize == 1);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen break;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen /* mark the block noncached again and
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen read it */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen bits[poffset / CHAR_BIT] &=
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen ~(1 << (poffset % CHAR_BIT));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen dest_offset -= page_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (ret <= 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (ret < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* EOF. mark the last block as cached even if it
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen isn't completely. read_highwater tells us how far
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen we've actually made. */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (dest_offset == cache->read_highwater) {
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen i_assert(poffset ==
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen cache->read_highwater / page_size);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen bits[poffset / CHAR_BIT] |=
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen 1 << (poffset % CHAR_BIT);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return dest_offset <= offset ? 0 :
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen dest_offset - offset < size ?
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen dest_offset - offset : size;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen dest += ret;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen dest_offset += ret;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (cache->read_highwater < dest_offset) {
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen unsigned int high_poffset =
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen cache->read_highwater / page_size;
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* read_highwater needs to be updated. if we didn't
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen just read that block, we can't trust anymore that
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen we have it cached */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen bits[high_poffset / CHAR_BIT] &=
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen ~(1 << (high_poffset % CHAR_BIT));
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen cache->read_highwater = dest_offset;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if ((size_t)ret != dest_size) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* partial read - probably EOF but make sure. */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen dest_size -= ret;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen continue;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen dest_size = page_size;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen psize--; poffset++;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return size;
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
e11a64ffc7f08b4cb05bcc27668d154d33d0c2e0Timo Sirainen{
e11a64ffc7f08b4cb05bcc27668d154d33d0c2e0Timo Sirainen *size_r = cache->read_highwater;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return cache->mmap_base;
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi}
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi uoff_t offset)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi size_t page_size = mmap_get_page_size();
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi unsigned char *bits;
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi unsigned int first_page, last_page;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(size < (uoff_t)-1 && offset < (uoff_t)-1 - size);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (file_cache_set_size(cache, offset + size) < 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* couldn't grow mapping. just make sure the written memory
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen area is invalidated then. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen file_cache_invalidate(cache, offset, size);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (cache->read_highwater < offset + size) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen unsigned int page = cache->read_highwater / page_size;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen page / CHAR_BIT, 1);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen *bits &= ~(1 << (page % CHAR_BIT));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cache->read_highwater = offset + size;
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen }
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen /* mark fully written pages cached */
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen if (size >= page_size) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen first_page = offset / page_size;
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen last_page = (offset + size) / page_size;
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen if ((offset % page_size) != 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen first_page++;
bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
last_page / CHAR_BIT + 1);
for (; first_page < last_page; first_page++) {
bits[first_page / CHAR_BIT] |=
1 << (first_page % CHAR_BIT);
}
}
}
void file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
{
size_t page_size = mmap_get_page_size();
unsigned char *bits, mask;
unsigned int i;
if (offset >= cache->read_highwater || size == 0)
return;
if (size > cache->read_highwater - offset)
size = cache->read_highwater - offset;
size = (offset + size + page_size-1) / page_size;
offset /= page_size;
i_assert(size > offset);
size -= offset;
if (size != 1) {
/* tell operating system that we don't need the memory anymore
and it may free it. don't bother to do it for single pages,
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;
}
}