mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-10-10' into staging
Block patches: - Parallelized request handling for qcow2 - Backup job refactoring to use a filter node instead of before-write notifiers - Add discard accounting information to file-posix nodes - Allow trivial reopening of nbd nodes - Some iotest fixes # gpg: Signature made Thu 10 Oct 2019 12:40:34 BST # gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40 # gpg: issuer "mreitz@redhat.com" # gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full] # Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40 * remotes/maxreitz/tags/pull-block-2019-10-10: (36 commits) iotests/162: Fix for newer Linux 5.3+ tests: fix I/O test for hosts defaulting to LUKSv2 nbd: add empty .bdrv_reopen_prepare block/backup: use backup-top instead of write notifiers block: introduce backup-top filter driver block/block-copy: split block_copy_set_callbacks function block/backup: move write_flags calculation inside backup_job_create block/backup: move in-flight requests handling from backup to block-copy iotests: Use stat -c %b in 125 iotests: Disable 125 on broken XFS versions iotests: Fix 125 for growth_mode = metadata qapi: query-blockstat: add driver specific file-posix stats file-posix: account discard operations scsi: account unmap operations scsi: move unmap error checking to the complete callback scsi: store unmap offset and nb_sectors in request struct ide: account UNMAP (TRIM) operations block: add empty account cookie type qapi: add unmap to BlockDeviceStats qapi: group BlockDeviceStats fields ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -5155,6 +5155,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv || !drv->bdrv_get_specific_stats) {
|
||||
return NULL;
|
||||
}
|
||||
return drv->bdrv_get_specific_stats(bs);
|
||||
}
|
||||
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
|
||||
{
|
||||
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
|
||||
@@ -5164,14 +5173,35 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
|
||||
bs->drv->bdrv_debug_event(bs, event);
|
||||
}
|
||||
|
||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag)
|
||||
static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
|
||||
bs = bs->file ? bs->file->bs : NULL;
|
||||
if (bs->file) {
|
||||
bs = bs->file->bs;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bs->drv->is_filter && bs->backing) {
|
||||
bs = bs->backing->bs;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) {
|
||||
assert(bs->drv->bdrv_debug_remove_breakpoint);
|
||||
return bs;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag)
|
||||
{
|
||||
bs = bdrv_find_debug_node(bs);
|
||||
if (bs) {
|
||||
return bs->drv->bdrv_debug_breakpoint(bs, event, tag);
|
||||
}
|
||||
|
||||
@@ -5180,11 +5210,8 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
|
||||
int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) {
|
||||
bs = bs->file ? bs->file->bs : NULL;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) {
|
||||
bs = bdrv_find_debug_node(bs);
|
||||
if (bs) {
|
||||
return bs->drv->bdrv_debug_remove_breakpoint(bs, tag);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,13 @@ block-obj-y += write-threshold.o
|
||||
block-obj-y += backup.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
block-obj-y += throttle.o copy-on-read.o
|
||||
block-obj-y += block-copy.o
|
||||
|
||||
block-obj-y += crypto.o
|
||||
|
||||
block-obj-y += aio_task.o
|
||||
block-obj-y += backup-top.o
|
||||
|
||||
common-obj-y += stream.o
|
||||
|
||||
nfs.o-libs := $(LIBNFS_LIBS)
|
||||
|
||||
@@ -195,6 +195,10 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
|
||||
assert(cookie->type < BLOCK_MAX_IOTYPE);
|
||||
|
||||
if (cookie->type == BLOCK_ACCT_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&stats->lock);
|
||||
|
||||
if (failed) {
|
||||
@@ -217,6 +221,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&stats->lock);
|
||||
|
||||
cookie->type = BLOCK_ACCT_NONE;
|
||||
}
|
||||
|
||||
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Aio tasks loops
|
||||
*
|
||||
* Copyright (c) 2019 Virtuozzo International GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/aio.h"
|
||||
#include "block/aio_task.h"
|
||||
|
||||
struct AioTaskPool {
|
||||
Coroutine *main_co;
|
||||
int status;
|
||||
int max_busy_tasks;
|
||||
int busy_tasks;
|
||||
bool waiting;
|
||||
};
|
||||
|
||||
static void coroutine_fn aio_task_co(void *opaque)
|
||||
{
|
||||
AioTask *task = opaque;
|
||||
AioTaskPool *pool = task->pool;
|
||||
|
||||
assert(pool->busy_tasks < pool->max_busy_tasks);
|
||||
pool->busy_tasks++;
|
||||
|
||||
task->ret = task->func(task);
|
||||
|
||||
pool->busy_tasks--;
|
||||
|
||||
if (task->ret < 0 && pool->status == 0) {
|
||||
pool->status = task->ret;
|
||||
}
|
||||
|
||||
g_free(task);
|
||||
|
||||
if (pool->waiting) {
|
||||
pool->waiting = false;
|
||||
aio_co_wake(pool->main_co);
|
||||
}
|
||||
}
|
||||
|
||||
void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool)
|
||||
{
|
||||
assert(pool->busy_tasks > 0);
|
||||
assert(qemu_coroutine_self() == pool->main_co);
|
||||
|
||||
pool->waiting = true;
|
||||
qemu_coroutine_yield();
|
||||
|
||||
assert(!pool->waiting);
|
||||
assert(pool->busy_tasks < pool->max_busy_tasks);
|
||||
}
|
||||
|
||||
void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool)
|
||||
{
|
||||
if (pool->busy_tasks < pool->max_busy_tasks) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_task_pool_wait_one(pool);
|
||||
}
|
||||
|
||||
void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool)
|
||||
{
|
||||
while (pool->busy_tasks > 0) {
|
||||
aio_task_pool_wait_one(pool);
|
||||
}
|
||||
}
|
||||
|
||||
void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task)
|
||||
{
|
||||
aio_task_pool_wait_slot(pool);
|
||||
|
||||
task->pool = pool;
|
||||
qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task));
|
||||
}
|
||||
|
||||
AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks)
|
||||
{
|
||||
AioTaskPool *pool = g_new0(AioTaskPool, 1);
|
||||
|
||||
pool->main_co = qemu_coroutine_self();
|
||||
pool->max_busy_tasks = max_busy_tasks;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
void aio_task_pool_free(AioTaskPool *pool)
|
||||
{
|
||||
g_free(pool);
|
||||
}
|
||||
|
||||
int aio_task_pool_status(AioTaskPool *pool)
|
||||
{
|
||||
if (!pool) {
|
||||
return 0; /* Sugar for lazy allocation of aio pool */
|
||||
}
|
||||
|
||||
return pool->status;
|
||||
}
|
||||
|
||||
bool aio_task_pool_empty(AioTaskPool *pool)
|
||||
{
|
||||
return pool->busy_tasks == 0;
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* backup-top filter driver
|
||||
*
|
||||
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
|
||||
* some node, and before each write it copies _old_ data to the target node.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
|
||||
*
|
||||
* Author:
|
||||
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "block/block-copy.h"
|
||||
|
||||
#include "block/backup-top.h"
|
||||
|
||||
typedef struct BDRVBackupTopState {
|
||||
BlockCopyState *bcs;
|
||||
BdrvChild *target;
|
||||
bool active;
|
||||
} BDRVBackupTopState;
|
||||
|
||||
static coroutine_fn int backup_top_co_preadv(
|
||||
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size);
|
||||
uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size);
|
||||
|
||||
return block_copy(s->bcs, off, end - off, NULL);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(bs->backing, offset, bytes);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) {
|
||||
int ret = backup_top_cbw(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
if (!bs->backing) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_co_flush(bs->backing->bs);
|
||||
}
|
||||
|
||||
static void backup_top_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->backing == NULL) {
|
||||
/*
|
||||
* we can be here after failed bdrv_attach_child in
|
||||
* bdrv_set_backing_hd
|
||||
*/
|
||||
return;
|
||||
}
|
||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
bs->backing->bs->filename);
|
||||
}
|
||||
|
||||
static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
|
||||
if (!s->active) {
|
||||
/*
|
||||
* The filter node may be in process of bdrv_append(), which firstly do
|
||||
* bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
|
||||
* we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
|
||||
* let's require nothing during bdrv_append() and refresh permissions
|
||||
* after it (see bdrv_backup_top_append()).
|
||||
*/
|
||||
*nperm = 0;
|
||||
*nshared = BLK_PERM_ALL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == &child_file) {
|
||||
/*
|
||||
* Target child
|
||||
*
|
||||
* Share write to target (child_file), to not interfere
|
||||
* with guest writes to its disk which may be in target backing chain.
|
||||
*/
|
||||
*nshared = BLK_PERM_ALL;
|
||||
*nperm = BLK_PERM_WRITE;
|
||||
} else {
|
||||
/* Source child */
|
||||
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
|
||||
nperm, nshared);
|
||||
|
||||
if (perm & BLK_PERM_WRITE) {
|
||||
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
|
||||
}
|
||||
*nshared &= ~BLK_PERM_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
BlockDriver bdrv_backup_top_filter = {
|
||||
.format_name = "backup-top",
|
||||
.instance_size = sizeof(BDRVBackupTopState),
|
||||
|
||||
.bdrv_co_preadv = backup_top_co_preadv,
|
||||
.bdrv_co_pwritev = backup_top_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = backup_top_co_pdiscard,
|
||||
.bdrv_co_flush = backup_top_co_flush,
|
||||
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||
|
||||
.bdrv_refresh_filename = backup_top_refresh_filename,
|
||||
|
||||
.bdrv_child_perm = backup_top_child_perm,
|
||||
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
uint64_t cluster_size,
|
||||
BdrvRequestFlags write_flags,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BDRVBackupTopState *state;
|
||||
BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
|
||||
filter_node_name,
|
||||
BDRV_O_RDWR, errp);
|
||||
|
||||
if (!top) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
top->total_sectors = source->total_sectors;
|
||||
top->opaque = state = g_new0(BDRVBackupTopState, 1);
|
||||
|
||||
bdrv_ref(target);
|
||||
state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
|
||||
if (!state->target) {
|
||||
bdrv_unref(target);
|
||||
bdrv_unref(top);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bdrv_drained_begin(source);
|
||||
|
||||
bdrv_ref(top);
|
||||
bdrv_append(top, source, &local_err);
|
||||
if (local_err) {
|
||||
error_prepend(&local_err, "Cannot append backup-top filter: ");
|
||||
goto append_failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* bdrv_append() finished successfully, now we can require permissions
|
||||
* we want.
|
||||
*/
|
||||
state->active = true;
|
||||
bdrv_child_refresh_perms(top, top->backing, &local_err);
|
||||
if (local_err) {
|
||||
error_prepend(&local_err,
|
||||
"Cannot set permissions for backup-top filter: ");
|
||||
goto failed_after_append;
|
||||
}
|
||||
|
||||
state->bcs = block_copy_state_new(top->backing, state->target,
|
||||
cluster_size, write_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_prepend(&local_err, "Cannot create block-copy-state: ");
|
||||
goto failed_after_append;
|
||||
}
|
||||
*bcs = state->bcs;
|
||||
|
||||
bdrv_drained_end(source);
|
||||
|
||||
return top;
|
||||
|
||||
failed_after_append:
|
||||
state->active = false;
|
||||
bdrv_backup_top_drop(top);
|
||||
|
||||
append_failed:
|
||||
bdrv_drained_end(source);
|
||||
bdrv_unref_child(top, state->target);
|
||||
bdrv_unref(top);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_backup_top_drop(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
s->active = false;
|
||||
bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
|
||||
bdrv_replace_node(bs, backing_bs(bs), &error_abort);
|
||||
bdrv_set_backing_hd(bs, NULL, &error_abort);
|
||||
|
||||
bdrv_drained_end(bs);
|
||||
|
||||
bdrv_unref(bs);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* backup-top filter driver
|
||||
*
|
||||
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
|
||||
* some node, and before each write it copies _old_ data to the target node.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
|
||||
*
|
||||
* Author:
|
||||
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BACKUP_TOP_H
|
||||
#define BACKUP_TOP_H
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "block/block-copy.h"
|
||||
|
||||
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
uint64_t cluster_size,
|
||||
BdrvRequestFlags write_flags,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp);
|
||||
void bdrv_backup_top_drop(BlockDriverState *bs);
|
||||
|
||||
#endif /* BACKUP_TOP_H */
|
||||
+78
-365
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* block_copy API
|
||||
*
|
||||
* Copyright (C) 2013 Proxmox Server Solutions
|
||||
* Copyright (c) 2019 Virtuozzo International GmbH.
|
||||
*
|
||||
* Authors:
|
||||
* Dietmar Maurer (dietmar@proxmox.com)
|
||||
* Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block-copy.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s,
|
||||
int64_t start,
|
||||
int64_t end)
|
||||
{
|
||||
BlockCopyInFlightReq *req;
|
||||
bool waited;
|
||||
|
||||
do {
|
||||
waited = false;
|
||||
QLIST_FOREACH(req, &s->inflight_reqs, list) {
|
||||
if (end > req->start_byte && start < req->end_byte) {
|
||||
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||
waited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (waited);
|
||||
}
|
||||
|
||||
static void block_copy_inflight_req_begin(BlockCopyState *s,
|
||||
BlockCopyInFlightReq *req,
|
||||
int64_t start, int64_t end)
|
||||
{
|
||||
req->start_byte = start;
|
||||
req->end_byte = end;
|
||||
qemu_co_queue_init(&req->wait_queue);
|
||||
QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
|
||||
}
|
||||
|
||||
static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req)
|
||||
{
|
||||
QLIST_REMOVE(req, list);
|
||||
qemu_co_queue_restart_all(&req->wait_queue);
|
||||
}
|
||||
|
||||
void block_copy_state_free(BlockCopyState *s)
|
||||
{
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_release_dirty_bitmap(s->source->bs, s->copy_bitmap);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
int64_t cluster_size,
|
||||
BdrvRequestFlags write_flags, Error **errp)
|
||||
{
|
||||
BlockCopyState *s;
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
uint32_t max_transfer =
|
||||
MIN_NON_ZERO(INT_MAX, MIN_NON_ZERO(source->bs->bl.max_transfer,
|
||||
target->bs->bl.max_transfer));
|
||||
|
||||
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
|
||||
errp);
|
||||
if (!copy_bitmap) {
|
||||
return NULL;
|
||||
}
|
||||
bdrv_disable_dirty_bitmap(copy_bitmap);
|
||||
|
||||
s = g_new(BlockCopyState, 1);
|
||||
*s = (BlockCopyState) {
|
||||
.source = source,
|
||||
.target = target,
|
||||
.copy_bitmap = copy_bitmap,
|
||||
.cluster_size = cluster_size,
|
||||
.len = bdrv_dirty_bitmap_size(copy_bitmap),
|
||||
.write_flags = write_flags,
|
||||
};
|
||||
|
||||
s->copy_range_size = QEMU_ALIGN_DOWN(max_transfer, cluster_size),
|
||||
/*
|
||||
* Set use_copy_range, consider the following:
|
||||
* 1. Compression is not supported for copy_range.
|
||||
* 2. copy_range does not respect max_transfer (it's a TODO), so we factor
|
||||
* that in here. If max_transfer is smaller than the job->cluster_size,
|
||||
* we do not use copy_range (in that case it's zero after aligning down
|
||||
* above).
|
||||
*/
|
||||
s->use_copy_range =
|
||||
!(write_flags & BDRV_REQ_WRITE_COMPRESSED) && s->copy_range_size > 0;
|
||||
|
||||
QLIST_INIT(&s->inflight_reqs);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void block_copy_set_callbacks(
|
||||
BlockCopyState *s,
|
||||
ProgressBytesCallbackFunc progress_bytes_callback,
|
||||
ProgressResetCallbackFunc progress_reset_callback,
|
||||
void *progress_opaque)
|
||||
{
|
||||
s->progress_bytes_callback = progress_bytes_callback;
|
||||
s->progress_reset_callback = progress_reset_callback;
|
||||
s->progress_opaque = progress_opaque;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy range to target with a bounce buffer and return the bytes copied. If
|
||||
* error occurred, return a negative error number
|
||||
*/
|
||||
static int coroutine_fn block_copy_with_bounce_buffer(BlockCopyState *s,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool *error_is_read,
|
||||
void **bounce_buffer)
|
||||
{
|
||||
int ret;
|
||||
int nbytes;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
|
||||
nbytes = MIN(s->cluster_size, s->len - start);
|
||||
if (!*bounce_buffer) {
|
||||
*bounce_buffer = qemu_blockalign(s->source->bs, s->cluster_size);
|
||||
}
|
||||
|
||||
ret = bdrv_co_pread(s->source, start, nbytes, *bounce_buffer, 0);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_with_bounce_buffer_read_fail(s, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = true;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_co_pwrite(s->target, start, nbytes, *bounce_buffer,
|
||||
s->write_flags);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_with_bounce_buffer_write_fail(s, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
fail:
|
||||
bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy range to target and return the bytes copied. If error occurred, return a
|
||||
* negative error number.
|
||||
*/
|
||||
static int coroutine_fn block_copy_with_offload(BlockCopyState *s,
|
||||
int64_t start,
|
||||
int64_t end)
|
||||
{
|
||||
int ret;
|
||||
int nr_clusters;
|
||||
int nbytes;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(s->copy_range_size, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
|
||||
nbytes = MIN(s->copy_range_size, MIN(end, s->len) - start);
|
||||
nr_clusters = DIV_ROUND_UP(nbytes, s->cluster_size);
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, start,
|
||||
s->cluster_size * nr_clusters);
|
||||
ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes,
|
||||
0, s->write_flags);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_with_offload_fail(s, start, ret);
|
||||
bdrv_set_dirty_bitmap(s->copy_bitmap, start,
|
||||
s->cluster_size * nr_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the cluster starting at offset is allocated or not.
|
||||
* return via pnum the number of contiguous clusters sharing this allocation.
|
||||
*/
|
||||
static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
|
||||
int64_t *pnum)
|
||||
{
|
||||
BlockDriverState *bs = s->source->bs;
|
||||
int64_t count, total_count = 0;
|
||||
int64_t bytes = s->len - offset;
|
||||
int ret;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
|
||||
while (true) {
|
||||
ret = bdrv_is_allocated(bs, offset, bytes, &count);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
total_count += count;
|
||||
|
||||
if (ret || count == 0) {
|
||||
/*
|
||||
* ret: partial segment(s) are considered allocated.
|
||||
* otherwise: unallocated tail is treated as an entire segment.
|
||||
*/
|
||||
*pnum = DIV_ROUND_UP(total_count, s->cluster_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Unallocated segment(s) with uncertain following segment(s) */
|
||||
if (total_count >= s->cluster_size) {
|
||||
*pnum = total_count / s->cluster_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset += count;
|
||||
bytes -= count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset bits in copy_bitmap starting at offset if they represent unallocated
|
||||
* data in the image. May reset subsequent contiguous bits.
|
||||
* @return 0 when the cluster at @offset was unallocated,
|
||||
* 1 otherwise, and -ret on error.
|
||||
*/
|
||||
int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
||||
int64_t offset, int64_t *count)
|
||||
{
|
||||
int ret;
|
||||
int64_t clusters, bytes;
|
||||
|
||||
ret = block_copy_is_cluster_allocated(s, offset, &clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes = clusters * s->cluster_size;
|
||||
|
||||
if (!ret) {
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||
s->progress_reset_callback(s->progress_opaque);
|
||||
}
|
||||
|
||||
*count = bytes;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn block_copy(BlockCopyState *s,
|
||||
int64_t start, uint64_t bytes,
|
||||
bool *error_is_read)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t end = bytes + start; /* bytes */
|
||||
void *bounce_buffer = NULL;
|
||||
int64_t status_bytes;
|
||||
BlockCopyInFlightReq req;
|
||||
|
||||
/*
|
||||
* block_copy() user is responsible for keeping source and target in same
|
||||
* aio context
|
||||
*/
|
||||
assert(bdrv_get_aio_context(s->source->bs) ==
|
||||
bdrv_get_aio_context(s->target->bs));
|
||||
|
||||
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(end, s->cluster_size));
|
||||
|
||||
block_copy_wait_inflight_reqs(s, start, bytes);
|
||||
block_copy_inflight_req_begin(s, &req, start, end);
|
||||
|
||||
while (start < end) {
|
||||
int64_t dirty_end;
|
||||
|
||||
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) {
|
||||
trace_block_copy_skip(s, start);
|
||||
start += s->cluster_size;
|
||||
continue; /* already copied */
|
||||
}
|
||||
|
||||
dirty_end = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
|
||||
(end - start));
|
||||
if (dirty_end < 0) {
|
||||
dirty_end = end;
|
||||
}
|
||||
|
||||
if (s->skip_unallocated) {
|
||||
ret = block_copy_reset_unallocated(s, start, &status_bytes);
|
||||
if (ret == 0) {
|
||||
trace_block_copy_skip_range(s, start, status_bytes);
|
||||
start += status_bytes;
|
||||
continue;
|
||||
}
|
||||
/* Clamp to known allocated region */
|
||||
dirty_end = MIN(dirty_end, start + status_bytes);
|
||||
}
|
||||
|
||||
trace_block_copy_process(s, start);
|
||||
|
||||
if (s->use_copy_range) {
|
||||
ret = block_copy_with_offload(s, start, dirty_end);
|
||||
if (ret < 0) {
|
||||
s->use_copy_range = false;
|
||||
}
|
||||
}
|
||||
if (!s->use_copy_range) {
|
||||
ret = block_copy_with_bounce_buffer(s, start, dirty_end,
|
||||
error_is_read, &bounce_buffer);
|
||||
}
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
start += ret;
|
||||
s->progress_bytes_callback(ret, s->progress_opaque);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (bounce_buffer) {
|
||||
qemu_vfree(bounce_buffer);
|
||||
}
|
||||
|
||||
block_copy_inflight_req_end(&req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
+53
-1
@@ -161,6 +161,11 @@ typedef struct BDRVRawState {
|
||||
bool needs_alignment;
|
||||
bool drop_cache;
|
||||
bool check_cache_dropped;
|
||||
struct {
|
||||
uint64_t discard_nb_ok;
|
||||
uint64_t discard_nb_failed;
|
||||
uint64_t discard_bytes_ok;
|
||||
} stats;
|
||||
|
||||
PRManager *pr_mgr;
|
||||
} BDRVRawState;
|
||||
@@ -2660,11 +2665,22 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs,
|
||||
#endif /* !__linux__ */
|
||||
}
|
||||
|
||||
static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret)
|
||||
{
|
||||
if (ret) {
|
||||
s->stats.discard_nb_failed++;
|
||||
} else {
|
||||
s->stats.discard_nb_ok++;
|
||||
s->stats.discard_bytes_ok += nbytes;
|
||||
}
|
||||
}
|
||||
|
||||
static coroutine_fn int
|
||||
raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawPosixAIOData acb;
|
||||
int ret;
|
||||
|
||||
acb = (RawPosixAIOData) {
|
||||
.bs = bs,
|
||||
@@ -2678,7 +2694,9 @@ raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev)
|
||||
acb.aio_type |= QEMU_AIO_BLKDEV;
|
||||
}
|
||||
|
||||
return raw_thread_pool_submit(bs, handle_aiocb_discard, &acb);
|
||||
ret = raw_thread_pool_submit(bs, handle_aiocb_discard, &acb);
|
||||
raw_account_discard(s, bytes, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int
|
||||
@@ -2735,6 +2753,36 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
return (BlockStatsSpecificFile) {
|
||||
.discard_nb_ok = s->stats.discard_nb_ok,
|
||||
.discard_nb_failed = s->stats.discard_nb_failed,
|
||||
.discard_bytes_ok = s->stats.discard_bytes_ok,
|
||||
};
|
||||
}
|
||||
|
||||
static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
|
||||
{
|
||||
BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
|
||||
|
||||
stats->driver = BLOCKDEV_DRIVER_FILE;
|
||||
stats->u.file = get_blockstats_specific_file(bs);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs)
|
||||
{
|
||||
BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
|
||||
|
||||
stats->driver = BLOCKDEV_DRIVER_HOST_DEVICE;
|
||||
stats->u.host_device = get_blockstats_specific_file(bs);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
static QemuOptsList raw_create_opts = {
|
||||
.name = "raw-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
|
||||
@@ -2942,6 +2990,7 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_get_info = raw_get_info,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
.bdrv_get_specific_stats = raw_get_specific_stats,
|
||||
.bdrv_check_perm = raw_check_perm,
|
||||
.bdrv_set_perm = raw_set_perm,
|
||||
.bdrv_abort_perm_update = raw_abort_perm_update,
|
||||
@@ -3301,10 +3350,12 @@ static int fd_open(BlockDriverState *bs)
|
||||
static coroutine_fn int
|
||||
hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = fd_open(bs);
|
||||
if (ret < 0) {
|
||||
raw_account_discard(s, bytes, ret);
|
||||
return ret;
|
||||
}
|
||||
return raw_do_pdiscard(bs, offset, bytes, true);
|
||||
@@ -3418,6 +3469,7 @@ static BlockDriver bdrv_host_device = {
|
||||
.bdrv_get_info = raw_get_info,
|
||||
.bdrv_get_allocated_file_size
|
||||
= raw_get_allocated_file_size,
|
||||
.bdrv_get_specific_stats = hdev_get_specific_stats,
|
||||
.bdrv_check_perm = raw_check_perm,
|
||||
.bdrv_set_perm = raw_set_perm,
|
||||
.bdrv_abort_perm_update = raw_abort_perm_update,
|
||||
|
||||
+15
@@ -1158,6 +1158,18 @@ static int coroutine_fn nbd_client_co_block_status(
|
||||
BDRV_BLOCK_OFFSET_VALID;
|
||||
}
|
||||
|
||||
static int nbd_client_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque;
|
||||
|
||||
if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) {
|
||||
error_setg(errp, "Can't reopen read-only NBD mount as read/write");
|
||||
return -EACCES;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nbd_client_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
@@ -1798,6 +1810,7 @@ static BlockDriver bdrv_nbd = {
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_reopen_prepare = nbd_client_reopen_prepare,
|
||||
.bdrv_co_preadv = nbd_client_co_preadv,
|
||||
.bdrv_co_pwritev = nbd_client_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
|
||||
@@ -1820,6 +1833,7 @@ static BlockDriver bdrv_nbd_tcp = {
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_reopen_prepare = nbd_client_reopen_prepare,
|
||||
.bdrv_co_preadv = nbd_client_co_preadv,
|
||||
.bdrv_co_pwritev = nbd_client_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
|
||||
@@ -1842,6 +1856,7 @@ static BlockDriver bdrv_nbd_unix = {
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_reopen_prepare = nbd_client_reopen_prepare,
|
||||
.bdrv_co_preadv = nbd_client_co_preadv,
|
||||
.bdrv_co_pwritev = nbd_client_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
|
||||
|
||||
@@ -440,24 +440,30 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
|
||||
ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP];
|
||||
ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP];
|
||||
|
||||
ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP];
|
||||
|
||||
ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
ds->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP];
|
||||
|
||||
ds->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP];
|
||||
ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP];
|
||||
|
||||
ds->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (ds->has_idle_time_ns) {
|
||||
@@ -537,6 +543,11 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
|
||||
|
||||
s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
|
||||
|
||||
s->driver_specific = bdrv_get_specific_stats(bs);
|
||||
if (s->driver_specific) {
|
||||
s->has_driver_specific = true;
|
||||
}
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
|
||||
|
||||
+305
-161
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,9 @@
|
||||
#define QCOW2_MAX_BITMAPS 65535
|
||||
#define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS)
|
||||
|
||||
/* Maximum of parallel sub-request per guest request */
|
||||
#define QCOW2_MAX_WORKERS 8
|
||||
|
||||
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||
#define QCOW_OFLAG_COPIED (1ULL << 63)
|
||||
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||
|
||||
+1
-1
@@ -543,7 +543,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
|
||||
s->backup_job = backup_job_create(
|
||||
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
||||
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false,
|
||||
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
|
||||
BLOCKDEV_ON_ERROR_REPORT,
|
||||
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
||||
backup_job_completed, bs, NULL, &local_err);
|
||||
|
||||
+9
-6
@@ -40,12 +40,14 @@ mirror_yield_in_flight(void *s, int64_t offset, int in_flight) "s %p offset %" P
|
||||
# backup.c
|
||||
backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64
|
||||
backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d"
|
||||
backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
|
||||
backup_do_cow_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
|
||||
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
|
||||
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||
backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||
|
||||
# block-copy.c
|
||||
block_copy_skip(void *bcs, int64_t start) "bcs %p start %"PRId64
|
||||
block_copy_skip_range(void *bcs, int64_t start, uint64_t bytes) "bcs %p start %"PRId64" bytes %"PRId64
|
||||
block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64
|
||||
block_copy_with_bounce_buffer_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
|
||||
block_copy_with_bounce_buffer_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
|
||||
block_copy_with_offload_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
|
||||
|
||||
# ../blockdev.c
|
||||
qmp_block_job_cancel(void *job) "job %p"
|
||||
@@ -62,6 +64,7 @@ file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "
|
||||
file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64
|
||||
|
||||
# qcow2.c
|
||||
qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t file_cluster_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu"
|
||||
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
|
||||
qcow2_writev_done_req(void *co, int ret) "co %p ret %d"
|
||||
qcow2_writev_start_part(void *co) "co %p"
|
||||
|
||||
@@ -3601,6 +3601,7 @@ static BlockJob *do_backup_common(BackupCommon *backup,
|
||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||
backup->sync, bmap, backup->bitmap_mode,
|
||||
backup->compress,
|
||||
backup->filter_node_name,
|
||||
backup->on_source_error,
|
||||
backup->on_target_error,
|
||||
job_flags, NULL, NULL, txn, errp);
|
||||
|
||||
@@ -442,6 +442,14 @@ static void ide_issue_trim_cb(void *opaque, int ret)
|
||||
TrimAIOCB *iocb = opaque;
|
||||
IDEState *s = iocb->s;
|
||||
|
||||
if (iocb->i >= 0) {
|
||||
if (ret >= 0) {
|
||||
block_acct_done(blk_get_stats(s->blk), &s->acct);
|
||||
} else {
|
||||
block_acct_failed(blk_get_stats(s->blk), &s->acct);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
while (iocb->j < iocb->qiov->niov) {
|
||||
int j = iocb->j;
|
||||
@@ -459,10 +467,14 @@ static void ide_issue_trim_cb(void *opaque, int ret)
|
||||
}
|
||||
|
||||
if (!ide_sect_range_ok(s, sector, count)) {
|
||||
block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP);
|
||||
iocb->ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
block_acct_start(blk_get_stats(s->blk), &s->acct,
|
||||
count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP);
|
||||
|
||||
/* Got an entry! Submit and exit. */
|
||||
iocb->aiocb = blk_aio_pdiscard(s->blk,
|
||||
sector << BDRV_SECTOR_BITS,
|
||||
|
||||
+23
-11
@@ -1608,25 +1608,28 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = data->r;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint64_t sector_num;
|
||||
uint32_t nb_sectors;
|
||||
|
||||
assert(r->req.aiocb == NULL);
|
||||
if (scsi_disk_req_check_error(r, ret, false)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (data->count > 0) {
|
||||
sector_num = ldq_be_p(&data->inbuf[0]);
|
||||
nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
|
||||
if (!check_lba_range(s, sector_num, nb_sectors)) {
|
||||
r->sector = ldq_be_p(&data->inbuf[0])
|
||||
* (s->qdev.blocksize / BDRV_SECTOR_SIZE);
|
||||
r->sector_count = (ldl_be_p(&data->inbuf[8]) & 0xffffffffULL)
|
||||
* (s->qdev.blocksize / BDRV_SECTOR_SIZE);
|
||||
if (!check_lba_range(s, r->sector, r->sector_count)) {
|
||||
block_acct_invalid(blk_get_stats(s->qdev.conf.blk),
|
||||
BLOCK_ACCT_UNMAP);
|
||||
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
|
||||
goto done;
|
||||
}
|
||||
|
||||
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
|
||||
r->sector_count * BDRV_SECTOR_SIZE,
|
||||
BLOCK_ACCT_UNMAP);
|
||||
|
||||
r->req.aiocb = blk_aio_pdiscard(s->qdev.conf.blk,
|
||||
sector_num * s->qdev.blocksize,
|
||||
nb_sectors * s->qdev.blocksize,
|
||||
r->sector * BDRV_SECTOR_SIZE,
|
||||
r->sector_count * BDRV_SECTOR_SIZE,
|
||||
scsi_unmap_complete, data);
|
||||
data->count--;
|
||||
data->inbuf += 16;
|
||||
@@ -1650,7 +1653,13 @@ static void scsi_unmap_complete(void *opaque, int ret)
|
||||
r->req.aiocb = NULL;
|
||||
|
||||
aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
|
||||
scsi_unmap_complete_noio(data, ret);
|
||||
if (scsi_disk_req_check_error(r, ret, true)) {
|
||||
scsi_req_unref(&r->req);
|
||||
g_free(data);
|
||||
} else {
|
||||
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
||||
scsi_unmap_complete_noio(data, ret);
|
||||
}
|
||||
aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
|
||||
}
|
||||
|
||||
@@ -1680,6 +1689,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
|
||||
}
|
||||
|
||||
if (blk_is_read_only(s->qdev.conf.blk)) {
|
||||
block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
|
||||
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
|
||||
return;
|
||||
}
|
||||
@@ -1695,10 +1705,12 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
|
||||
return;
|
||||
|
||||
invalid_param_len:
|
||||
block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
|
||||
return;
|
||||
|
||||
invalid_field:
|
||||
block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,11 @@ typedef struct BlockAcctTimedStats BlockAcctTimedStats;
|
||||
typedef struct BlockAcctStats BlockAcctStats;
|
||||
|
||||
enum BlockAcctType {
|
||||
BLOCK_ACCT_NONE = 0,
|
||||
BLOCK_ACCT_READ,
|
||||
BLOCK_ACCT_WRITE,
|
||||
BLOCK_ACCT_FLUSH,
|
||||
BLOCK_ACCT_UNMAP,
|
||||
BLOCK_MAX_IOTYPE,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Aio tasks loops
|
||||
*
|
||||
* Copyright (c) 2019 Virtuozzo International GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BLOCK_AIO_TASK_H
|
||||
#define BLOCK_AIO_TASK_H
|
||||
|
||||
#include "qemu/coroutine.h"
|
||||
|
||||
typedef struct AioTaskPool AioTaskPool;
|
||||
typedef struct AioTask AioTask;
|
||||
typedef int coroutine_fn (*AioTaskFunc)(AioTask *task);
|
||||
struct AioTask {
|
||||
AioTaskPool *pool;
|
||||
AioTaskFunc func;
|
||||
int ret;
|
||||
};
|
||||
|
||||
AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks);
|
||||
void aio_task_pool_free(AioTaskPool *);
|
||||
|
||||
/* error code of failed task or 0 if all is OK */
|
||||
int aio_task_pool_status(AioTaskPool *pool);
|
||||
|
||||
bool aio_task_pool_empty(AioTaskPool *pool);
|
||||
|
||||
/* User provides filled @task, however task->pool will be set automatically */
|
||||
void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task);
|
||||
|
||||
void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool);
|
||||
void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool);
|
||||
void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool);
|
||||
|
||||
#endif /* BLOCK_AIO_TASK_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user