From: Darrick J. Wong <djwong@xxxxxxxxxx> Make it so that we can reconfigure the unix IO manager cache size. fuse2fs might want more than 32 blocks. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- lib/ext2fs/unix_io.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 7 deletions(-) diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c index 207a8e63b77fd4..f8be1fe6f8d2c0 100644 --- a/lib/ext2fs/unix_io.c +++ b/lib/ext2fs/unix_io.c @@ -91,7 +91,7 @@ struct unix_cache { unsigned write_err:1; }; -#define CACHE_SIZE 8 +#define DEFAULT_CACHE_SIZE 8 #define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ #define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ @@ -102,7 +102,8 @@ struct unix_private_data { int align; int access_time; ext2_loff_t offset; - struct unix_cache cache[CACHE_SIZE]; + struct unix_cache *cache; + unsigned int cache_size; void *bounce; struct struct_io_stats io_stats; #ifdef HAVE_PTHREAD @@ -476,7 +477,7 @@ static errcode_t alloc_cache(io_channel channel, int i; data->access_time = 0; - for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + for (i=0, cache = data->cache; i < data->cache_size; i++, cache++) { cache->block = 0; cache->access_time = 0; cache->dirty = 0; @@ -502,7 +503,7 @@ static void free_cache(struct unix_private_data *data) int i; data->access_time = 0; - for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + for (i=0, cache = data->cache; i < data->cache_size; i++, cache++) { cache->block = 0; cache->access_time = 0; cache->dirty = 0; @@ -528,7 +529,7 @@ static struct unix_cache *find_cached_block(struct unix_private_data *data, int i; unused_cache = oldest_cache = 0; - for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + for (i=0, cache = data->cache; i < data->cache_size; i++, cache++) { if (!cache->in_use) { if (!unused_cache) unused_cache = cache; @@ -592,7 +593,7 @@ static errcode_t flush_cached_blocks(io_channel channel, if ((flags & FLUSH_NOLOCK) == 0) mutex_lock(data, CACHE_MTX); - for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + for (i=0, cache = data->cache; i < data->cache_size; i++, cache++) { if (!cache->in_use || !cache->dirty) continue; retval = raw_write_blk(channel, data, @@ -616,7 +617,7 @@ static errcode_t flush_cached_blocks(io_channel channel, if ((flags & FLUSH_NOLOCK) == 0) mutex_lock(data, CACHE_MTX); errors_found = 0; - for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + for (i=0, cache = data->cache; i < data->cache_size; i++, cache++) { if (!cache->in_use || !cache->write_err) continue; errors_found = 1; @@ -648,6 +649,89 @@ static errcode_t flush_cached_blocks(io_channel channel, } return retval2; } + +/* Shrink the cache buffers */ +static errcode_t shrink_cache(io_channel channel, + struct unix_private_data *data, + unsigned int new_size) +{ + struct unix_cache *cache, *new_cache; + int i; + errcode_t retval; + + mutex_lock(data, CACHE_MTX); + + retval = flush_cached_blocks(channel, data, + FLUSH_INVALIDATE | FLUSH_NOLOCK); + if (retval) + goto unlock; + + for (i = new_size, cache = data->cache + new_size; + i < data->cache_size; + i++, cache++) { + cache->block = 0; + cache->access_time = 0; + cache->dirty = 0; + cache->in_use = 0; + if (cache->buf) + ext2fs_free_mem(&cache->buf); + } + + new_cache = realloc(data->cache, new_size * sizeof(struct unix_cache)); + if (!new_cache) { + retval = EXT2_ET_NO_MEMORY; + goto unlock; + } + + data->cache = new_cache; + data->cache_size = new_size; + +unlock: + mutex_unlock(data, CACHE_MTX); + return retval; +} + +/* Grow the cache buffers */ +static errcode_t grow_cache(io_channel channel, + struct unix_private_data *data, + unsigned int new_size) +{ + struct unix_cache *cache, *new_cache; + int i; + errcode_t retval; + + mutex_lock(data, CACHE_MTX); + + retval = flush_cached_blocks(channel, data, + FLUSH_INVALIDATE | FLUSH_NOLOCK); + if (retval) + goto unlock; + + new_cache = realloc(data->cache, new_size * sizeof(struct unix_cache)); + if (!new_cache) { + retval = EXT2_ET_NO_MEMORY; + goto unlock; + } + + for (i = data->cache_size, cache = new_cache + data->cache_size; + i < new_size; + i++, cache++) { + cache->block = 0; + cache->access_time = 0; + cache->dirty = 0; + cache->in_use = 0; + retval = io_channel_alloc_buf(channel, 0, &cache->buf); + if (retval) + goto unlock; + } + + data->cache = new_cache; + data->cache_size = new_size; + +unlock: + mutex_unlock(data, CACHE_MTX); + return retval; +} #endif /* NO_IO_CACHE */ #ifdef __linux__ @@ -743,6 +827,13 @@ static errcode_t unix_open_channel(const char *name, int fd, data->flags = flags; data->dev = fd; + data->cache_size = DEFAULT_CACHE_SIZE; + data->cache = calloc(DEFAULT_CACHE_SIZE, sizeof(struct unix_cache)); + if (!data->cache) { + retval = EXT2_ET_NO_MEMORY; + goto cleanup; + } + #if defined(O_DIRECT) if (flags & IO_FLAG_DIRECT_IO) io->align = ext2fs_get_dio_alignment(data->dev); @@ -869,6 +960,8 @@ static errcode_t unix_open_channel(const char *name, int fd, if (data->dev >= 0) close(data->dev); free_cache(data); + if (data->cache) + free(data->cache); ext2fs_free_mem(&data); } if (io) { @@ -953,6 +1046,7 @@ static errcode_t unix_close(io_channel channel) if (close(data->dev) < 0) retval = errno; free_cache(data); + free(data->cache); #ifdef HAVE_PTHREAD if (data->flags & IO_FLAG_THREADS) { pthread_mutex_destroy(&data->cache_mutex); @@ -1308,6 +1402,25 @@ static errcode_t unix_set_option(io_channel channel, const char *option, } return EXT2_ET_INVALID_ARGUMENT; } +#ifndef NO_IO_CACHE + if (!strcmp(option, "cache_blocks")) { + unsigned long long size; + + if (!arg) + return EXT2_ET_INVALID_ARGUMENT; + + errno = 0; + size = strtoll(arg, NULL, 0); + if (errno || size == 0 || size > INT32_MAX) + return EXT2_ET_INVALID_ARGUMENT; + + if (data->cache_size == size) + return 0; + if (data->cache_size > size) + return shrink_cache(channel, data, size); + return grow_cache(channel, data, size); + } +#endif return EXT2_ET_INVALID_ARGUMENT; }