file-cache.c revision 636468e8e0b241f2026a41675cb9e15dfe2d9641
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2004-2011 Dovecot authors, see the included COPYING file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid file_cache_free(struct file_cache **_cache)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenint file_cache_set_size(struct file_cache *cache, uoff_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("file_cache_set_size(%"PRIuUOFF_T"): size too large",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* grow mmaping */
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen i_error("mmap_anon(%"PRIuUOFF_T") failed: %m", size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("mremap_anon(%"PRIuUOFF_T") failed: %m", size);
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen size_t poffset, psize, dest_offset, dest_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* make sure our calculations won't overflow. most likely
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen we'll be reading less data, but allow it anyway so caller
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen doesn't have to deal with any extra checks. */
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen offset + size - cache->mmap_length > 1024*1024) {
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen /* growing more than a megabyte, make sure that the
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen file is large enough so we don't allocate memory
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen more than needed */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (file_cache_set_size(cache, offset + size) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen while (psize > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* page is already in cache */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* this is the last partially cached block.
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen use the caching only if we don't want to
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen read past read_highwater */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* mark the block noncached again and
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* EOF. mark the last block as cached even if it
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen isn't completely. read_highwater tells us how far
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen we've actually made. */
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen /* read_highwater needs to be updated. if we didn't
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen just read that block, we can't trust anymore that
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen we have it cached */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* partial read - probably EOF but make sure. */
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen unsigned char *bits;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen if (file_cache_set_size(cache, offset + size) < 0) {
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen /* couldn't grow mapping. just make sure the written memory
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen area is invalidated then. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int page = cache->read_highwater / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* mark fully written pages cached */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (; first_page < last_page; first_page++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (offset >= cache->read_highwater || size == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* ignore anything after read highwater */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we're invalidating everything up to read highwater.
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen drop the highwater position. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->read_highwater = offset & ~(page_size-1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = (offset + size + page_size-1) / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* tell operating system that we don't need the memory anymore
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen and it may free it. don't bother to do it for single pages,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen there's a good chance that they get re-read back
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen immediately. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the first byte */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the middle bytes */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the last byte */