From 4c2e4e498716b8a556fccec20fb1348555c2b189 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 14 Feb 2020 10:21:37 +0100 Subject: [PATCH qemu 1/7] PVE backup: use separate CoRwlock for data accessed by qmp query_backup This should minimize contention between query_backup and running backup jobs. --- blockdev.c | 178 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 66 deletions(-) diff --git a/blockdev.c b/blockdev.c index 46e8a2780a..4367eaf2a7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3203,22 +3203,26 @@ static void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) AIO_WAIT_WHILE(ctx, !wrapper.finished); } + static struct PVEBackupState { - Error *error; + struct { + CoRwlock rwlock; + Error *error; + time_t start_time; + time_t end_time; + char *backup_file; + uuid_t uuid; + char uuid_str[37]; + size_t total; + size_t transferred; + size_t zero_bytes; + } stat; bool cancel; - uuid_t uuid; - char uuid_str[37]; int64_t speed; - time_t start_time; - time_t end_time; - char *backup_file; VmaWriter *vmaw; GList *di_list; - size_t total; - size_t transferred; - size_t zero_bytes; CoMutex backup_mutex; - bool backup_mutex_initialized; + bool mutex_initialized; } backup_state; typedef struct PVEBackupDevInfo { @@ -3251,11 +3255,14 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, uint64_t cluster_num = start / VMA_CLUSTER_SIZE; if ((cluster_num * VMA_CLUSTER_SIZE) != start) { - if (!backup_state.error) { - error_setg(&backup_state.error, + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); + if (!backup_state.stat.error) { + qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); + error_setg(&backup_state.stat.error, "got unaligned write inside backup dump " "callback (sector %ld)", start); } + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); qemu_co_mutex_unlock(&backup_state.backup_mutex); return -1; // not aligned to cluster size } @@ -3273,27 +3280,35 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, buf += VMA_CLUSTER_SIZE; } if (ret < 0) { - if (!backup_state.error) { - vma_writer_error_propagate(backup_state.vmaw, &backup_state.error); + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); + if (!backup_state.stat.error) { + qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); + vma_writer_error_propagate(backup_state.vmaw, &backup_state.stat.error); } + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); + qemu_co_mutex_unlock(&backup_state.backup_mutex); return ret; } else { - backup_state.zero_bytes += zero_bytes; + qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); + backup_state.stat.zero_bytes += zero_bytes; if (remaining >= VMA_CLUSTER_SIZE) { - backup_state.transferred += VMA_CLUSTER_SIZE; + backup_state.stat.transferred += VMA_CLUSTER_SIZE; remaining -= VMA_CLUSTER_SIZE; } else { - backup_state.transferred += remaining; + backup_state.stat.transferred += remaining; remaining = 0; } + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); } } } else { + qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); if (!buf) { - backup_state.zero_bytes += size; + backup_state.stat.zero_bytes += size; } - backup_state.transferred += size; + backup_state.stat.transferred += size; + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); } qemu_co_mutex_unlock(&backup_state.backup_mutex); @@ -3313,12 +3328,20 @@ static void coroutine_fn pvebackup_co_cleanup(void) return; } - backup_state.end_time = time(NULL); + qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); + backup_state.stat.end_time = time(NULL); + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); if (backup_state.vmaw) { Error *local_err = NULL; vma_writer_close(backup_state.vmaw, &local_err); - error_propagate(&backup_state.error, local_err); + + if (local_err != NULL) { + qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); + error_propagate(&backup_state.stat.error, local_err); + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); + } + backup_state.vmaw = NULL; } @@ -3345,10 +3368,14 @@ static void coroutine_fn pvebackup_co_complete_cb(void *opaque) di->completed = true; - if (ret < 0 && !backup_state.error) { - error_setg(&backup_state.error, "job failed with err %d - %s", + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); + + if (ret < 0 && !backup_state.stat.error) { + qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); + error_setg(&backup_state.stat.error, "job failed with err %d - %s", ret, strerror(-ret)); } + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); di->bs = NULL; di->target = NULL; @@ -3401,9 +3428,12 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) return; } - if (!backup_state.error) { - error_setg(&backup_state.error, "backup cancelled"); + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); + if (!backup_state.stat.error) { + qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); + error_setg(&backup_state.stat.error, "backup cancelled"); } + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); if (backup_state.vmaw) { /* make sure vma writer does not block anymore */ @@ -3438,7 +3468,7 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) void qmp_backup_cancel(Error **errp) { - if (!backup_state.backup_mutex_initialized) + if (!backup_state.mutex_initialized) return; block_on_coroutine_fn(pvebackup_co_cancel, NULL); @@ -3508,9 +3538,13 @@ static void coroutine_fn pvebackup_co_run_next_job(void) qemu_co_mutex_unlock(&backup_state.backup_mutex); aio_context_acquire(aio_context); - + if (job_should_pause(&job->job)) { - if (backup_state.error || backup_state.cancel) { + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); + bool error_or_canceled = backup_state.stat.error || backup_state.cancel; + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); + + if (error_or_canceled) { job_cancel(&job->job, false); } else { job_resume(&job->job); @@ -3565,9 +3599,10 @@ static void coroutine_fn pvebackup_co_start(void *opaque) const char *config_name = "qemu-server.conf"; const char *firewall_name = "qemu-server.fw"; - if (!backup_state.backup_mutex_initialized) { + if (!backup_state.mutex_initialized) { + qemu_co_rwlock_init(&backup_state.stat.rwlock); qemu_co_mutex_init(&backup_state.backup_mutex); - backup_state.backup_mutex_initialized = true; + backup_state.mutex_initialized = true; } qemu_co_mutex_lock(&backup_state.backup_mutex); @@ -3727,31 +3762,36 @@ static void coroutine_fn pvebackup_co_start(void *opaque) backup_state.cancel = false; - if (backup_state.error) { - error_free(backup_state.error); - backup_state.error = NULL; - } + qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); - backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0; + if (backup_state.stat.error) { + error_free(backup_state.stat.error); + backup_state.stat.error = NULL; + } - backup_state.start_time = time(NULL); - backup_state.end_time = 0; + backup_state.stat.start_time = time(NULL); + backup_state.stat.end_time = 0; - if (backup_state.backup_file) { - g_free(backup_state.backup_file); + if (backup_state.stat.backup_file) { + g_free(backup_state.stat.backup_file); } - backup_state.backup_file = g_strdup(task->backup_file); + backup_state.stat.backup_file = g_strdup(task->backup_file); - backup_state.vmaw = vmaw; + uuid_copy(backup_state.stat.uuid, uuid); + uuid_unparse_lower(uuid, backup_state.stat.uuid_str); + char *uuid_str = g_strdup(backup_state.stat.uuid_str); - uuid_copy(backup_state.uuid, uuid); - uuid_unparse_lower(uuid, backup_state.uuid_str); + backup_state.stat.total = total; + backup_state.stat.transferred = 0; + backup_state.stat.zero_bytes = 0; - backup_state.di_list = di_list; + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); + + backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0; + + backup_state.vmaw = vmaw; - backup_state.total = total; - backup_state.transferred = 0; - backup_state.zero_bytes = 0; + backup_state.di_list = di_list; /* start all jobs (paused state) */ l = di_list; @@ -3763,7 +3803,9 @@ static void coroutine_fn pvebackup_co_start(void *opaque) JOB_DEFAULT, pvebackup_co_dump_cb, dump_cb_block_size, pvebackup_complete_cb, di, 1, NULL, &local_err); if (!job || local_err != NULL) { - error_setg(&backup_state.error, "backup_job_create failed"); + qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); + error_setg(&backup_state.stat.error, "backup_job_create failed"); + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); break; } job_start(&job->job); @@ -3775,14 +3817,18 @@ static void coroutine_fn pvebackup_co_start(void *opaque) qemu_co_mutex_unlock(&backup_state.backup_mutex); - if (!backup_state.error) { + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); + bool no_errors = !backup_state.stat.error; + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); + + if (no_errors) { pvebackup_co_run_next_job(); // run one job } else { pvebackup_co_cancel(NULL); } uuid_info = g_malloc0(sizeof(*uuid_info)); - uuid_info->UUID = g_strdup(backup_state.uuid_str); + uuid_info->UUID = uuid_str; task->result = uuid_info; return; @@ -3866,54 +3912,54 @@ static void coroutine_fn pvebackup_co_query(void *opaque) BackupStatus *info = g_malloc0(sizeof(*info)); - if (!backup_state.backup_mutex_initialized) + if (!backup_state.mutex_initialized) return; - qemu_co_mutex_lock(&backup_state.backup_mutex); + qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); - if (!backup_state.start_time) { + if (!backup_state.stat.start_time) { /* not started, return {} */ task->result = info; - qemu_co_mutex_unlock(&backup_state.backup_mutex); + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); return; } info->has_status = true; info->has_start_time = true; - info->start_time = backup_state.start_time; + info->start_time = backup_state.stat.start_time; - if (backup_state.backup_file) { + if (backup_state.stat.backup_file) { info->has_backup_file = true; - info->backup_file = g_strdup(backup_state.backup_file); + info->backup_file = g_strdup(backup_state.stat.backup_file); } info->has_uuid = true; - info->uuid = g_strdup(backup_state.uuid_str); + info->uuid = g_strdup(backup_state.stat.uuid_str); - if (backup_state.end_time) { - if (backup_state.error) { + if (backup_state.stat.end_time) { + if (backup_state.stat.error) { info->status = g_strdup("error"); info->has_errmsg = true; - info->errmsg = g_strdup(error_get_pretty(backup_state.error)); + info->errmsg = g_strdup(error_get_pretty(backup_state.stat.error)); } else { info->status = g_strdup("done"); } info->has_end_time = true; - info->end_time = backup_state.end_time; + info->end_time = backup_state.stat.end_time; } else { info->status = g_strdup("active"); } info->has_total = true; - info->total = backup_state.total; + info->total = backup_state.stat.total; info->has_zero_bytes = true; - info->zero_bytes = backup_state.zero_bytes; + info->zero_bytes = backup_state.stat.zero_bytes; info->has_transferred = true; - info->transferred = backup_state.transferred; + info->transferred = backup_state.stat.transferred; task->result = info; - qemu_co_mutex_unlock(&backup_state.backup_mutex); + qemu_co_rwlock_unlock(&backup_state.stat.rwlock); } BackupStatus *qmp_query_backup(Error **errp) -- 2.20.1