xref: /qemu/block/preallocate.c (revision 1f051dcb)
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 
1761f051dcbSKevin Wolf static int GRAPH_RDLOCK
preallocate_truncate_to_real_size(BlockDriverState * bs,Error ** errp)1771f051dcbSKevin 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 
2081f051dcbSKevin Wolf     GLOBAL_STATE_CODE();
2091f051dcbSKevin Wolf     GRAPH_RDLOCK_GUARD_MAINLOOP();
2101f051dcbSKevin 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 
2341f051dcbSKevin Wolf     GLOBAL_STATE_CODE();
2351f051dcbSKevin Wolf     GRAPH_RDLOCK_GUARD_MAINLOOP();
2361f051dcbSKevin 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)2971f051dcbSKevin 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 
5131f051dcbSKevin Wolf static int GRAPH_RDLOCK
preallocate_drop_resize(BlockDriverState * bs,Error ** errp)5141f051dcbSKevin 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 {
5471f051dcbSKevin Wolf     GLOBAL_STATE_CODE();
5481f051dcbSKevin Wolf     GRAPH_RDLOCK_GUARD_MAINLOOP();
5491f051dcbSKevin 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