file-cache.c revision 8da8fa22f337c7e9f9ec6183e22f351811478387
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2004 Timo Sirainen */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid file_cache_free(struct file_cache **_cache)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainenstatic int file_cache_set_size(struct file_cache *cache, uoff_t size)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_error("file_cache_set_size(%"PRIuUOFF_T"): size too large",
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* grow mmaping */
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen i_error("mmap_anon(%"PRIuUOFF_T") failed: %m", size);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen i_error("mremap_anon(%"PRIuUOFF_T") failed: %m", size);
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen size_t poffset, psize, dest_offset, dest_size;
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. */
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 */
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen if (file_cache_set_size(cache, offset + size) < 0)
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen while (psize > 0) {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen if (bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) {
d74c9f4bf7ee37f3f58a895032ea013d07294865Timo Sirainen /* page is already in cache */
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 */
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen /* mark the block noncached again and
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
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 /* 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 */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* partial read - probably EOF but make sure. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
90126be6a0a7a44f67bdf6bb983b29f6d8e3685aAki Tuomi unsigned char *bits;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(size < (uoff_t)-1 && offset < (uoff_t)-1 - size);
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. */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen unsigned int page = cache->read_highwater / page_size;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen /* mark fully written pages cached */
size--;
if (size > 0) {
mask = 0;