bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "lib.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "buffer.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "mmap-util.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "file-cache.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include <sys/stat.h>
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenstruct file_cache {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen int fd;
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen char *path;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen buffer_t *page_bitmask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen void *mmap_base;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t mmap_length;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t read_highwater;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen};
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenstruct file_cache *file_cache_new(int fd)
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen{
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen return file_cache_new_path(fd, "");
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen}
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainenstruct file_cache *file_cache_new_path(int fd, const char *path)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct file_cache *cache;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache = i_new(struct file_cache, 1);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->fd = fd;
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen cache->path = i_strdup(path);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return cache;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid file_cache_free(struct file_cache **_cache)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct file_cache *cache = *_cache;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen *_cache = NULL;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (cache->mmap_base != NULL) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("munmap_anon(%s) failed: %m", cache->path);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen buffer_free(&cache->page_bitmask);
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_free(cache->path);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_free(cache);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->fd = fd;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
f7e0daedc6838c45df6b2a0724a57662b92e7df4Timo Sirainenint file_cache_set_size(struct file_cache *cache, uoff_t size)
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen{
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen size_t page_size = mmap_get_page_size();
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen uoff_t diff;
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen void *new_base;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen i_assert(page_size > 0);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen diff = size % page_size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (diff != 0)
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen size += page_size - diff;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen i_assert((size % page_size) == 0);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (size <= cache->mmap_length)
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen return 0;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (size > (size_t)-1) {
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("file_cache_set_size(%s, %"PRIuUOFF_T"): size too large",
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen cache->path, size);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen return -1;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* grow mmaping */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (cache->mmap_base == NULL) {
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen cache->mmap_base = mmap_anon(size);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (cache->mmap_base == MAP_FAILED) {
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("mmap_anon(%s, %"PRIuUOFF_T") failed: %m",
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen cache->path, size);
636468e8e0b241f2026a41675cb9e15dfe2d9641Timo Sirainen cache->mmap_base = NULL;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen cache->mmap_length = 0;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen return -1;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen } else {
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen new_base = mremap_anon(cache->mmap_base, cache->mmap_length,
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen size, MREMAP_MAYMOVE);
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen if (new_base == MAP_FAILED) {
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("mremap_anon(%s, %"PRIuUOFF_T") failed: %m",
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen cache->path, size);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen return -1;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen
8da8fa22f337c7e9f9ec6183e22f351811478387Timo Sirainen cache->mmap_base = new_base;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen cache->mmap_length = size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen return 0;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen}
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t page_size = mmap_get_page_size();
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen size_t poffset, psize, dest_offset, dest_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned char *bits, *dest;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen ssize_t ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen i_assert(page_size > 0);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen if (size > SSIZE_T_MAX) {
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen /* make sure our calculations won't overflow. most likely
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen we'll be reading less data, but allow it anyway so caller
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen doesn't have to deal with any extra checks. */
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen size = SSIZE_T_MAX;
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen }
34861542d8975d85bc87a2e21e40ada22545b672Timo Sirainen if (offset >= (uoff_t)-1 - size)
34861542d8975d85bc87a2e21e40ada22545b672Timo Sirainen size = (uoff_t)-1 - offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset + size > cache->mmap_length &&
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen offset + size - cache->mmap_length > 1024*1024) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* growing more than a megabyte, make sure that the
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file is large enough so we don't allocate memory
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen more than needed */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct stat st;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen if (fstat(cache->fd, &st) < 0) {
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen if (errno != ESTALE)
c18dcef4da347b735861aa35fc6518c6c2e97755Timo Sirainen i_error("fstat(%s) failed: %m", cache->path);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return -1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset + size > (uoff_t)st.st_size) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset >= (uoff_t)st.st_size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return 0;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = (uoff_t)st.st_size - offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (file_cache_set_size(cache, offset + size) < 0)
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen return -1;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen poffset = offset / page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_assert(psize > 0);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
77717f1e9e8442fd2cdbca87cbc45c03d0b5fcaeTimo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
9dd7e489b8f0049ca9111393f08f73bac3591b89Timo Sirainen (poffset + psize + CHAR_BIT - 1) /
9dd7e489b8f0049ca9111393f08f73bac3591b89Timo Sirainen CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset = poffset * page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_size = page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen while (psize > 0) {
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if ((bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) != 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* page is already in cache */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset += page_size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (dest_offset <= cache->read_highwater) {
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen psize--; poffset++;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen dest += page_size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen continue;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* this is the last partially cached block.
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen use the caching only if we don't want to
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen read past read_highwater */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (offset + size <= cache->read_highwater) {
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen i_assert(psize == 1);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen break;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* mark the block noncached again and
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen read it */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen bits[poffset / CHAR_BIT] &=
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen ~(1 << (poffset % CHAR_BIT));
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen dest_offset -= page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (ret <= 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (ret < 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return -1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* EOF. mark the last block as cached even if it
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen isn't completely. read_highwater tells us how far
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen we've actually made. */
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen if (dest_offset == cache->read_highwater) {
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen i_assert(poffset ==
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen cache->read_highwater / page_size);
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen bits[poffset / CHAR_BIT] |=
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen 1 << (poffset % CHAR_BIT);
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return dest_offset <= offset ? 0 :
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset - offset < size ?
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset - offset : size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest += ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset += ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen if (cache->read_highwater < dest_offset) {
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen unsigned int high_poffset =
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen cache->read_highwater / page_size;
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen /* read_highwater needs to be updated. if we didn't
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen just read that block, we can't trust anymore that
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen we have it cached */
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen bits[high_poffset / CHAR_BIT] &=
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen ~(1 << (high_poffset % CHAR_BIT));
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->read_highwater = dest_offset;
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if ((size_t)ret != dest_size) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* partial read - probably EOF but make sure. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_size -= ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen continue;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_size = page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen psize--; poffset++;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen *size_r = cache->read_highwater;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return cache->mmap_base;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen uoff_t offset)
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen{
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen size_t page_size = mmap_get_page_size();
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen unsigned char *bits;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen unsigned int first_page, last_page;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen i_assert(page_size > 0);
ef0fe74b5d663fe9966b98f39d2eae5ec2e5ac6cTimo Sirainen i_assert((uoff_t)-1 - offset > size);
34861542d8975d85bc87a2e21e40ada22545b672Timo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (file_cache_set_size(cache, offset + size) < 0) {
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen /* couldn't grow mapping. just make sure the written memory
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen area is invalidated then. */
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen file_cache_invalidate(cache, offset, size);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen return;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
4498761e9ee18c96a19ce3029258a6390efeb092Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen if (cache->read_highwater < offset + size) {
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen unsigned int page = cache->read_highwater / page_size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask,
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen page / CHAR_BIT, 1);
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen *bits &= ~(1 << (page % CHAR_BIT));
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen cache->read_highwater = offset + size;
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen }
b6ff64ea9a8615271ffdfde1aad2da2fd1b0ba1fTimo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen /* mark fully written pages cached */
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen if (size >= page_size) {
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen first_page = offset / page_size;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen last_page = (offset + size) / page_size;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen if ((offset % page_size) != 0)
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen first_page++;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen last_page / CHAR_BIT + 1);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen for (; first_page < last_page; first_page++) {
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen bits[first_page / CHAR_BIT] |=
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen 1 << (first_page % CHAR_BIT);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen }
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen }
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen}
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
74eff344f67ca582eec8c38dfdc6f5f6faa21fd8Timo Sirainenvoid file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t page_size = mmap_get_page_size();
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned char *bits, mask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned int i;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen if (offset >= cache->read_highwater || size == 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen i_assert(page_size > 0);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen if (size > cache->read_highwater - offset) {
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen /* ignore anything after read highwater */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = cache->read_highwater - offset;
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen }
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen if (size >= cache->read_highwater) {
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen /* we're invalidating everything up to read highwater.
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen drop the highwater position. */
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen cache->read_highwater = offset & ~(page_size-1);
83543e092d7b874fb856effe2c5b556dbcc6194cTimo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = (offset + size + page_size-1) / page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen offset /= page_size;
091e808a5af4dbc40aacde8dd1fbf98fae52e6c1Timo Sirainen i_assert(size > offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size -= offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen if (size != 1) {
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen /* tell operating system that we don't need the memory anymore
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen and it may free it. don't bother to do it for single pages,
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen there's a good chance that they get re-read back
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen immediately. */
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen (void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size),
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen size * page_size, MADV_DONTNEED);
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen }
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT,
67c4b39511dd04caf45ade2100989e6052f02fa9Timo Sirainen 1 + (size + CHAR_BIT - 1) / CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the first byte */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mask |= 1 << i;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size--;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen *bits++ &= ~mask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the middle bytes */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen memset(bits, 0, size / CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits += size / CHAR_BIT;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size %= CHAR_BIT;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the last byte */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (size > 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen for (i = 0, mask = 0; i < size; i++)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mask |= 1 << i;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen *bits &= ~mask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}