block: introduce preallocate filter
It's intended to be inserted between format and protocol nodes to preallocate additional space (expanding protocol file) on writes crossing EOF. It improves performance for file-systems with slow allocation. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Message-Id: <20201021145859.11201-9-vsementsov@virtuozzo.com> Reviewed-by: Max Reitz <mreitz@redhat.com> [mreitz: Two comment fixes, and bumped the version from 5.2 to 6.0] Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
9530a25b8b
commit
33fa2222eb
@ -12,6 +12,7 @@ block_ss.add(files(
|
||||
'block-copy.c',
|
||||
'commit.c',
|
||||
'copy-on-read.c',
|
||||
'preallocate.c',
|
||||
'create.c',
|
||||
'crypto.c',
|
||||
'dirty-bitmap.c',
|
||||
|
559
block/preallocate.c
Normal file
559
block/preallocate.c
Normal file
@ -0,0 +1,559 @@
|
||||
/*
|
||||
* preallocate filter driver
|
||||
*
|
||||
* The driver performs preallocate operation: it is injected above
|
||||
* some node, and before each write over EOF it does additional preallocating
|
||||
* write-zeroes request.
|
||||
*
|
||||
* Copyright (c) 2020 Virtuozzo International GmbH.
|
||||
*
|
||||
* Author:
|
||||
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/units.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
|
||||
typedef struct PreallocateOpts {
|
||||
int64_t prealloc_size;
|
||||
int64_t prealloc_align;
|
||||
} PreallocateOpts;
|
||||
|
||||
typedef struct BDRVPreallocateState {
|
||||
PreallocateOpts opts;
|
||||
|
||||
/*
|
||||
* Track real data end, to crop preallocation on close. If < 0 the status is
|
||||
* unknown.
|
||||
*
|
||||
* @data_end is a maximum of file size on open (or when we get write/resize
|
||||
* permissions) and all write request ends after it. So it's safe to
|
||||
* truncate to data_end if it is valid.
|
||||
*/
|
||||
int64_t data_end;
|
||||
|
||||
/*
|
||||
* Start of trailing preallocated area which reads as zero. May be smaller
|
||||
* than data_end, if user does over-EOF write zero operation. If < 0 the
|
||||
* status is unknown.
|
||||
*
|
||||
* If both @zero_start and @file_end are valid, the region
|
||||
* [@zero_start, @file_end) is known to be preallocated zeroes. If @file_end
|
||||
* is not valid, @zero_start doesn't make much sense.
|
||||
*/
|
||||
int64_t zero_start;
|
||||
|
||||
/*
|
||||
* Real end of file. Actually the cache for bdrv_getlength(bs->file->bs),
|
||||
* to avoid extra lseek() calls on each write operation. If < 0 the status
|
||||
* is unknown.
|
||||
*/
|
||||
int64_t file_end;
|
||||
|
||||
/*
|
||||
* All three states @data_end, @zero_start and @file_end are guaranteed to
|
||||
* be invalid (< 0) when we don't have both exclusive BLK_PERM_RESIZE and
|
||||
* BLK_PERM_WRITE permissions on file child.
|
||||
*/
|
||||
} BDRVPreallocateState;
|
||||
|
||||
#define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align"
|
||||
#define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size"
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "preallocate",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = PREALLOCATE_OPT_PREALLOC_ALIGN,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "on preallocation, align file length to this number, "
|
||||
"default 1M",
|
||||
},
|
||||
{
|
||||
.name = PREALLOCATE_OPT_PREALLOC_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "how much to preallocate, default 128M",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static bool preallocate_absorb_opts(PreallocateOpts *dest, QDict *options,
|
||||
BlockDriverState *child_bs, Error **errp)
|
||||
{
|
||||
QemuOpts *opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
|
||||
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest->prealloc_align =
|
||||
qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_ALIGN, 1 * MiB);
|
||||
dest->prealloc_size =
|
||||
qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_SIZE, 128 * MiB);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
if (!QEMU_IS_ALIGNED(dest->prealloc_align, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "prealloc-align parameter of preallocate filter "
|
||||
"is not aligned to %llu", BDRV_SECTOR_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(dest->prealloc_align,
|
||||
child_bs->bl.request_alignment)) {
|
||||
error_setg(errp, "prealloc-align parameter of preallocate filter "
|
||||
"is not aligned to underlying node request alignment "
|
||||
"(%" PRIi32 ")", child_bs->bl.request_alignment);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int preallocate_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
|
||||
/*
|
||||
* s->data_end and friends should be initialized on permission update.
|
||||
* For this to work, mark them invalid.
|
||||
*/
|
||||
s->file_end = s->zero_start = s->data_end = -EINVAL;
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void preallocate_close(BlockDriverState *bs)
|
||||
{
|
||||
int ret;
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
|
||||
if (s->data_end < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->file_end < 0) {
|
||||
s->file_end = bdrv_getlength(bs->file->bs);
|
||||
if (s->file_end < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->data_end < s->file_end) {
|
||||
ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0,
|
||||
NULL);
|
||||
s->file_end = ret < 0 ? ret : s->data_end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle reopen.
|
||||
*
|
||||
* We must implement reopen handlers, otherwise reopen just don't work. Handle
|
||||
* new options and don't care about preallocation state, as it is handled in
|
||||
* set/check permission handlers.
|
||||
*/
|
||||
|
||||
static int preallocate_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
PreallocateOpts *opts = g_new0(PreallocateOpts, 1);
|
||||
|
||||
if (!preallocate_absorb_opts(opts, reopen_state->options,
|
||||
reopen_state->bs->file->bs, errp)) {
|
||||
g_free(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reopen_state->opaque = opts;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void preallocate_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
BDRVPreallocateState *s = state->bs->opaque;
|
||||
|
||||
s->opts = *(PreallocateOpts *)state->opaque;
|
||||
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
static void preallocate_reopen_abort(BDRVReopenState *state)
|
||||
{
|
||||
g_free(state->opaque);
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
static coroutine_fn int preallocate_co_preadv_part(
|
||||
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset, int flags)
|
||||
{
|
||||
return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||
flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn preallocate_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
}
|
||||
|
||||
static bool can_write_resize(uint64_t perm)
|
||||
{
|
||||
return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE);
|
||||
}
|
||||
|
||||
static bool has_prealloc_perms(BlockDriverState *bs)
|
||||
{
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
|
||||
if (can_write_resize(bs->file->perm)) {
|
||||
assert(!(bs->file->shared_perm & BLK_PERM_WRITE));
|
||||
assert(!(bs->file->shared_perm & BLK_PERM_RESIZE));
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(s->data_end < 0);
|
||||
assert(s->zero_start < 0);
|
||||
assert(s->file_end < 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call on each write. Returns true if @want_merge_zero is true and the region
|
||||
* [offset, offset + bytes) is zeroed (as a result of this call or earlier
|
||||
* preallocation).
|
||||
*
|
||||
* want_merge_zero is used to merge write-zero request with preallocation in
|
||||
* one bdrv_co_pwrite_zeroes() call.
|
||||
*/
|
||||
static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, bool want_merge_zero)
|
||||
{
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
int64_t end = offset + bytes;
|
||||
int64_t prealloc_start, prealloc_end;
|
||||
int ret;
|
||||
|
||||
if (!has_prealloc_perms(bs)) {
|
||||
/* We don't have state neither should try to recover it */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->data_end < 0) {
|
||||
s->data_end = bdrv_getlength(bs->file->bs);
|
||||
if (s->data_end < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->file_end < 0) {
|
||||
s->file_end = s->data_end;
|
||||
}
|
||||
}
|
||||
|
||||
if (end <= s->data_end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We have valid s->data_end, and request writes beyond it. */
|
||||
|
||||
s->data_end = end;
|
||||
if (s->zero_start < 0 || !want_merge_zero) {
|
||||
s->zero_start = end;
|
||||
}
|
||||
|
||||
if (s->file_end < 0) {
|
||||
s->file_end = bdrv_getlength(bs->file->bs);
|
||||
if (s->file_end < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now s->data_end, s->zero_start and s->file_end are valid. */
|
||||
|
||||
if (end <= s->file_end) {
|
||||
/* No preallocation needed. */
|
||||
return want_merge_zero && offset >= s->zero_start;
|
||||
}
|
||||
|
||||
/* Now we want new preallocation, as request writes beyond s->file_end. */
|
||||
|
||||
prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end;
|
||||
prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size,
|
||||
s->opts.prealloc_align);
|
||||
|
||||
ret = bdrv_co_pwrite_zeroes(
|
||||
bs->file, prealloc_start, prealloc_end - prealloc_start,
|
||||
BDRV_REQ_NO_FALLBACK | BDRV_REQ_SERIALISING | BDRV_REQ_NO_WAIT);
|
||||
if (ret < 0) {
|
||||
s->file_end = ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
s->file_end = prealloc_end;
|
||||
return want_merge_zero;
|
||||
}
|
||||
|
||||
static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
bool want_merge_zero =
|
||||
!(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK));
|
||||
if (handle_write(bs, offset, bytes, want_merge_zero)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int preallocate_co_pwritev_part(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
QEMUIOVector *qiov,
|
||||
size_t qiov_offset,
|
||||
int flags)
|
||||
{
|
||||
handle_write(bs, offset, bytes, false);
|
||||
|
||||
return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||
flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
preallocate_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
bool exact, PreallocMode prealloc,
|
||||
BdrvRequestFlags flags, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
if (s->data_end >= 0 && offset > s->data_end) {
|
||||
if (s->file_end < 0) {
|
||||
s->file_end = bdrv_getlength(bs->file->bs);
|
||||
if (s->file_end < 0) {
|
||||
error_setg(errp, "failed to get file length");
|
||||
return s->file_end;
|
||||
}
|
||||
}
|
||||
|
||||
if (prealloc == PREALLOC_MODE_FALLOC) {
|
||||
/*
|
||||
* If offset <= s->file_end, the task is already done, just
|
||||
* update s->data_end, to move part of "filter preallocation"
|
||||
* to "preallocation requested by user".
|
||||
* Otherwise just proceed to preallocate missing part.
|
||||
*/
|
||||
if (offset <= s->file_end) {
|
||||
s->data_end = offset;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We have to drop our preallocation, to
|
||||
* - avoid "Cannot use preallocation for shrinking files" in
|
||||
* case of offset < file_end
|
||||
* - give PREALLOC_MODE_OFF a chance to keep small disk
|
||||
* usage
|
||||
* - give PREALLOC_MODE_FULL a chance to actually write the
|
||||
* whole region as user expects
|
||||
*/
|
||||
if (s->file_end > s->data_end) {
|
||||
ret = bdrv_co_truncate(bs->file, s->data_end, true,
|
||||
PREALLOC_MODE_OFF, 0, errp);
|
||||
if (ret < 0) {
|
||||
s->file_end = ret;
|
||||
error_prepend(errp, "preallocate-filter: failed to drop "
|
||||
"write-zero preallocation: ");
|
||||
return ret;
|
||||
}
|
||||
s->file_end = s->data_end;
|
||||
}
|
||||
}
|
||||
|
||||
s->data_end = offset;
|
||||
}
|
||||
|
||||
ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
|
||||
if (ret < 0) {
|
||||
s->file_end = s->zero_start = s->data_end = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (has_prealloc_perms(bs)) {
|
||||
s->file_end = s->zero_start = s->data_end = offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn preallocate_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_co_flush(bs->file->bs);
|
||||
}
|
||||
|
||||
static int64_t preallocate_getlength(BlockDriverState *bs)
|
||||
{
|
||||
int64_t ret;
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
|
||||
if (s->data_end >= 0) {
|
||||
return s->data_end;
|
||||
}
|
||||
|
||||
ret = bdrv_getlength(bs->file->bs);
|
||||
|
||||
if (has_prealloc_perms(bs)) {
|
||||
s->file_end = s->zero_start = s->data_end = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int preallocate_check_perm(BlockDriverState *bs,
|
||||
uint64_t perm, uint64_t shared, Error **errp)
|
||||
{
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
|
||||
if (s->data_end >= 0 && !can_write_resize(perm)) {
|
||||
/*
|
||||
* Lose permissions.
|
||||
* We should truncate in check_perm, as in set_perm bs->file->perm will
|
||||
* be already changed, and we should not violate it.
|
||||
*/
|
||||
if (s->file_end < 0) {
|
||||
s->file_end = bdrv_getlength(bs->file->bs);
|
||||
if (s->file_end < 0) {
|
||||
error_setg(errp, "Failed to get file length");
|
||||
return s->file_end;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->data_end < s->file_end) {
|
||||
int ret = bdrv_truncate(bs->file, s->data_end, true,
|
||||
PREALLOC_MODE_OFF, 0, NULL);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to drop preallocation");
|
||||
s->file_end = ret;
|
||||
return ret;
|
||||
}
|
||||
s->file_end = s->data_end;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void preallocate_set_perm(BlockDriverState *bs,
|
||||
uint64_t perm, uint64_t shared)
|
||||
{
|
||||
BDRVPreallocateState *s = bs->opaque;
|
||||
|
||||
if (can_write_resize(perm)) {
|
||||
if (s->data_end < 0) {
|
||||
s->data_end = s->file_end = s->zero_start =
|
||||
bdrv_getlength(bs->file->bs);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We drop our permissions, as well as allow shared
|
||||
* permissions (see preallocate_child_perm), anyone will be able to
|
||||
* change the child, so mark all states invalid. We'll regain control if
|
||||
* get good permissions back.
|
||||
*/
|
||||
s->data_end = s->file_end = s->zero_start = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
BdrvChildRole role, BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
|
||||
|
||||
if (can_write_resize(perm)) {
|
||||
/* This should come by default, but let's enforce: */
|
||||
*nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||
|
||||
/*
|
||||
* Don't share, to keep our states s->file_end, s->data_end and
|
||||
* s->zero_start valid.
|
||||
*/
|
||||
*nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
BlockDriver bdrv_preallocate_filter = {
|
||||
.format_name = "preallocate",
|
||||
.instance_size = sizeof(BDRVPreallocateState),
|
||||
|
||||
.bdrv_getlength = preallocate_getlength,
|
||||
.bdrv_open = preallocate_open,
|
||||
.bdrv_close = preallocate_close,
|
||||
|
||||
.bdrv_reopen_prepare = preallocate_reopen_prepare,
|
||||
.bdrv_reopen_commit = preallocate_reopen_commit,
|
||||
.bdrv_reopen_abort = preallocate_reopen_abort,
|
||||
|
||||
.bdrv_co_preadv_part = preallocate_co_preadv_part,
|
||||
.bdrv_co_pwritev_part = preallocate_co_pwritev_part,
|
||||
.bdrv_co_pwrite_zeroes = preallocate_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = preallocate_co_pdiscard,
|
||||
.bdrv_co_flush = preallocate_co_flush,
|
||||
.bdrv_co_truncate = preallocate_co_truncate,
|
||||
|
||||
.bdrv_check_perm = preallocate_check_perm,
|
||||
.bdrv_set_perm = preallocate_set_perm,
|
||||
.bdrv_child_perm = preallocate_child_perm,
|
||||
|
||||
.has_variable_length = true,
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
static void bdrv_preallocate_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_preallocate_filter);
|
||||
}
|
||||
|
||||
block_init(bdrv_preallocate_init);
|
@ -952,3 +952,29 @@ on host and see if there are locks held by the QEMU process on the image file.
|
||||
More than one byte could be locked by the QEMU instance, each byte of which
|
||||
reflects a particular permission that is acquired or protected by the running
|
||||
block driver.
|
||||
|
||||
Filter drivers
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
QEMU supports several filter drivers, which don't store any data, but perform
|
||||
some additional tasks, hooking io requests.
|
||||
|
||||
.. program:: filter-drivers
|
||||
.. option:: preallocate
|
||||
|
||||
The preallocate filter driver is intended to be inserted between format
|
||||
and protocol nodes and preallocates some additional space
|
||||
(expanding the protocol file) when writing past the file’s end. This can be
|
||||
useful for file-systems with slow allocation.
|
||||
|
||||
Supported options:
|
||||
|
||||
.. program:: preallocate
|
||||
.. option:: prealloc-align
|
||||
|
||||
On preallocation, align the file length to this value (in bytes), default 1M.
|
||||
|
||||
.. program:: preallocate
|
||||
.. option:: prealloc-size
|
||||
|
||||
How much to preallocate (in bytes), default 128M.
|
||||
|
@ -2829,7 +2829,7 @@
|
||||
'cloop', 'compress', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps',
|
||||
'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi',
|
||||
'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
|
||||
'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||
'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||
'sheepdog',
|
||||
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
|
||||
@ -3098,6 +3098,23 @@
|
||||
'data': { 'aes': 'QCryptoBlockOptionsQCow',
|
||||
'luks': 'QCryptoBlockOptionsLUKS'} }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsPreallocate:
|
||||
#
|
||||
# Filter driver intended to be inserted between format and protocol node
|
||||
# and do preallocation in protocol node on write.
|
||||
#
|
||||
# @prealloc-align: on preallocation, align file length to this number,
|
||||
# default 1048576 (1M)
|
||||
#
|
||||
# @prealloc-size: how much to preallocate, default 134217728 (128M)
|
||||
#
|
||||
# Since: 6.0
|
||||
##
|
||||
{ 'struct': 'BlockdevOptionsPreallocate',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*prealloc-align': 'int', '*prealloc-size': 'int' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsQcow2:
|
||||
#
|
||||
@ -4006,6 +4023,7 @@
|
||||
'null-co': 'BlockdevOptionsNull',
|
||||
'nvme': 'BlockdevOptionsNVMe',
|
||||
'parallels': 'BlockdevOptionsGenericFormat',
|
||||
'preallocate':'BlockdevOptionsPreallocate',
|
||||
'qcow2': 'BlockdevOptionsQcow2',
|
||||
'qcow': 'BlockdevOptionsQcow',
|
||||
'qed': 'BlockdevOptionsGenericCOWFormat',
|
||||
|
Loading…
Reference in New Issue
Block a user