133fa2222SVladimir Sementsov-Ogievskiy /*
233fa2222SVladimir Sementsov-Ogievskiy * preallocate filter driver
333fa2222SVladimir Sementsov-Ogievskiy *
433fa2222SVladimir Sementsov-Ogievskiy * The driver performs preallocate operation: it is injected above
533fa2222SVladimir Sementsov-Ogievskiy * some node, and before each write over EOF it does additional preallocating
633fa2222SVladimir Sementsov-Ogievskiy * write-zeroes request.
733fa2222SVladimir Sementsov-Ogievskiy *
833fa2222SVladimir Sementsov-Ogievskiy * Copyright (c) 2020 Virtuozzo International GmbH.
933fa2222SVladimir Sementsov-Ogievskiy *
1033fa2222SVladimir Sementsov-Ogievskiy * Author:
1133fa2222SVladimir Sementsov-Ogievskiy * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
1233fa2222SVladimir Sementsov-Ogievskiy *
1333fa2222SVladimir Sementsov-Ogievskiy * This program is free software; you can redistribute it and/or modify
1433fa2222SVladimir Sementsov-Ogievskiy * it under the terms of the GNU General Public License as published by
1533fa2222SVladimir Sementsov-Ogievskiy * the Free Software Foundation; either version 2 of the License, or
1633fa2222SVladimir Sementsov-Ogievskiy * (at your option) any later version.
1733fa2222SVladimir Sementsov-Ogievskiy *
1833fa2222SVladimir Sementsov-Ogievskiy * This program is distributed in the hope that it will be useful,
1933fa2222SVladimir Sementsov-Ogievskiy * but WITHOUT ANY WARRANTY; without even the implied warranty of
2033fa2222SVladimir Sementsov-Ogievskiy * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2133fa2222SVladimir Sementsov-Ogievskiy * GNU General Public License for more details.
2233fa2222SVladimir Sementsov-Ogievskiy *
2333fa2222SVladimir Sementsov-Ogievskiy * You should have received a copy of the GNU General Public License
2433fa2222SVladimir Sementsov-Ogievskiy * along with this program. If not, see <http://www.gnu.org/licenses/>.
2533fa2222SVladimir Sementsov-Ogievskiy */
2633fa2222SVladimir Sementsov-Ogievskiy
2733fa2222SVladimir Sementsov-Ogievskiy #include "qemu/osdep.h"
2833fa2222SVladimir Sementsov-Ogievskiy
2933fa2222SVladimir Sementsov-Ogievskiy #include "qapi/error.h"
3033fa2222SVladimir Sementsov-Ogievskiy #include "qemu/module.h"
3133fa2222SVladimir Sementsov-Ogievskiy #include "qemu/option.h"
3233fa2222SVladimir Sementsov-Ogievskiy #include "qemu/units.h"
33e2c1c34fSMarkus Armbruster #include "block/block-io.h"
3433fa2222SVladimir Sementsov-Ogievskiy #include "block/block_int.h"
3533fa2222SVladimir Sementsov-Ogievskiy
3633fa2222SVladimir Sementsov-Ogievskiy
3733fa2222SVladimir Sementsov-Ogievskiy typedef struct PreallocateOpts {
3833fa2222SVladimir Sementsov-Ogievskiy int64_t prealloc_size;
3933fa2222SVladimir Sementsov-Ogievskiy int64_t prealloc_align;
4033fa2222SVladimir Sementsov-Ogievskiy } PreallocateOpts;
4133fa2222SVladimir Sementsov-Ogievskiy
4233fa2222SVladimir Sementsov-Ogievskiy typedef struct BDRVPreallocateState {
4333fa2222SVladimir Sementsov-Ogievskiy PreallocateOpts opts;
4433fa2222SVladimir Sementsov-Ogievskiy
4533fa2222SVladimir Sementsov-Ogievskiy /*
4633fa2222SVladimir Sementsov-Ogievskiy * Track real data end, to crop preallocation on close. If < 0 the status is
4733fa2222SVladimir Sementsov-Ogievskiy * unknown.
4833fa2222SVladimir Sementsov-Ogievskiy *
4933fa2222SVladimir Sementsov-Ogievskiy * @data_end is a maximum of file size on open (or when we get write/resize
5033fa2222SVladimir Sementsov-Ogievskiy * permissions) and all write request ends after it. So it's safe to
5133fa2222SVladimir Sementsov-Ogievskiy * truncate to data_end if it is valid.
5233fa2222SVladimir Sementsov-Ogievskiy */
5333fa2222SVladimir Sementsov-Ogievskiy int64_t data_end;
5433fa2222SVladimir Sementsov-Ogievskiy
5533fa2222SVladimir Sementsov-Ogievskiy /*
5633fa2222SVladimir Sementsov-Ogievskiy * Start of trailing preallocated area which reads as zero. May be smaller
5733fa2222SVladimir Sementsov-Ogievskiy * than data_end, if user does over-EOF write zero operation. If < 0 the
5833fa2222SVladimir Sementsov-Ogievskiy * status is unknown.
5933fa2222SVladimir Sementsov-Ogievskiy *
6033fa2222SVladimir Sementsov-Ogievskiy * If both @zero_start and @file_end are valid, the region
6133fa2222SVladimir Sementsov-Ogievskiy * [@zero_start, @file_end) is known to be preallocated zeroes. If @file_end
6233fa2222SVladimir Sementsov-Ogievskiy * is not valid, @zero_start doesn't make much sense.
6333fa2222SVladimir Sementsov-Ogievskiy */
6433fa2222SVladimir Sementsov-Ogievskiy int64_t zero_start;
6533fa2222SVladimir Sementsov-Ogievskiy
6633fa2222SVladimir Sementsov-Ogievskiy /*
6733fa2222SVladimir Sementsov-Ogievskiy * Real end of file. Actually the cache for bdrv_getlength(bs->file->bs),
6833fa2222SVladimir Sementsov-Ogievskiy * to avoid extra lseek() calls on each write operation. If < 0 the status
6933fa2222SVladimir Sementsov-Ogievskiy * is unknown.
7033fa2222SVladimir Sementsov-Ogievskiy */
7133fa2222SVladimir Sementsov-Ogievskiy int64_t file_end;
7233fa2222SVladimir Sementsov-Ogievskiy
7333fa2222SVladimir Sementsov-Ogievskiy /*
7433fa2222SVladimir Sementsov-Ogievskiy * All three states @data_end, @zero_start and @file_end are guaranteed to
7533fa2222SVladimir Sementsov-Ogievskiy * be invalid (< 0) when we don't have both exclusive BLK_PERM_RESIZE and
7633fa2222SVladimir Sementsov-Ogievskiy * BLK_PERM_WRITE permissions on file child.
7733fa2222SVladimir Sementsov-Ogievskiy */
78edcce17bSKevin Wolf
79edcce17bSKevin Wolf /* Gives up the resize permission on children when parents don't need it */
80edcce17bSKevin Wolf QEMUBH *drop_resize_bh;
8133fa2222SVladimir Sementsov-Ogievskiy } BDRVPreallocateState;
8233fa2222SVladimir Sementsov-Ogievskiy
83edcce17bSKevin Wolf static int preallocate_drop_resize(BlockDriverState *bs, Error **errp);
84edcce17bSKevin Wolf static void preallocate_drop_resize_bh(void *opaque);
85edcce17bSKevin Wolf
8633fa2222SVladimir Sementsov-Ogievskiy #define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align"
8733fa2222SVladimir Sementsov-Ogievskiy #define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size"
8833fa2222SVladimir Sementsov-Ogievskiy static QemuOptsList runtime_opts = {
8933fa2222SVladimir Sementsov-Ogievskiy .name = "preallocate",
9033fa2222SVladimir Sementsov-Ogievskiy .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
9133fa2222SVladimir Sementsov-Ogievskiy .desc = {
9233fa2222SVladimir Sementsov-Ogievskiy {
9333fa2222SVladimir Sementsov-Ogievskiy .name = PREALLOCATE_OPT_PREALLOC_ALIGN,
9433fa2222SVladimir Sementsov-Ogievskiy .type = QEMU_OPT_SIZE,
9533fa2222SVladimir Sementsov-Ogievskiy .help = "on preallocation, align file length to this number, "
9633fa2222SVladimir Sementsov-Ogievskiy "default 1M",
9733fa2222SVladimir Sementsov-Ogievskiy },
9833fa2222SVladimir Sementsov-Ogievskiy {
9933fa2222SVladimir Sementsov-Ogievskiy .name = PREALLOCATE_OPT_PREALLOC_SIZE,
10033fa2222SVladimir Sementsov-Ogievskiy .type = QEMU_OPT_SIZE,
10133fa2222SVladimir Sementsov-Ogievskiy .help = "how much to preallocate, default 128M",
10233fa2222SVladimir Sementsov-Ogievskiy },
10333fa2222SVladimir Sementsov-Ogievskiy { /* end of list */ }
10433fa2222SVladimir Sementsov-Ogievskiy },
10533fa2222SVladimir Sementsov-Ogievskiy };
10633fa2222SVladimir Sementsov-Ogievskiy
preallocate_absorb_opts(PreallocateOpts * dest,QDict * options,BlockDriverState * child_bs,Error ** errp)10733fa2222SVladimir Sementsov-Ogievskiy static bool preallocate_absorb_opts(PreallocateOpts *dest, QDict *options,
10833fa2222SVladimir Sementsov-Ogievskiy BlockDriverState *child_bs, Error **errp)
10933fa2222SVladimir Sementsov-Ogievskiy {
11033fa2222SVladimir Sementsov-Ogievskiy QemuOpts *opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
11133fa2222SVladimir Sementsov-Ogievskiy
11233fa2222SVladimir Sementsov-Ogievskiy if (!qemu_opts_absorb_qdict(opts, options, errp)) {
11333fa2222SVladimir Sementsov-Ogievskiy return false;
11433fa2222SVladimir Sementsov-Ogievskiy }
11533fa2222SVladimir Sementsov-Ogievskiy
11633fa2222SVladimir Sementsov-Ogievskiy dest->prealloc_align =
11733fa2222SVladimir Sementsov-Ogievskiy qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_ALIGN, 1 * MiB);
11833fa2222SVladimir Sementsov-Ogievskiy dest->prealloc_size =
11933fa2222SVladimir Sementsov-Ogievskiy qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_SIZE, 128 * MiB);
12033fa2222SVladimir Sementsov-Ogievskiy
12133fa2222SVladimir Sementsov-Ogievskiy qemu_opts_del(opts);
12233fa2222SVladimir Sementsov-Ogievskiy
12333fa2222SVladimir Sementsov-Ogievskiy if (!QEMU_IS_ALIGNED(dest->prealloc_align, BDRV_SECTOR_SIZE)) {
12433fa2222SVladimir Sementsov-Ogievskiy error_setg(errp, "prealloc-align parameter of preallocate filter "
12533fa2222SVladimir Sementsov-Ogievskiy "is not aligned to %llu", BDRV_SECTOR_SIZE);
12633fa2222SVladimir Sementsov-Ogievskiy return false;
12733fa2222SVladimir Sementsov-Ogievskiy }
12833fa2222SVladimir Sementsov-Ogievskiy
12933fa2222SVladimir Sementsov-Ogievskiy if (!QEMU_IS_ALIGNED(dest->prealloc_align,
13033fa2222SVladimir Sementsov-Ogievskiy child_bs->bl.request_alignment)) {
13133fa2222SVladimir Sementsov-Ogievskiy error_setg(errp, "prealloc-align parameter of preallocate filter "
13233fa2222SVladimir Sementsov-Ogievskiy "is not aligned to underlying node request alignment "
13333fa2222SVladimir Sementsov-Ogievskiy "(%" PRIi32 ")", child_bs->bl.request_alignment);
13433fa2222SVladimir Sementsov-Ogievskiy return false;
13533fa2222SVladimir Sementsov-Ogievskiy }
13633fa2222SVladimir Sementsov-Ogievskiy
13733fa2222SVladimir Sementsov-Ogievskiy return true;
13833fa2222SVladimir Sementsov-Ogievskiy }
13933fa2222SVladimir Sementsov-Ogievskiy
preallocate_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)14033fa2222SVladimir Sementsov-Ogievskiy static int preallocate_open(BlockDriverState *bs, QDict *options, int flags,
14133fa2222SVladimir Sementsov-Ogievskiy Error **errp)
14233fa2222SVladimir Sementsov-Ogievskiy {
14333fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
14483930780SVladimir Sementsov-Ogievskiy int ret;
14533fa2222SVladimir Sementsov-Ogievskiy
146a4b740dbSKevin Wolf GLOBAL_STATE_CODE();
147a4b740dbSKevin Wolf
14833fa2222SVladimir Sementsov-Ogievskiy /*
14933fa2222SVladimir Sementsov-Ogievskiy * s->data_end and friends should be initialized on permission update.
15033fa2222SVladimir Sementsov-Ogievskiy * For this to work, mark them invalid.
15133fa2222SVladimir Sementsov-Ogievskiy */
15233fa2222SVladimir Sementsov-Ogievskiy s->file_end = s->zero_start = s->data_end = -EINVAL;
153edcce17bSKevin Wolf s->drop_resize_bh = qemu_bh_new(preallocate_drop_resize_bh, bs);
15433fa2222SVladimir Sementsov-Ogievskiy
15583930780SVladimir Sementsov-Ogievskiy ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
15683930780SVladimir Sementsov-Ogievskiy if (ret < 0) {
15783930780SVladimir Sementsov-Ogievskiy return ret;
15833fa2222SVladimir Sementsov-Ogievskiy }
15933fa2222SVladimir Sementsov-Ogievskiy
160a4b740dbSKevin Wolf GRAPH_RDLOCK_GUARD_MAINLOOP();
161a4b740dbSKevin Wolf
16233fa2222SVladimir Sementsov-Ogievskiy if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) {
16333fa2222SVladimir Sementsov-Ogievskiy return -EINVAL;
16433fa2222SVladimir Sementsov-Ogievskiy }
16533fa2222SVladimir Sementsov-Ogievskiy
16633fa2222SVladimir Sementsov-Ogievskiy bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
16733fa2222SVladimir Sementsov-Ogievskiy (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
16833fa2222SVladimir Sementsov-Ogievskiy
16933fa2222SVladimir Sementsov-Ogievskiy bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
17033fa2222SVladimir Sementsov-Ogievskiy ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
17133fa2222SVladimir Sementsov-Ogievskiy bs->file->bs->supported_zero_flags);
17233fa2222SVladimir Sementsov-Ogievskiy
17333fa2222SVladimir Sementsov-Ogievskiy return 0;
17433fa2222SVladimir Sementsov-Ogievskiy }
17533fa2222SVladimir Sementsov-Ogievskiy
176*1f051dcbSKevin Wolf static int GRAPH_RDLOCK
preallocate_truncate_to_real_size(BlockDriverState * bs,Error ** errp)177*1f051dcbSKevin Wolf preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp)
17833fa2222SVladimir Sementsov-Ogievskiy {
17933fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
18001e28f60SKevin Wolf int ret;
18133fa2222SVladimir Sementsov-Ogievskiy
18233fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
18333fa2222SVladimir Sementsov-Ogievskiy s->file_end = bdrv_getlength(bs->file->bs);
18433fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
18501e28f60SKevin Wolf error_setg_errno(errp, -s->file_end, "Failed to get file length");
18601e28f60SKevin Wolf return s->file_end;
18733fa2222SVladimir Sementsov-Ogievskiy }
18833fa2222SVladimir Sementsov-Ogievskiy }
18933fa2222SVladimir Sementsov-Ogievskiy
19033fa2222SVladimir Sementsov-Ogievskiy if (s->data_end < s->file_end) {
19133fa2222SVladimir Sementsov-Ogievskiy ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0,
19233fa2222SVladimir Sementsov-Ogievskiy NULL);
19301e28f60SKevin Wolf if (ret < 0) {
19401e28f60SKevin Wolf error_setg_errno(errp, -ret, "Failed to drop preallocation");
19501e28f60SKevin Wolf s->file_end = ret;
19601e28f60SKevin Wolf return ret;
19701e28f60SKevin Wolf }
19801e28f60SKevin Wolf s->file_end = s->data_end;
19901e28f60SKevin Wolf }
20001e28f60SKevin Wolf
20101e28f60SKevin Wolf return 0;
20201e28f60SKevin Wolf }
20301e28f60SKevin Wolf
preallocate_close(BlockDriverState * bs)20401e28f60SKevin Wolf static void preallocate_close(BlockDriverState *bs)
20501e28f60SKevin Wolf {
20601e28f60SKevin Wolf BDRVPreallocateState *s = bs->opaque;
20701e28f60SKevin Wolf
208*1f051dcbSKevin Wolf GLOBAL_STATE_CODE();
209*1f051dcbSKevin Wolf GRAPH_RDLOCK_GUARD_MAINLOOP();
210*1f051dcbSKevin Wolf
211edcce17bSKevin Wolf qemu_bh_cancel(s->drop_resize_bh);
212edcce17bSKevin Wolf qemu_bh_delete(s->drop_resize_bh);
213edcce17bSKevin Wolf
21401e28f60SKevin Wolf if (s->data_end >= 0) {
21501e28f60SKevin Wolf preallocate_truncate_to_real_size(bs, NULL);
21633fa2222SVladimir Sementsov-Ogievskiy }
21733fa2222SVladimir Sementsov-Ogievskiy }
21833fa2222SVladimir Sementsov-Ogievskiy
21933fa2222SVladimir Sementsov-Ogievskiy
22033fa2222SVladimir Sementsov-Ogievskiy /*
22133fa2222SVladimir Sementsov-Ogievskiy * Handle reopen.
22233fa2222SVladimir Sementsov-Ogievskiy *
22333fa2222SVladimir Sementsov-Ogievskiy * We must implement reopen handlers, otherwise reopen just don't work. Handle
22433fa2222SVladimir Sementsov-Ogievskiy * new options and don't care about preallocation state, as it is handled in
22533fa2222SVladimir Sementsov-Ogievskiy * set/check permission handlers.
22633fa2222SVladimir Sementsov-Ogievskiy */
22733fa2222SVladimir Sementsov-Ogievskiy
preallocate_reopen_prepare(BDRVReopenState * reopen_state,BlockReopenQueue * queue,Error ** errp)22833fa2222SVladimir Sementsov-Ogievskiy static int preallocate_reopen_prepare(BDRVReopenState *reopen_state,
22933fa2222SVladimir Sementsov-Ogievskiy BlockReopenQueue *queue, Error **errp)
23033fa2222SVladimir Sementsov-Ogievskiy {
23133fa2222SVladimir Sementsov-Ogievskiy PreallocateOpts *opts = g_new0(PreallocateOpts, 1);
232edcce17bSKevin Wolf int ret;
23333fa2222SVladimir Sementsov-Ogievskiy
234*1f051dcbSKevin Wolf GLOBAL_STATE_CODE();
235*1f051dcbSKevin Wolf GRAPH_RDLOCK_GUARD_MAINLOOP();
236*1f051dcbSKevin Wolf
23733fa2222SVladimir Sementsov-Ogievskiy if (!preallocate_absorb_opts(opts, reopen_state->options,
23833fa2222SVladimir Sementsov-Ogievskiy reopen_state->bs->file->bs, errp)) {
23933fa2222SVladimir Sementsov-Ogievskiy g_free(opts);
24033fa2222SVladimir Sementsov-Ogievskiy return -EINVAL;
24133fa2222SVladimir Sementsov-Ogievskiy }
24233fa2222SVladimir Sementsov-Ogievskiy
243edcce17bSKevin Wolf /*
244edcce17bSKevin Wolf * Drop the preallocation already here if reopening read-only. The child
245edcce17bSKevin Wolf * might also be reopened read-only and then scheduling a BH during the
246edcce17bSKevin Wolf * permission update is too late.
247edcce17bSKevin Wolf */
248edcce17bSKevin Wolf if ((reopen_state->flags & BDRV_O_RDWR) == 0) {
249edcce17bSKevin Wolf ret = preallocate_drop_resize(reopen_state->bs, errp);
250edcce17bSKevin Wolf if (ret < 0) {
251edcce17bSKevin Wolf g_free(opts);
252edcce17bSKevin Wolf return ret;
253edcce17bSKevin Wolf }
254edcce17bSKevin Wolf }
255edcce17bSKevin Wolf
25633fa2222SVladimir Sementsov-Ogievskiy reopen_state->opaque = opts;
25733fa2222SVladimir Sementsov-Ogievskiy
25833fa2222SVladimir Sementsov-Ogievskiy return 0;
25933fa2222SVladimir Sementsov-Ogievskiy }
26033fa2222SVladimir Sementsov-Ogievskiy
preallocate_reopen_commit(BDRVReopenState * state)26133fa2222SVladimir Sementsov-Ogievskiy static void preallocate_reopen_commit(BDRVReopenState *state)
26233fa2222SVladimir Sementsov-Ogievskiy {
26333fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = state->bs->opaque;
26433fa2222SVladimir Sementsov-Ogievskiy
26533fa2222SVladimir Sementsov-Ogievskiy s->opts = *(PreallocateOpts *)state->opaque;
26633fa2222SVladimir Sementsov-Ogievskiy
26733fa2222SVladimir Sementsov-Ogievskiy g_free(state->opaque);
26833fa2222SVladimir Sementsov-Ogievskiy state->opaque = NULL;
26933fa2222SVladimir Sementsov-Ogievskiy }
27033fa2222SVladimir Sementsov-Ogievskiy
preallocate_reopen_abort(BDRVReopenState * state)27133fa2222SVladimir Sementsov-Ogievskiy static void preallocate_reopen_abort(BDRVReopenState *state)
27233fa2222SVladimir Sementsov-Ogievskiy {
27333fa2222SVladimir Sementsov-Ogievskiy g_free(state->opaque);
27433fa2222SVladimir Sementsov-Ogievskiy state->opaque = NULL;
27533fa2222SVladimir Sementsov-Ogievskiy }
27633fa2222SVladimir Sementsov-Ogievskiy
277b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
preallocate_co_preadv_part(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,size_t qiov_offset,BdrvRequestFlags flags)278b9b10c35SKevin Wolf preallocate_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes,
279b9b10c35SKevin Wolf QEMUIOVector *qiov, size_t qiov_offset,
280b9b10c35SKevin Wolf BdrvRequestFlags flags)
28133fa2222SVladimir Sementsov-Ogievskiy {
28233fa2222SVladimir Sementsov-Ogievskiy return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
28333fa2222SVladimir Sementsov-Ogievskiy flags);
28433fa2222SVladimir Sementsov-Ogievskiy }
28533fa2222SVladimir Sementsov-Ogievskiy
2869a5a1c62SEmanuele Giuseppe Esposito static int coroutine_fn GRAPH_RDLOCK
preallocate_co_pdiscard(BlockDriverState * bs,int64_t offset,int64_t bytes)2879a5a1c62SEmanuele Giuseppe Esposito preallocate_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
28833fa2222SVladimir Sementsov-Ogievskiy {
28933fa2222SVladimir Sementsov-Ogievskiy return bdrv_co_pdiscard(bs->file, offset, bytes);
29033fa2222SVladimir Sementsov-Ogievskiy }
29133fa2222SVladimir Sementsov-Ogievskiy
can_write_resize(uint64_t perm)29233fa2222SVladimir Sementsov-Ogievskiy static bool can_write_resize(uint64_t perm)
29333fa2222SVladimir Sementsov-Ogievskiy {
29433fa2222SVladimir Sementsov-Ogievskiy return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE);
29533fa2222SVladimir Sementsov-Ogievskiy }
29633fa2222SVladimir Sementsov-Ogievskiy
has_prealloc_perms(BlockDriverState * bs)297*1f051dcbSKevin Wolf static bool GRAPH_RDLOCK has_prealloc_perms(BlockDriverState *bs)
29833fa2222SVladimir Sementsov-Ogievskiy {
29933fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
30033fa2222SVladimir Sementsov-Ogievskiy
30133fa2222SVladimir Sementsov-Ogievskiy if (can_write_resize(bs->file->perm)) {
30233fa2222SVladimir Sementsov-Ogievskiy assert(!(bs->file->shared_perm & BLK_PERM_WRITE));
30333fa2222SVladimir Sementsov-Ogievskiy assert(!(bs->file->shared_perm & BLK_PERM_RESIZE));
30433fa2222SVladimir Sementsov-Ogievskiy return true;
30533fa2222SVladimir Sementsov-Ogievskiy }
30633fa2222SVladimir Sementsov-Ogievskiy
30733fa2222SVladimir Sementsov-Ogievskiy assert(s->data_end < 0);
30833fa2222SVladimir Sementsov-Ogievskiy assert(s->zero_start < 0);
30933fa2222SVladimir Sementsov-Ogievskiy assert(s->file_end < 0);
31033fa2222SVladimir Sementsov-Ogievskiy return false;
31133fa2222SVladimir Sementsov-Ogievskiy }
31233fa2222SVladimir Sementsov-Ogievskiy
31333fa2222SVladimir Sementsov-Ogievskiy /*
31433fa2222SVladimir Sementsov-Ogievskiy * Call on each write. Returns true if @want_merge_zero is true and the region
31533fa2222SVladimir Sementsov-Ogievskiy * [offset, offset + bytes) is zeroed (as a result of this call or earlier
31633fa2222SVladimir Sementsov-Ogievskiy * preallocation).
31733fa2222SVladimir Sementsov-Ogievskiy *
31833fa2222SVladimir Sementsov-Ogievskiy * want_merge_zero is used to merge write-zero request with preallocation in
31933fa2222SVladimir Sementsov-Ogievskiy * one bdrv_co_pwrite_zeroes() call.
32033fa2222SVladimir Sementsov-Ogievskiy */
321abaf8b75SKevin Wolf static bool coroutine_fn GRAPH_RDLOCK
handle_write(BlockDriverState * bs,int64_t offset,int64_t bytes,bool want_merge_zero)322abaf8b75SKevin Wolf handle_write(BlockDriverState *bs, int64_t offset, int64_t bytes,
323abaf8b75SKevin Wolf bool want_merge_zero)
32433fa2222SVladimir Sementsov-Ogievskiy {
32533fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
32633fa2222SVladimir Sementsov-Ogievskiy int64_t end = offset + bytes;
32733fa2222SVladimir Sementsov-Ogievskiy int64_t prealloc_start, prealloc_end;
32833fa2222SVladimir Sementsov-Ogievskiy int ret;
32945e62b46SVladimir Sementsov-Ogievskiy uint32_t file_align = bs->file->bs->bl.request_alignment;
33045e62b46SVladimir Sementsov-Ogievskiy uint32_t prealloc_align = MAX(s->opts.prealloc_align, file_align);
33145e62b46SVladimir Sementsov-Ogievskiy
33245e62b46SVladimir Sementsov-Ogievskiy assert(QEMU_IS_ALIGNED(prealloc_align, file_align));
33333fa2222SVladimir Sementsov-Ogievskiy
33433fa2222SVladimir Sementsov-Ogievskiy if (!has_prealloc_perms(bs)) {
33533fa2222SVladimir Sementsov-Ogievskiy /* We don't have state neither should try to recover it */
33633fa2222SVladimir Sementsov-Ogievskiy return false;
33733fa2222SVladimir Sementsov-Ogievskiy }
33833fa2222SVladimir Sementsov-Ogievskiy
33933fa2222SVladimir Sementsov-Ogievskiy if (s->data_end < 0) {
340bd53086eSEmanuele Giuseppe Esposito s->data_end = bdrv_co_getlength(bs->file->bs);
34133fa2222SVladimir Sementsov-Ogievskiy if (s->data_end < 0) {
34233fa2222SVladimir Sementsov-Ogievskiy return false;
34333fa2222SVladimir Sementsov-Ogievskiy }
34433fa2222SVladimir Sementsov-Ogievskiy
34533fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
34633fa2222SVladimir Sementsov-Ogievskiy s->file_end = s->data_end;
34733fa2222SVladimir Sementsov-Ogievskiy }
34833fa2222SVladimir Sementsov-Ogievskiy }
34933fa2222SVladimir Sementsov-Ogievskiy
35033fa2222SVladimir Sementsov-Ogievskiy if (end <= s->data_end) {
35133fa2222SVladimir Sementsov-Ogievskiy return false;
35233fa2222SVladimir Sementsov-Ogievskiy }
35333fa2222SVladimir Sementsov-Ogievskiy
35433fa2222SVladimir Sementsov-Ogievskiy /* We have valid s->data_end, and request writes beyond it. */
35533fa2222SVladimir Sementsov-Ogievskiy
35633fa2222SVladimir Sementsov-Ogievskiy s->data_end = end;
35733fa2222SVladimir Sementsov-Ogievskiy if (s->zero_start < 0 || !want_merge_zero) {
35833fa2222SVladimir Sementsov-Ogievskiy s->zero_start = end;
35933fa2222SVladimir Sementsov-Ogievskiy }
36033fa2222SVladimir Sementsov-Ogievskiy
36133fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
362bd53086eSEmanuele Giuseppe Esposito s->file_end = bdrv_co_getlength(bs->file->bs);
36333fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
36433fa2222SVladimir Sementsov-Ogievskiy return false;
36533fa2222SVladimir Sementsov-Ogievskiy }
36633fa2222SVladimir Sementsov-Ogievskiy }
36733fa2222SVladimir Sementsov-Ogievskiy
36833fa2222SVladimir Sementsov-Ogievskiy /* Now s->data_end, s->zero_start and s->file_end are valid. */
36933fa2222SVladimir Sementsov-Ogievskiy
37033fa2222SVladimir Sementsov-Ogievskiy if (end <= s->file_end) {
37133fa2222SVladimir Sementsov-Ogievskiy /* No preallocation needed. */
37233fa2222SVladimir Sementsov-Ogievskiy return want_merge_zero && offset >= s->zero_start;
37333fa2222SVladimir Sementsov-Ogievskiy }
37433fa2222SVladimir Sementsov-Ogievskiy
37533fa2222SVladimir Sementsov-Ogievskiy /* Now we want new preallocation, as request writes beyond s->file_end. */
37633fa2222SVladimir Sementsov-Ogievskiy
37745e62b46SVladimir Sementsov-Ogievskiy prealloc_start = QEMU_ALIGN_UP(
37845e62b46SVladimir Sementsov-Ogievskiy want_merge_zero ? MIN(offset, s->file_end) : s->file_end,
37945e62b46SVladimir Sementsov-Ogievskiy file_align);
38045e62b46SVladimir Sementsov-Ogievskiy prealloc_end = QEMU_ALIGN_UP(
38145e62b46SVladimir Sementsov-Ogievskiy MAX(prealloc_start, end) + s->opts.prealloc_size,
38245e62b46SVladimir Sementsov-Ogievskiy prealloc_align);
38345e62b46SVladimir Sementsov-Ogievskiy
38445e62b46SVladimir Sementsov-Ogievskiy want_merge_zero = want_merge_zero && (prealloc_start <= offset);
38533fa2222SVladimir Sementsov-Ogievskiy
38633fa2222SVladimir Sementsov-Ogievskiy ret = bdrv_co_pwrite_zeroes(
38733fa2222SVladimir Sementsov-Ogievskiy bs->file, prealloc_start, prealloc_end - prealloc_start,
38833fa2222SVladimir Sementsov-Ogievskiy BDRV_REQ_NO_FALLBACK | BDRV_REQ_SERIALISING | BDRV_REQ_NO_WAIT);
38933fa2222SVladimir Sementsov-Ogievskiy if (ret < 0) {
39033fa2222SVladimir Sementsov-Ogievskiy s->file_end = ret;
39133fa2222SVladimir Sementsov-Ogievskiy return false;
39233fa2222SVladimir Sementsov-Ogievskiy }
39333fa2222SVladimir Sementsov-Ogievskiy
39433fa2222SVladimir Sementsov-Ogievskiy s->file_end = prealloc_end;
39533fa2222SVladimir Sementsov-Ogievskiy return want_merge_zero;
39633fa2222SVladimir Sementsov-Ogievskiy }
39733fa2222SVladimir Sementsov-Ogievskiy
398abaf8b75SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
preallocate_co_pwrite_zeroes(BlockDriverState * bs,int64_t offset,int64_t bytes,BdrvRequestFlags flags)399abaf8b75SKevin Wolf preallocate_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
400abaf8b75SKevin Wolf int64_t bytes, BdrvRequestFlags flags)
40133fa2222SVladimir Sementsov-Ogievskiy {
40233fa2222SVladimir Sementsov-Ogievskiy bool want_merge_zero =
40333fa2222SVladimir Sementsov-Ogievskiy !(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK));
40433fa2222SVladimir Sementsov-Ogievskiy if (handle_write(bs, offset, bytes, want_merge_zero)) {
40533fa2222SVladimir Sementsov-Ogievskiy return 0;
40633fa2222SVladimir Sementsov-Ogievskiy }
40733fa2222SVladimir Sementsov-Ogievskiy
40833fa2222SVladimir Sementsov-Ogievskiy return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
40933fa2222SVladimir Sementsov-Ogievskiy }
41033fa2222SVladimir Sementsov-Ogievskiy
411b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
preallocate_co_pwritev_part(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,size_t qiov_offset,BdrvRequestFlags flags)412b9b10c35SKevin Wolf preallocate_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes,
413b9b10c35SKevin Wolf QEMUIOVector *qiov, size_t qiov_offset,
414e75abedaSVladimir Sementsov-Ogievskiy BdrvRequestFlags flags)
41533fa2222SVladimir Sementsov-Ogievskiy {
41633fa2222SVladimir Sementsov-Ogievskiy handle_write(bs, offset, bytes, false);
41733fa2222SVladimir Sementsov-Ogievskiy
41833fa2222SVladimir Sementsov-Ogievskiy return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
41933fa2222SVladimir Sementsov-Ogievskiy flags);
42033fa2222SVladimir Sementsov-Ogievskiy }
42133fa2222SVladimir Sementsov-Ogievskiy
422c2b8e315SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
preallocate_co_truncate(BlockDriverState * bs,int64_t offset,bool exact,PreallocMode prealloc,BdrvRequestFlags flags,Error ** errp)42333fa2222SVladimir Sementsov-Ogievskiy preallocate_co_truncate(BlockDriverState *bs, int64_t offset,
42433fa2222SVladimir Sementsov-Ogievskiy bool exact, PreallocMode prealloc,
42533fa2222SVladimir Sementsov-Ogievskiy BdrvRequestFlags flags, Error **errp)
42633fa2222SVladimir Sementsov-Ogievskiy {
42733fa2222SVladimir Sementsov-Ogievskiy ERRP_GUARD();
42833fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
42933fa2222SVladimir Sementsov-Ogievskiy int ret;
43033fa2222SVladimir Sementsov-Ogievskiy
43133fa2222SVladimir Sementsov-Ogievskiy if (s->data_end >= 0 && offset > s->data_end) {
43233fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
433bd53086eSEmanuele Giuseppe Esposito s->file_end = bdrv_co_getlength(bs->file->bs);
43433fa2222SVladimir Sementsov-Ogievskiy if (s->file_end < 0) {
43533fa2222SVladimir Sementsov-Ogievskiy error_setg(errp, "failed to get file length");
43633fa2222SVladimir Sementsov-Ogievskiy return s->file_end;
43733fa2222SVladimir Sementsov-Ogievskiy }
43833fa2222SVladimir Sementsov-Ogievskiy }
43933fa2222SVladimir Sementsov-Ogievskiy
44033fa2222SVladimir Sementsov-Ogievskiy if (prealloc == PREALLOC_MODE_FALLOC) {
44133fa2222SVladimir Sementsov-Ogievskiy /*
44233fa2222SVladimir Sementsov-Ogievskiy * If offset <= s->file_end, the task is already done, just
44333fa2222SVladimir Sementsov-Ogievskiy * update s->data_end, to move part of "filter preallocation"
44433fa2222SVladimir Sementsov-Ogievskiy * to "preallocation requested by user".
44533fa2222SVladimir Sementsov-Ogievskiy * Otherwise just proceed to preallocate missing part.
44633fa2222SVladimir Sementsov-Ogievskiy */
44733fa2222SVladimir Sementsov-Ogievskiy if (offset <= s->file_end) {
44833fa2222SVladimir Sementsov-Ogievskiy s->data_end = offset;
44933fa2222SVladimir Sementsov-Ogievskiy return 0;
45033fa2222SVladimir Sementsov-Ogievskiy }
45133fa2222SVladimir Sementsov-Ogievskiy } else {
45233fa2222SVladimir Sementsov-Ogievskiy /*
45333fa2222SVladimir Sementsov-Ogievskiy * We have to drop our preallocation, to
45433fa2222SVladimir Sementsov-Ogievskiy * - avoid "Cannot use preallocation for shrinking files" in
45533fa2222SVladimir Sementsov-Ogievskiy * case of offset < file_end
45633fa2222SVladimir Sementsov-Ogievskiy * - give PREALLOC_MODE_OFF a chance to keep small disk
45733fa2222SVladimir Sementsov-Ogievskiy * usage
45833fa2222SVladimir Sementsov-Ogievskiy * - give PREALLOC_MODE_FULL a chance to actually write the
45933fa2222SVladimir Sementsov-Ogievskiy * whole region as user expects
46033fa2222SVladimir Sementsov-Ogievskiy */
46133fa2222SVladimir Sementsov-Ogievskiy if (s->file_end > s->data_end) {
46233fa2222SVladimir Sementsov-Ogievskiy ret = bdrv_co_truncate(bs->file, s->data_end, true,
46333fa2222SVladimir Sementsov-Ogievskiy PREALLOC_MODE_OFF, 0, errp);
46433fa2222SVladimir Sementsov-Ogievskiy if (ret < 0) {
46533fa2222SVladimir Sementsov-Ogievskiy s->file_end = ret;
46633fa2222SVladimir Sementsov-Ogievskiy error_prepend(errp, "preallocate-filter: failed to drop "
46733fa2222SVladimir Sementsov-Ogievskiy "write-zero preallocation: ");
46833fa2222SVladimir Sementsov-Ogievskiy return ret;
46933fa2222SVladimir Sementsov-Ogievskiy }
47033fa2222SVladimir Sementsov-Ogievskiy s->file_end = s->data_end;
47133fa2222SVladimir Sementsov-Ogievskiy }
47233fa2222SVladimir Sementsov-Ogievskiy }
47333fa2222SVladimir Sementsov-Ogievskiy
47433fa2222SVladimir Sementsov-Ogievskiy s->data_end = offset;
47533fa2222SVladimir Sementsov-Ogievskiy }
47633fa2222SVladimir Sementsov-Ogievskiy
47733fa2222SVladimir Sementsov-Ogievskiy ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
47833fa2222SVladimir Sementsov-Ogievskiy if (ret < 0) {
47933fa2222SVladimir Sementsov-Ogievskiy s->file_end = s->zero_start = s->data_end = ret;
48033fa2222SVladimir Sementsov-Ogievskiy return ret;
48133fa2222SVladimir Sementsov-Ogievskiy }
48233fa2222SVladimir Sementsov-Ogievskiy
48333fa2222SVladimir Sementsov-Ogievskiy if (has_prealloc_perms(bs)) {
48433fa2222SVladimir Sementsov-Ogievskiy s->file_end = s->zero_start = s->data_end = offset;
48533fa2222SVladimir Sementsov-Ogievskiy }
48633fa2222SVladimir Sementsov-Ogievskiy return 0;
48733fa2222SVladimir Sementsov-Ogievskiy }
48833fa2222SVladimir Sementsov-Ogievskiy
preallocate_co_flush(BlockDriverState * bs)48988095349SEmanuele Giuseppe Esposito static int coroutine_fn GRAPH_RDLOCK preallocate_co_flush(BlockDriverState *bs)
49033fa2222SVladimir Sementsov-Ogievskiy {
49133fa2222SVladimir Sementsov-Ogievskiy return bdrv_co_flush(bs->file->bs);
49233fa2222SVladimir Sementsov-Ogievskiy }
49333fa2222SVladimir Sementsov-Ogievskiy
4948ab8140aSKevin Wolf static int64_t coroutine_fn GRAPH_RDLOCK
preallocate_co_getlength(BlockDriverState * bs)4958ab8140aSKevin Wolf preallocate_co_getlength(BlockDriverState *bs)
49633fa2222SVladimir Sementsov-Ogievskiy {
49733fa2222SVladimir Sementsov-Ogievskiy int64_t ret;
49833fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
49933fa2222SVladimir Sementsov-Ogievskiy
50033fa2222SVladimir Sementsov-Ogievskiy if (s->data_end >= 0) {
50133fa2222SVladimir Sementsov-Ogievskiy return s->data_end;
50233fa2222SVladimir Sementsov-Ogievskiy }
50333fa2222SVladimir Sementsov-Ogievskiy
504c86422c5SEmanuele Giuseppe Esposito ret = bdrv_co_getlength(bs->file->bs);
50533fa2222SVladimir Sementsov-Ogievskiy
50633fa2222SVladimir Sementsov-Ogievskiy if (has_prealloc_perms(bs)) {
50733fa2222SVladimir Sementsov-Ogievskiy s->file_end = s->zero_start = s->data_end = ret;
50833fa2222SVladimir Sementsov-Ogievskiy }
50933fa2222SVladimir Sementsov-Ogievskiy
51033fa2222SVladimir Sementsov-Ogievskiy return ret;
51133fa2222SVladimir Sementsov-Ogievskiy }
51233fa2222SVladimir Sementsov-Ogievskiy
513*1f051dcbSKevin Wolf static int GRAPH_RDLOCK
preallocate_drop_resize(BlockDriverState * bs,Error ** errp)514*1f051dcbSKevin Wolf preallocate_drop_resize(BlockDriverState *bs, Error **errp)
51533fa2222SVladimir Sementsov-Ogievskiy {
51633fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
517edcce17bSKevin Wolf int ret;
51833fa2222SVladimir Sementsov-Ogievskiy
519edcce17bSKevin Wolf if (s->data_end < 0) {
520edcce17bSKevin Wolf return 0;
52133fa2222SVladimir Sementsov-Ogievskiy }
52233fa2222SVladimir Sementsov-Ogievskiy
523edcce17bSKevin Wolf /*
524edcce17bSKevin Wolf * Before switching children to be read-only, truncate them to remove
525edcce17bSKevin Wolf * the preallocation and let them have the real size.
526edcce17bSKevin Wolf */
527edcce17bSKevin Wolf ret = preallocate_truncate_to_real_size(bs, errp);
528edcce17bSKevin Wolf if (ret < 0) {
529edcce17bSKevin Wolf return ret;
530edcce17bSKevin Wolf }
531edcce17bSKevin Wolf
532edcce17bSKevin Wolf /*
533edcce17bSKevin Wolf * We'll drop our permissions and will allow other users to take write and
534edcce17bSKevin Wolf * resize permissions (see preallocate_child_perm). Anyone will be able to
535edcce17bSKevin Wolf * change the child, so mark all states invalid. We'll regain control if a
536edcce17bSKevin Wolf * parent requests write access again.
537edcce17bSKevin Wolf */
538edcce17bSKevin Wolf s->data_end = s->file_end = s->zero_start = -EINVAL;
539edcce17bSKevin Wolf
540edcce17bSKevin Wolf bdrv_child_refresh_perms(bs, bs->file, NULL);
541edcce17bSKevin Wolf
54233fa2222SVladimir Sementsov-Ogievskiy return 0;
54333fa2222SVladimir Sementsov-Ogievskiy }
54433fa2222SVladimir Sementsov-Ogievskiy
preallocate_drop_resize_bh(void * opaque)545edcce17bSKevin Wolf static void preallocate_drop_resize_bh(void *opaque)
546edcce17bSKevin Wolf {
547*1f051dcbSKevin Wolf GLOBAL_STATE_CODE();
548*1f051dcbSKevin Wolf GRAPH_RDLOCK_GUARD_MAINLOOP();
549*1f051dcbSKevin Wolf
550edcce17bSKevin Wolf /*
551edcce17bSKevin Wolf * In case of errors, we'll simply keep the exclusive lock on the image
552edcce17bSKevin Wolf * indefinitely.
553edcce17bSKevin Wolf */
554edcce17bSKevin Wolf preallocate_drop_resize(opaque, NULL);
555edcce17bSKevin Wolf }
556edcce17bSKevin Wolf
55779a55866SKevin Wolf static void GRAPH_RDLOCK
preallocate_set_perm(BlockDriverState * bs,uint64_t perm,uint64_t shared)55879a55866SKevin Wolf preallocate_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
55933fa2222SVladimir Sementsov-Ogievskiy {
56033fa2222SVladimir Sementsov-Ogievskiy BDRVPreallocateState *s = bs->opaque;
56133fa2222SVladimir Sementsov-Ogievskiy
56233fa2222SVladimir Sementsov-Ogievskiy if (can_write_resize(perm)) {
563edcce17bSKevin Wolf qemu_bh_cancel(s->drop_resize_bh);
56433fa2222SVladimir Sementsov-Ogievskiy if (s->data_end < 0) {
56533fa2222SVladimir Sementsov-Ogievskiy s->data_end = s->file_end = s->zero_start =
566edcce17bSKevin Wolf bs->file->bs->total_sectors * BDRV_SECTOR_SIZE;
56733fa2222SVladimir Sementsov-Ogievskiy }
56833fa2222SVladimir Sementsov-Ogievskiy } else {
569edcce17bSKevin Wolf qemu_bh_schedule(s->drop_resize_bh);
57033fa2222SVladimir Sementsov-Ogievskiy }
57133fa2222SVladimir Sementsov-Ogievskiy }
57233fa2222SVladimir Sementsov-Ogievskiy
preallocate_child_perm(BlockDriverState * bs,BdrvChild * c,BdrvChildRole role,BlockReopenQueue * reopen_queue,uint64_t perm,uint64_t shared,uint64_t * nperm,uint64_t * nshared)57333fa2222SVladimir Sementsov-Ogievskiy static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c,
57433fa2222SVladimir Sementsov-Ogievskiy BdrvChildRole role, BlockReopenQueue *reopen_queue,
57533fa2222SVladimir Sementsov-Ogievskiy uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared)
57633fa2222SVladimir Sementsov-Ogievskiy {
577edcce17bSKevin Wolf BDRVPreallocateState *s = bs->opaque;
578edcce17bSKevin Wolf
57933fa2222SVladimir Sementsov-Ogievskiy bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
58033fa2222SVladimir Sementsov-Ogievskiy
581edcce17bSKevin Wolf /*
582edcce17bSKevin Wolf * We need exclusive write and resize permissions on the child not only when
583edcce17bSKevin Wolf * the parent can write to it, but also after the parent gave up write
584edcce17bSKevin Wolf * permissions until preallocate_drop_resize() has completed.
585edcce17bSKevin Wolf */
586edcce17bSKevin Wolf if (can_write_resize(perm) || s->data_end != -EINVAL) {
58733fa2222SVladimir Sementsov-Ogievskiy *nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
58833fa2222SVladimir Sementsov-Ogievskiy
58933fa2222SVladimir Sementsov-Ogievskiy /*
59033fa2222SVladimir Sementsov-Ogievskiy * Don't share, to keep our states s->file_end, s->data_end and
59133fa2222SVladimir Sementsov-Ogievskiy * s->zero_start valid.
59233fa2222SVladimir Sementsov-Ogievskiy */
59333fa2222SVladimir Sementsov-Ogievskiy *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
59433fa2222SVladimir Sementsov-Ogievskiy }
59533fa2222SVladimir Sementsov-Ogievskiy }
59633fa2222SVladimir Sementsov-Ogievskiy
5979ea473fbSKevin Wolf static BlockDriver bdrv_preallocate_filter = {
59833fa2222SVladimir Sementsov-Ogievskiy .format_name = "preallocate",
59933fa2222SVladimir Sementsov-Ogievskiy .instance_size = sizeof(BDRVPreallocateState),
60033fa2222SVladimir Sementsov-Ogievskiy
601c86422c5SEmanuele Giuseppe Esposito .bdrv_co_getlength = preallocate_co_getlength,
60233fa2222SVladimir Sementsov-Ogievskiy .bdrv_open = preallocate_open,
60333fa2222SVladimir Sementsov-Ogievskiy .bdrv_close = preallocate_close,
60433fa2222SVladimir Sementsov-Ogievskiy
60533fa2222SVladimir Sementsov-Ogievskiy .bdrv_reopen_prepare = preallocate_reopen_prepare,
60633fa2222SVladimir Sementsov-Ogievskiy .bdrv_reopen_commit = preallocate_reopen_commit,
60733fa2222SVladimir Sementsov-Ogievskiy .bdrv_reopen_abort = preallocate_reopen_abort,
60833fa2222SVladimir Sementsov-Ogievskiy
60933fa2222SVladimir Sementsov-Ogievskiy .bdrv_co_preadv_part = preallocate_co_preadv_part,
61033fa2222SVladimir Sementsov-Ogievskiy .bdrv_co_pwritev_part = preallocate_co_pwritev_part,
61133fa2222SVladimir Sementsov-Ogievskiy .bdrv_co_pwrite_zeroes = preallocate_co_pwrite_zeroes,
61233fa2222SVladimir Sementsov-Ogievskiy .bdrv_co_pdiscard = preallocate_co_pdiscard,
61333fa2222SVladimir Sementsov-Ogievskiy .bdrv_co_flush = preallocate_co_flush,
61433fa2222SVladimir Sementsov-Ogievskiy .bdrv_co_truncate = preallocate_co_truncate,
61533fa2222SVladimir Sementsov-Ogievskiy
61633fa2222SVladimir Sementsov-Ogievskiy .bdrv_set_perm = preallocate_set_perm,
61733fa2222SVladimir Sementsov-Ogievskiy .bdrv_child_perm = preallocate_child_perm,
61833fa2222SVladimir Sementsov-Ogievskiy
61933fa2222SVladimir Sementsov-Ogievskiy .is_filter = true,
62033fa2222SVladimir Sementsov-Ogievskiy };
62133fa2222SVladimir Sementsov-Ogievskiy
bdrv_preallocate_init(void)62233fa2222SVladimir Sementsov-Ogievskiy static void bdrv_preallocate_init(void)
62333fa2222SVladimir Sementsov-Ogievskiy {
62433fa2222SVladimir Sementsov-Ogievskiy bdrv_register(&bdrv_preallocate_filter);
62533fa2222SVladimir Sementsov-Ogievskiy }
62633fa2222SVladimir Sementsov-Ogievskiy
62733fa2222SVladimir Sementsov-Ogievskiy block_init(bdrv_preallocate_init);
628