libext2fs: zero blocks via FALLOC_FL_ZERO_RANGE in ext2fs_zero_blocks

Plumb a new call into the IO manager to support translating
ext2fs_zero_blocks calls into the equivalent FALLOC_FL_ZERO_RANGE
fallocate flag primitive when possible.  This patch provides _only_
support for file-based images.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
debian
Darrick J. Wong 2015-03-28 23:01:08 -04:00 committed by Theodore Ts'o
parent 41f2210131
commit 3d28f54589
5 changed files with 110 additions and 1 deletions

View File

@ -93,7 +93,9 @@ struct struct_io_manager {
errcode_t (*cache_readahead)(io_channel channel,
unsigned long long block,
unsigned long long count);
long reserved[15];
errcode_t (*zeroout)(io_channel channel, unsigned long long block,
unsigned long long count);
long reserved[14];
};
#define IO_FLAG_RW 0x0001
@ -125,6 +127,9 @@ extern errcode_t io_channel_write_blk64(io_channel channel,
extern errcode_t io_channel_discard(io_channel channel,
unsigned long long block,
unsigned long long count);
extern errcode_t io_channel_zeroout(io_channel channel,
unsigned long long block,
unsigned long long count);
extern errcode_t io_channel_alloc_buf(io_channel channel,
int count, void *ptr);
extern errcode_t io_channel_cache_readahead(io_channel io,

View File

@ -112,6 +112,17 @@ errcode_t io_channel_discard(io_channel channel, unsigned long long block,
return EXT2_ET_UNIMPLEMENTED;
}
errcode_t io_channel_zeroout(io_channel channel, unsigned long long block,
unsigned long long count)
{
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
if (channel->manager->zeroout)
return (channel->manager->zeroout)(channel, block, count);
return EXT2_ET_UNIMPLEMENTED;
}
errcode_t io_channel_alloc_buf(io_channel io, int count, void *ptr)
{
size_t size;

View File

@ -170,6 +170,11 @@ errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num,
if (num <= 0)
return 0;
/* Try a zero out command, if supported */
retval = io_channel_zeroout(fs->io, blk, num);
if (retval == 0)
return 0;
/* Allocate the zeroizing buffer if necessary */
if (num > stride_length && stride_length < MAX_STRIDE_LENGTH) {
void *p;

View File

@ -86,6 +86,7 @@ void (*test_io_cb_write_byte)
#define TEST_FLAG_SET_OPTION 0x20
#define TEST_FLAG_DISCARD 0x40
#define TEST_FLAG_READAHEAD 0x80
#define TEST_FLAG_ZEROOUT 0x100
static void test_dump_block(io_channel channel,
struct test_private_data *data,
@ -507,6 +508,25 @@ static errcode_t test_cache_readahead(io_channel channel,
return retval;
}
static errcode_t test_zeroout(io_channel channel, unsigned long long block,
unsigned long long count)
{
struct test_private_data *data;
errcode_t retval = 0;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct test_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
if (data->real)
retval = io_channel_zeroout(data->real, block, count);
if (data->flags & TEST_FLAG_ZEROOUT)
fprintf(data->outfile,
"Test_io: zeroout(%llu, %llu) returned %s\n",
block, count, retval ? error_message(retval) : "OK");
return retval;
}
static struct struct_io_manager struct_test_manager = {
.magic = EXT2_ET_MAGIC_IO_MANAGER,
.name = "Test I/O Manager",
@ -523,6 +543,7 @@ static struct struct_io_manager struct_test_manager = {
.write_blk64 = test_write_blk64,
.discard = test_discard,
.cache_readahead = test_cache_readahead,
.zeroout = test_zeroout,
};
io_manager test_io_manager = &struct_test_manager;

View File

@ -987,6 +987,72 @@ unimplemented:
return EXT2_ET_UNIMPLEMENTED;
}
static errcode_t unix_zeroout(io_channel channel, unsigned long long block,
unsigned long long count)
{
struct unix_private_data *data;
int ret;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct unix_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
if (getenv("UNIX_IO_NOZEROOUT"))
goto unimplemented;
if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
/* Not implemented until the BLKZEROOUT mess is fixed */
goto unimplemented;
} else {
/* Regular file, try to use truncate/punch/zero. */
#if defined(HAVE_FALLOCATE) && (defined(FALLOC_FL_ZERO_RANGE) || \
(defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)))
struct stat statbuf;
if (count == 0)
return 0;
/*
* If we're trying to zero a range past the end of the file,
* extend the file size, then punch (or zero_range) everything.
*/
ret = fstat(data->dev, &statbuf);
if (ret)
goto err;
if (statbuf.st_size < (block + count) * channel->block_size) {
ret = ftruncate(data->dev,
(block + count) * channel->block_size);
if (ret)
goto err;
}
#if defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
ret = fallocate(data->dev,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
(off_t)(block) * channel->block_size,
(off_t)(count) * channel->block_size);
if (ret == 0)
goto err;
#endif
#ifdef FALLOC_FL_ZERO_RANGE
ret = fallocate(data->dev,
FALLOC_FL_ZERO_RANGE,
(off_t)(block) * channel->block_size,
(off_t)(count) * channel->block_size);
#endif
#else
goto unimplemented;
#endif /* HAVE_FALLOCATE && (ZERO_RANGE || (PUNCH_HOLE && KEEP_SIZE)) */
}
err:
if (ret < 0) {
if (errno == EOPNOTSUPP)
goto unimplemented;
return errno;
}
return 0;
unimplemented:
return EXT2_ET_UNIMPLEMENTED;
}
static struct struct_io_manager struct_unix_manager = {
.magic = EXT2_ET_MAGIC_IO_MANAGER,
.name = "Unix I/O Manager",
@ -1003,6 +1069,7 @@ static struct struct_io_manager struct_unix_manager = {
.write_blk64 = unix_write_blk64,
.discard = unix_discard,
.cache_readahead = unix_cache_readahead,
.zeroout = unix_zeroout,
};
io_manager unix_io_manager = &struct_unix_manager;