27
#include "qemu/osdep.h"
29
#include "qapi/error.h"
30
#include "qemu/module.h"
31
#include "qemu/option.h"
32
#include "qemu/units.h"
33
#include "block/block-io.h"
34
#include "block/block_int.h"
37
typedef struct PreallocateOpts {
38
int64_t prealloc_size;
39
int64_t prealloc_align;
42
typedef struct BDRVPreallocateState {
80
QEMUBH *drop_resize_bh;
81
} BDRVPreallocateState;
83
static int preallocate_drop_resize(BlockDriverState *bs, Error **errp);
84
static void preallocate_drop_resize_bh(void *opaque);
86
#define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align"
87
#define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size"
88
static QemuOptsList runtime_opts = {
89
.name = "preallocate",
90
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
93
.name = PREALLOCATE_OPT_PREALLOC_ALIGN,
94
.type = QEMU_OPT_SIZE,
95
.help = "on preallocation, align file length to this number, "
99
.name = PREALLOCATE_OPT_PREALLOC_SIZE,
100
.type = QEMU_OPT_SIZE,
101
.help = "how much to preallocate, default 128M",
107
static bool preallocate_absorb_opts(PreallocateOpts *dest, QDict *options,
108
BlockDriverState *child_bs, Error **errp)
110
QemuOpts *opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
112
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
116
dest->prealloc_align =
117
qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_ALIGN, 1 * MiB);
118
dest->prealloc_size =
119
qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_SIZE, 128 * MiB);
123
if (!QEMU_IS_ALIGNED(dest->prealloc_align, BDRV_SECTOR_SIZE)) {
124
error_setg(errp, "prealloc-align parameter of preallocate filter "
125
"is not aligned to %llu", BDRV_SECTOR_SIZE);
129
if (!QEMU_IS_ALIGNED(dest->prealloc_align,
130
child_bs->bl.request_alignment)) {
131
error_setg(errp, "prealloc-align parameter of preallocate filter "
132
"is not aligned to underlying node request alignment "
133
"(%" PRIi32 ")", child_bs->bl.request_alignment);
140
static int preallocate_open(BlockDriverState *bs, QDict *options, int flags,
143
BDRVPreallocateState *s = bs->opaque;
152
s->file_end = s->zero_start = s->data_end = -EINVAL;
153
s->drop_resize_bh = qemu_bh_new(preallocate_drop_resize_bh, bs);
155
ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
160
GRAPH_RDLOCK_GUARD_MAINLOOP();
162
if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) {
166
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
167
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
169
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
170
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
171
bs->file->bs->supported_zero_flags);
176
static int GRAPH_RDLOCK
177
preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp)
179
BDRVPreallocateState *s = bs->opaque;
182
if (s->file_end < 0) {
183
s->file_end = bdrv_getlength(bs->file->bs);
184
if (s->file_end < 0) {
185
error_setg_errno(errp, -s->file_end, "Failed to get file length");
190
if (s->data_end < s->file_end) {
191
ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0,
194
error_setg_errno(errp, -ret, "Failed to drop preallocation");
198
s->file_end = s->data_end;
204
static void preallocate_close(BlockDriverState *bs)
206
BDRVPreallocateState *s = bs->opaque;
209
GRAPH_RDLOCK_GUARD_MAINLOOP();
211
qemu_bh_cancel(s->drop_resize_bh);
212
qemu_bh_delete(s->drop_resize_bh);
214
if (s->data_end >= 0) {
215
preallocate_truncate_to_real_size(bs, NULL);
228
static int preallocate_reopen_prepare(BDRVReopenState *reopen_state,
229
BlockReopenQueue *queue, Error **errp)
231
PreallocateOpts *opts = g_new0(PreallocateOpts, 1);
235
GRAPH_RDLOCK_GUARD_MAINLOOP();
237
if (!preallocate_absorb_opts(opts, reopen_state->options,
238
reopen_state->bs->file->bs, errp)) {
248
if ((reopen_state->flags & BDRV_O_RDWR) == 0) {
249
ret = preallocate_drop_resize(reopen_state->bs, errp);
256
reopen_state->opaque = opts;
261
static void preallocate_reopen_commit(BDRVReopenState *state)
263
BDRVPreallocateState *s = state->bs->opaque;
265
s->opts = *(PreallocateOpts *)state->opaque;
267
g_free(state->opaque);
268
state->opaque = NULL;
271
static void preallocate_reopen_abort(BDRVReopenState *state)
273
g_free(state->opaque);
274
state->opaque = NULL;
277
static int coroutine_fn GRAPH_RDLOCK
278
preallocate_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes,
279
QEMUIOVector *qiov, size_t qiov_offset,
280
BdrvRequestFlags flags)
282
return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
286
static int coroutine_fn GRAPH_RDLOCK
287
preallocate_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
289
return bdrv_co_pdiscard(bs->file, offset, bytes);
292
static bool can_write_resize(uint64_t perm)
294
return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE);
297
static bool GRAPH_RDLOCK has_prealloc_perms(BlockDriverState *bs)
299
BDRVPreallocateState *s = bs->opaque;
301
if (can_write_resize(bs->file->perm)) {
302
assert(!(bs->file->shared_perm & BLK_PERM_WRITE));
303
assert(!(bs->file->shared_perm & BLK_PERM_RESIZE));
307
assert(s->data_end < 0);
308
assert(s->zero_start < 0);
309
assert(s->file_end < 0);
321
static bool coroutine_fn GRAPH_RDLOCK
322
handle_write(BlockDriverState *bs, int64_t offset, int64_t bytes,
323
bool want_merge_zero)
325
BDRVPreallocateState *s = bs->opaque;
326
int64_t end = offset + bytes;
327
int64_t prealloc_start, prealloc_end;
329
uint32_t file_align = bs->file->bs->bl.request_alignment;
330
uint32_t prealloc_align = MAX(s->opts.prealloc_align, file_align);
332
assert(QEMU_IS_ALIGNED(prealloc_align, file_align));
334
if (!has_prealloc_perms(bs)) {
339
if (s->data_end < 0) {
340
s->data_end = bdrv_co_getlength(bs->file->bs);
341
if (s->data_end < 0) {
345
if (s->file_end < 0) {
346
s->file_end = s->data_end;
350
if (end <= s->data_end) {
357
if (s->zero_start < 0 || !want_merge_zero) {
361
if (s->file_end < 0) {
362
s->file_end = bdrv_co_getlength(bs->file->bs);
363
if (s->file_end < 0) {
370
if (end <= s->file_end) {
372
return want_merge_zero && offset >= s->zero_start;
377
prealloc_start = QEMU_ALIGN_UP(
378
want_merge_zero ? MIN(offset, s->file_end) : s->file_end,
380
prealloc_end = QEMU_ALIGN_UP(
381
MAX(prealloc_start, end) + s->opts.prealloc_size,
384
want_merge_zero = want_merge_zero && (prealloc_start <= offset);
386
ret = bdrv_co_pwrite_zeroes(
387
bs->file, prealloc_start, prealloc_end - prealloc_start,
388
BDRV_REQ_NO_FALLBACK | BDRV_REQ_SERIALISING | BDRV_REQ_NO_WAIT);
394
s->file_end = prealloc_end;
395
return want_merge_zero;
398
static int coroutine_fn GRAPH_RDLOCK
399
preallocate_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
400
int64_t bytes, BdrvRequestFlags flags)
402
bool want_merge_zero =
403
!(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK));
404
if (handle_write(bs, offset, bytes, want_merge_zero)) {
408
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
411
static int coroutine_fn GRAPH_RDLOCK
412
preallocate_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes,
413
QEMUIOVector *qiov, size_t qiov_offset,
414
BdrvRequestFlags flags)
416
handle_write(bs, offset, bytes, false);
418
return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
422
static int coroutine_fn GRAPH_RDLOCK
423
preallocate_co_truncate(BlockDriverState *bs, int64_t offset,
424
bool exact, PreallocMode prealloc,
425
BdrvRequestFlags flags, Error **errp)
428
BDRVPreallocateState *s = bs->opaque;
431
if (s->data_end >= 0 && offset > s->data_end) {
432
if (s->file_end < 0) {
433
s->file_end = bdrv_co_getlength(bs->file->bs);
434
if (s->file_end < 0) {
435
error_setg(errp, "failed to get file length");
440
if (prealloc == PREALLOC_MODE_FALLOC) {
447
if (offset <= s->file_end) {
448
s->data_end = offset;
461
if (s->file_end > s->data_end) {
462
ret = bdrv_co_truncate(bs->file, s->data_end, true,
463
PREALLOC_MODE_OFF, 0, errp);
466
error_prepend(errp, "preallocate-filter: failed to drop "
467
"write-zero preallocation: ");
470
s->file_end = s->data_end;
474
s->data_end = offset;
477
ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
479
s->file_end = s->zero_start = s->data_end = ret;
483
if (has_prealloc_perms(bs)) {
484
s->file_end = s->zero_start = s->data_end = offset;
489
static int coroutine_fn GRAPH_RDLOCK preallocate_co_flush(BlockDriverState *bs)
491
return bdrv_co_flush(bs->file->bs);
494
static int64_t coroutine_fn GRAPH_RDLOCK
495
preallocate_co_getlength(BlockDriverState *bs)
498
BDRVPreallocateState *s = bs->opaque;
500
if (s->data_end >= 0) {
504
ret = bdrv_co_getlength(bs->file->bs);
506
if (has_prealloc_perms(bs)) {
507
s->file_end = s->zero_start = s->data_end = ret;
513
static int GRAPH_RDLOCK
514
preallocate_drop_resize(BlockDriverState *bs, Error **errp)
516
BDRVPreallocateState *s = bs->opaque;
519
if (s->data_end < 0) {
527
ret = preallocate_truncate_to_real_size(bs, errp);
538
s->data_end = s->file_end = s->zero_start = -EINVAL;
540
bdrv_child_refresh_perms(bs, bs->file, NULL);
545
static void preallocate_drop_resize_bh(void *opaque)
548
GRAPH_RDLOCK_GUARD_MAINLOOP();
554
preallocate_drop_resize(opaque, NULL);
557
static void GRAPH_RDLOCK
558
preallocate_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
560
BDRVPreallocateState *s = bs->opaque;
562
if (can_write_resize(perm)) {
563
qemu_bh_cancel(s->drop_resize_bh);
564
if (s->data_end < 0) {
565
s->data_end = s->file_end = s->zero_start =
566
bs->file->bs->total_sectors * BDRV_SECTOR_SIZE;
569
qemu_bh_schedule(s->drop_resize_bh);
573
static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c,
574
BdrvChildRole role, BlockReopenQueue *reopen_queue,
575
uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared)
577
BDRVPreallocateState *s = bs->opaque;
579
bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
586
if (can_write_resize(perm) || s->data_end != -EINVAL) {
587
*nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
593
*nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
597
static BlockDriver bdrv_preallocate_filter = {
598
.format_name = "preallocate",
599
.instance_size = sizeof(BDRVPreallocateState),
601
.bdrv_co_getlength = preallocate_co_getlength,
602
.bdrv_open = preallocate_open,
603
.bdrv_close = preallocate_close,
605
.bdrv_reopen_prepare = preallocate_reopen_prepare,
606
.bdrv_reopen_commit = preallocate_reopen_commit,
607
.bdrv_reopen_abort = preallocate_reopen_abort,
609
.bdrv_co_preadv_part = preallocate_co_preadv_part,
610
.bdrv_co_pwritev_part = preallocate_co_pwritev_part,
611
.bdrv_co_pwrite_zeroes = preallocate_co_pwrite_zeroes,
612
.bdrv_co_pdiscard = preallocate_co_pdiscard,
613
.bdrv_co_flush = preallocate_co_flush,
614
.bdrv_co_truncate = preallocate_co_truncate,
616
.bdrv_set_perm = preallocate_set_perm,
617
.bdrv_child_perm = preallocate_child_perm,
622
static void bdrv_preallocate_init(void)
624
bdrv_register(&bdrv_preallocate_filter);
627
block_init(bdrv_preallocate_init);