]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0051-PVE-Backup-Use-a-transaction-to-synchronize-job-stat.patch
bump version to 5.1.0-3
[pve-qemu.git] / debian / patches / pve / 0051-PVE-Backup-Use-a-transaction-to-synchronize-job-stat.patch
CommitLineData
d333327a
SR
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Stefan Reiter <s.reiter@proxmox.com>
3Date: Thu, 20 Aug 2020 14:25:00 +0200
4Subject: [PATCH] PVE-Backup: Use a transaction to synchronize job states
5
6By using a JobTxn, we can sync dirty bitmaps only when *all* jobs were
7successful - meaning we don't need to remove them when the backup fails,
8since QEMU's BITMAP_SYNC_MODE_ON_SUCCESS will now handle that for us.
9
10To keep the rate-limiting and IO impact from before, we use a sequential
11transaction, so drives will still be backed up one after the other.
12---
13 pve-backup.c | 167 +++++++++++++++------------------------------------
14 1 file changed, 49 insertions(+), 118 deletions(-)
15
16diff --git a/pve-backup.c b/pve-backup.c
17index 04c21c80aa..9562e9c98d 100644
18--- a/pve-backup.c
19+++ b/pve-backup.c
20@@ -52,6 +52,7 @@ static struct PVEBackupState {
21 VmaWriter *vmaw;
22 ProxmoxBackupHandle *pbs;
23 GList *di_list;
24+ JobTxn *txn;
25 QemuMutex backup_mutex;
26 CoMutex dump_callback_mutex;
27 } backup_state;
28@@ -71,32 +72,12 @@ typedef struct PVEBackupDevInfo {
29 size_t size;
30 uint64_t block_size;
31 uint8_t dev_id;
32- bool completed;
33 char targetfile[PATH_MAX];
34 BdrvDirtyBitmap *bitmap;
35 BlockDriverState *target;
36+ BlockJob *job;
37 } PVEBackupDevInfo;
38
39-static void pvebackup_run_next_job(void);
40-
41-static BlockJob *
42-lookup_active_block_job(PVEBackupDevInfo *di)
43-{
44- if (!di->completed && di->bs) {
45- for (BlockJob *job = block_job_next(NULL); job; job = block_job_next(job)) {
46- if (job->job.driver->job_type != JOB_TYPE_BACKUP) {
47- continue;
48- }
49-
50- BackupBlockJob *bjob = container_of(job, BackupBlockJob, common);
51- if (bjob && bjob->source_bs == di->bs) {
52- return job;
53- }
54- }
55- }
56- return NULL;
57-}
58-
59 static void pvebackup_propagate_error(Error *err)
60 {
61 qemu_mutex_lock(&backup_state.stat.lock);
62@@ -272,18 +253,6 @@ static void coroutine_fn pvebackup_co_cleanup(void *unused)
63 if (local_err != NULL) {
64 pvebackup_propagate_error(local_err);
65 }
66- } else {
67- // on error or cancel we cannot ensure synchronization of dirty
68- // bitmaps with backup server, so remove all and do full backup next
69- GList *l = backup_state.di_list;
70- while (l) {
71- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
72- l = g_list_next(l);
73-
74- if (di->bitmap) {
75- bdrv_release_dirty_bitmap(di->bitmap);
76- }
77- }
78 }
79
80 proxmox_backup_disconnect(backup_state.pbs);
81@@ -322,8 +291,6 @@ static void pvebackup_complete_cb(void *opaque, int ret)
82
83 qemu_mutex_lock(&backup_state.backup_mutex);
84
85- di->completed = true;
86-
87 if (ret < 0) {
88 Error *local_err = NULL;
89 error_setg(&local_err, "job failed with err %d - %s", ret, strerror(-ret));
90@@ -336,20 +303,17 @@ static void pvebackup_complete_cb(void *opaque, int ret)
91
92 block_on_coroutine_fn(pvebackup_complete_stream, di);
93
94- // remove self from job queue
95+ // remove self from job list
96 backup_state.di_list = g_list_remove(backup_state.di_list, di);
97
98- if (di->bitmap && ret < 0) {
99- // on error or cancel we cannot ensure synchronization of dirty
100- // bitmaps with backup server, so remove all and do full backup next
101- bdrv_release_dirty_bitmap(di->bitmap);
102- }
103-
104 g_free(di);
105
106- qemu_mutex_unlock(&backup_state.backup_mutex);
107+ /* call cleanup if we're the last job */
108+ if (!g_list_first(backup_state.di_list)) {
109+ block_on_coroutine_fn(pvebackup_co_cleanup, NULL);
110+ }
111
112- pvebackup_run_next_job();
113+ qemu_mutex_unlock(&backup_state.backup_mutex);
114 }
115
116 static void pvebackup_cancel(void)
117@@ -371,36 +335,28 @@ static void pvebackup_cancel(void)
118 proxmox_backup_abort(backup_state.pbs, "backup canceled");
119 }
120
121- qemu_mutex_unlock(&backup_state.backup_mutex);
122-
123- for(;;) {
124-
125- BlockJob *next_job = NULL;
126-
127- qemu_mutex_lock(&backup_state.backup_mutex);
128-
129- GList *l = backup_state.di_list;
130- while (l) {
131- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
132- l = g_list_next(l);
133+ /* it's enough to cancel one job in the transaction, the rest will follow
134+ * automatically */
135+ GList *bdi = g_list_first(backup_state.di_list);
136+ BlockJob *cancel_job = bdi && bdi->data ?
137+ ((PVEBackupDevInfo *)bdi->data)->job :
138+ NULL;
139
140- BlockJob *job = lookup_active_block_job(di);
141- if (job != NULL) {
142- next_job = job;
143- break;
144- }
145- }
146+ /* ref the job before releasing the mutex, just to be safe */
147+ if (cancel_job) {
148+ job_ref(&cancel_job->job);
149+ }
150
151- qemu_mutex_unlock(&backup_state.backup_mutex);
152+ /* job_cancel_sync may enter the job, so we need to release the
153+ * backup_mutex to avoid deadlock */
154+ qemu_mutex_unlock(&backup_state.backup_mutex);
155
156- if (next_job) {
157- AioContext *aio_context = next_job->job.aio_context;
158- aio_context_acquire(aio_context);
159- job_cancel_sync(&next_job->job);
160- aio_context_release(aio_context);
161- } else {
162- break;
163- }
164+ if (cancel_job) {
165+ AioContext *aio_context = cancel_job->job.aio_context;
166+ aio_context_acquire(aio_context);
167+ job_cancel_sync(&cancel_job->job);
168+ job_unref(&cancel_job->job);
169+ aio_context_release(aio_context);
170 }
171 }
172
173@@ -459,51 +415,19 @@ static int coroutine_fn pvebackup_co_add_config(
174 goto out;
175 }
176
177-bool job_should_pause(Job *job);
178-
179-static void pvebackup_run_next_job(void)
180-{
181- assert(!qemu_in_coroutine());
182-
183- qemu_mutex_lock(&backup_state.backup_mutex);
184-
185- GList *l = backup_state.di_list;
186- while (l) {
187- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
188- l = g_list_next(l);
189-
190- BlockJob *job = lookup_active_block_job(di);
191-
192- if (job) {
193- qemu_mutex_unlock(&backup_state.backup_mutex);
194-
195- AioContext *aio_context = job->job.aio_context;
196- aio_context_acquire(aio_context);
197-
198- if (job_should_pause(&job->job)) {
199- bool error_or_canceled = pvebackup_error_or_canceled();
200- if (error_or_canceled) {
201- job_cancel_sync(&job->job);
202- } else {
203- job_resume(&job->job);
204- }
205- }
206- aio_context_release(aio_context);
207- return;
208- }
209- }
210-
211- block_on_coroutine_fn(pvebackup_co_cleanup, NULL); // no more jobs, run cleanup
212-
213- qemu_mutex_unlock(&backup_state.backup_mutex);
214-}
215-
216 static bool create_backup_jobs(void) {
217
218 assert(!qemu_in_coroutine());
219
220 Error *local_err = NULL;
221
222+ /* create job transaction to synchronize bitmap commit and cancel all
223+ * jobs in case one errors */
224+ if (backup_state.txn) {
225+ job_txn_unref(backup_state.txn);
226+ }
227+ backup_state.txn = job_txn_new_seq();
228+
229 /* create and start all jobs (paused state) */
230 GList *l = backup_state.di_list;
231 while (l) {
232@@ -524,7 +448,7 @@ static bool create_backup_jobs(void) {
233 BlockJob *job = backup_job_create(
234 NULL, di->bs, di->target, backup_state.speed, sync_mode, di->bitmap,
235 bitmap_mode, false, NULL, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
236- JOB_DEFAULT, pvebackup_complete_cb, di, 1, NULL, &local_err);
237+ JOB_DEFAULT, pvebackup_complete_cb, di, backup_state.txn, &local_err);
238
239 aio_context_release(aio_context);
240
241@@ -536,7 +460,8 @@ static bool create_backup_jobs(void) {
242 pvebackup_propagate_error(create_job_err);
243 break;
244 }
245- job_start(&job->job);
246+
247+ di->job = job;
248
249 bdrv_unref(di->target);
250 di->target = NULL;
251@@ -554,6 +479,10 @@ static bool create_backup_jobs(void) {
252 bdrv_unref(di->target);
253 di->target = NULL;
254 }
255+
256+ if (di->job) {
257+ job_unref(&di->job->job);
258+ }
259 }
260 }
261
262@@ -944,10 +873,6 @@ err:
263 PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
264 l = g_list_next(l);
265
266- if (di->bitmap) {
267- bdrv_release_dirty_bitmap(di->bitmap);
268- }
269-
270 if (di->target) {
271 bdrv_unref(di->target);
272 }
273@@ -1036,9 +961,15 @@ UuidInfo *qmp_backup(
274 block_on_coroutine_fn(pvebackup_co_prepare, &task);
275
276 if (*errp == NULL) {
277- create_backup_jobs();
278+ bool errors = create_backup_jobs();
279 qemu_mutex_unlock(&backup_state.backup_mutex);
280- pvebackup_run_next_job();
281+
282+ if (!errors) {
283+ /* start the first job in the transaction
284+ * note: this might directly enter the job, so we need to do this
285+ * after unlocking the backup_mutex */
286+ job_txn_start_seq(backup_state.txn);
287+ }
288 } else {
289 qemu_mutex_unlock(&backup_state.backup_mutex);
290 }