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