file-cache.c revision 636468e8e0b241f2026a41675cb9e15dfe2d9641
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2004-2011 Dovecot authors, see the included COPYING file */
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen#include "lib.h"
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen#include "buffer.h"
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen#include "mmap-util.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "file-cache.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <sys/stat.h>
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainenstruct file_cache {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int fd;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen buffer_t *page_bitmask;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen void *mmap_base;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t mmap_length;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t read_highwater;
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen};
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen
1d4f710106fb498750456724628da6063e012e6dTimo Sirainenstruct file_cache *file_cache_new(int fd)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen struct file_cache *cache;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainen cache = i_new(struct file_cache, 1);
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen cache->fd = fd;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return cache;
336b825e0321b798690351d9899b1b0cb99ec462Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid file_cache_free(struct file_cache **_cache)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_cache *cache = *_cache;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen *_cache = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (cache->mmap_base != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("munmap_anon() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buffer_free(&cache->page_bitmask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_free(cache);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->fd = fd;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenint file_cache_set_size(struct file_cache *cache, uoff_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t page_size = mmap_get_page_size();
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t diff = size % page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen void *new_base;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen if (diff != 0)
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen size += page_size - diff;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen i_assert((size % page_size) == 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size <= cache->mmap_length)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > (size_t)-1) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("file_cache_set_size(%"PRIuUOFF_T"): size too large",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* grow mmaping */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (cache->mmap_base == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->mmap_base = mmap_anon(size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (cache->mmap_base == MAP_FAILED) {
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen i_error("mmap_anon(%"PRIuUOFF_T") failed: %m", size);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen cache->mmap_base = NULL;
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen cache->mmap_length = 0;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size, MREMAP_MAYMOVE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (new_base == MAP_FAILED) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("mremap_anon(%"PRIuUOFF_T") failed: %m", size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->mmap_base = new_base;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->mmap_length = size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t page_size = mmap_get_page_size();
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen size_t poffset, psize, dest_offset, dest_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned char *bits, *dest;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > SSIZE_T_MAX) {
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. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = SSIZE_T_MAX;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (offset >= (uoff_t)-1 - size)
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen size = (uoff_t)-1 - offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen if (offset + size > cache->mmap_length &&
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 */
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen struct stat st;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstat(cache->fd, &st) < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (errno != ESTALE)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("fstat(file_cache) failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (offset + size > (uoff_t)st.st_size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (offset >= (uoff_t)st.st_size)
c7fca6cbb32388556d9f6d8313486cc4e4a3c058Timo Sirainen return 0;
c7fca6cbb32388556d9f6d8313486cc4e4a3c058Timo Sirainen size = (uoff_t)st.st_size - offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
c7fca6cbb32388556d9f6d8313486cc4e4a3c058Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (file_cache_set_size(cache, offset + size) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen poffset = offset / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(psize > 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (poffset + psize + CHAR_BIT - 1) /
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen CHAR_BIT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest_offset = poffset * page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest_size = page_size;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
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 dest_offset += page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (dest_offset <= cache->read_highwater) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen psize--; poffset++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest += page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen continue;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
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 if (offset + size <= cache->read_highwater) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(psize == 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* mark the block noncached again and
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen read it */
05e55893a799de645fc8cd2203d6013f0e0f1b79Timo Sirainen bits[poffset / CHAR_BIT] &=
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen ~(1 << (poffset % CHAR_BIT));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest_offset -= page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret <= 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
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. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (dest_offset == cache->read_highwater) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(poffset ==
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->read_highwater / page_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits[poffset / CHAR_BIT] |=
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen 1 << (poffset % CHAR_BIT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return dest_offset <= offset ? 0 :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest_offset - offset < size ?
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest_offset - offset : size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest += ret;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen dest_offset += ret;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (cache->read_highwater < dest_offset) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int high_poffset =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->read_highwater / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
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 */
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen bits[high_poffset / CHAR_BIT] &=
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen ~(1 << (high_poffset % CHAR_BIT));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->read_highwater = dest_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((size_t)ret != dest_size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* partial read - probably EOF but make sure. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dest_size -= ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen continue;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen dest_size = page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen psize--; poffset++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen{
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen *size_r = cache->read_highwater;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen return cache->mmap_base;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen uoff_t offset)
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen{
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen size_t page_size = mmap_get_page_size();
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen unsigned char *bits;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int first_page, last_page;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen i_assert((uoff_t)-1 - offset > size);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
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 file_cache_invalidate(cache, offset, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (cache->read_highwater < offset + size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int page = cache->read_highwater / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen page / CHAR_BIT, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *bits &= ~(1 << (page % CHAR_BIT));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cache->read_highwater = offset + size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* mark fully written pages cached */
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen if (size >= page_size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen first_page = offset / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen last_page = (offset + size) / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((offset % page_size) != 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen first_page++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen last_page / CHAR_BIT + 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (; first_page < last_page; first_page++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits[first_page / CHAR_BIT] |=
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen 1 << (first_page % CHAR_BIT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t page_size = mmap_get_page_size();
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned char *bits, mask;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (offset >= cache->read_highwater || size == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > cache->read_highwater - offset) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* ignore anything after read highwater */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = cache->read_highwater - offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size >= cache->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 }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = (offset + size + page_size-1) / page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen offset /= page_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(size > offset);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size -= offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size != 1) {
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 size * page_size, MADV_DONTNEED);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen 1 + (size + CHAR_BIT - 1) / CHAR_BIT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the first byte */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mask |= 1 << i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size--;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *bits++ &= ~mask;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the middle bytes */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memset(bits, 0, size / CHAR_BIT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bits += size / CHAR_BIT;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size %= CHAR_BIT;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the last byte */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0, mask = 0; i < size; i++)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mask |= 1 << i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *bits &= ~mask;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen