mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/ericb/tags/pull-bitmaps-2020-07-27' into staging
bitmaps patches for 2020-07-27 - Improve handling of various post-copy bitmap migration scenarios. A lost bitmap should merely mean that the next backup must be full rather than incremental, rather than abruptly breaking the entire guest migration. - Associated iotest improvements # gpg: Signature made Mon 27 Jul 2020 21:46:17 BST # gpg: using RSA key 71C2CC22B1C4602927D2F3AAA7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" [full] # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" [full] # gpg: aka "[jpeg image of size 6874]" [full] # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-bitmaps-2020-07-27: (24 commits) migration: Fix typos in bitmap migration comments iotests: Adjust which migration tests are quick qemu-iotests/199: add source-killed case to bitmaps postcopy qemu-iotests/199: add early shutdown case to bitmaps postcopy qemu-iotests/199: check persistent bitmaps qemu-iotests/199: prepare for new test-cases addition migration/savevm: don't worry if bitmap migration postcopy failed migration/block-dirty-bitmap: cancel migration on shutdown migration/block-dirty-bitmap: relax error handling in incoming part migration/block-dirty-bitmap: keep bitmap state for all bitmaps migration/block-dirty-bitmap: simplify dirty_bitmap_load_complete migration/block-dirty-bitmap: rename finish_lock to just lock migration/block-dirty-bitmap: refactor state global variables migration/block-dirty-bitmap: move mutex init to dirty_bitmap_mig_init migration/block-dirty-bitmap: rename dirty_bitmap_mig_cleanup migration/block-dirty-bitmap: rename state structure types migration/block-dirty-bitmap: fix dirty_bitmap_mig_before_vm_start qemu-iotests/199: increase postcopy period qemu-iotests/199: change discard patterns qemu-iotests/199: improve performance: set bitmap by discard ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+1
-1
@@ -66,7 +66,7 @@ typedef struct {
|
||||
} QEMU_PACKED QCowExtension;
|
||||
|
||||
#define QCOW2_EXT_MAGIC_END 0
|
||||
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
|
||||
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xe2792aca
|
||||
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
|
||||
#define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
|
||||
#define QCOW2_EXT_MAGIC_BITMAPS 0x23852875
|
||||
|
||||
@@ -231,7 +231,7 @@ be stored. Each extension has a structure like the following:
|
||||
|
||||
Byte 0 - 3: Header extension type:
|
||||
0x00000000 - End of the header extension area
|
||||
0xE2792ACA - Backing file format name string
|
||||
0xe2792aca - Backing file format name string
|
||||
0x6803f857 - Feature name table
|
||||
0x23852875 - Bitmaps extension
|
||||
0x0537be77 - Full disk encryption header pointer
|
||||
|
||||
+304
-174
File diff suppressed because it is too large
Load Diff
+13
-2
@@ -165,8 +165,6 @@ void migration_object_init(void)
|
||||
qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0);
|
||||
qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0);
|
||||
|
||||
init_dirty_bitmap_incoming_migration();
|
||||
|
||||
if (!migration_object_check(current_migration, &err)) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
@@ -190,6 +188,19 @@ void migration_shutdown(void)
|
||||
*/
|
||||
migrate_fd_cancel(current_migration);
|
||||
object_unref(OBJECT(current_migration));
|
||||
|
||||
/*
|
||||
* Cancel outgoing migration of dirty bitmaps. It should
|
||||
* at least unref used block nodes.
|
||||
*/
|
||||
dirty_bitmap_mig_cancel_outgoing();
|
||||
|
||||
/*
|
||||
* Cancel incoming migration of dirty bitmaps. Dirty bitmaps
|
||||
* are non-critical data, and their loss never considered as
|
||||
* something serious.
|
||||
*/
|
||||
dirty_bitmap_mig_cancel_incoming();
|
||||
}
|
||||
|
||||
/* For outgoing */
|
||||
|
||||
@@ -335,7 +335,8 @@ void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis,
|
||||
void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value);
|
||||
|
||||
void dirty_bitmap_mig_before_vm_start(void);
|
||||
void init_dirty_bitmap_incoming_migration(void);
|
||||
void dirty_bitmap_mig_cancel_outgoing(void);
|
||||
void dirty_bitmap_mig_cancel_incoming(void);
|
||||
void migrate_add_address(SocketAddress *address);
|
||||
|
||||
int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque);
|
||||
|
||||
+32
-5
@@ -1813,6 +1813,9 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||
QEMUFile *f = mis->from_src_file;
|
||||
int load_res;
|
||||
MigrationState *migr = migrate_get_current();
|
||||
|
||||
object_ref(OBJECT(migr));
|
||||
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
||||
MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
||||
@@ -1839,11 +1842,24 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||
|
||||
trace_postcopy_ram_listen_thread_exit();
|
||||
if (load_res < 0) {
|
||||
error_report("%s: loadvm failed: %d", __func__, load_res);
|
||||
qemu_file_set_error(f, load_res);
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
|
||||
MIGRATION_STATUS_FAILED);
|
||||
} else {
|
||||
dirty_bitmap_mig_cancel_incoming();
|
||||
if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
|
||||
!migrate_postcopy_ram() && migrate_dirty_bitmaps())
|
||||
{
|
||||
error_report("%s: loadvm failed during postcopy: %d. All states "
|
||||
"are migrated except dirty bitmaps. Some dirty "
|
||||
"bitmaps may be lost, and present migrated dirty "
|
||||
"bitmaps are correctly migrated and valid.",
|
||||
__func__, load_res);
|
||||
load_res = 0; /* prevent further exit() */
|
||||
} else {
|
||||
error_report("%s: loadvm failed: %d", __func__, load_res);
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
|
||||
MIGRATION_STATUS_FAILED);
|
||||
}
|
||||
}
|
||||
if (load_res >= 0) {
|
||||
/*
|
||||
* This looks good, but it's possible that the device loading in the
|
||||
* main thread hasn't finished yet, and so we might not be in 'RUN'
|
||||
@@ -1879,6 +1895,8 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
||||
mis->have_listen_thread = false;
|
||||
postcopy_state_set(POSTCOPY_INCOMING_END);
|
||||
|
||||
object_unref(OBJECT(migr));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2437,6 +2455,8 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis)
|
||||
{
|
||||
trace_postcopy_pause_incoming();
|
||||
|
||||
assert(migrate_postcopy_ram());
|
||||
|
||||
/* Clear the triggered bit to allow one recovery */
|
||||
mis->postcopy_recover_triggered = false;
|
||||
|
||||
@@ -2521,15 +2541,22 @@ out:
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
|
||||
/* Cancel bitmaps incoming regardless of recovery */
|
||||
dirty_bitmap_mig_cancel_incoming();
|
||||
|
||||
/*
|
||||
* If we are during an active postcopy, then we pause instead
|
||||
* of bail out to at least keep the VM's dirty data. Note
|
||||
* that POSTCOPY_INCOMING_LISTENING stage is still not enough,
|
||||
* during which we're still receiving device states and we
|
||||
* still haven't yet started the VM on destination.
|
||||
*
|
||||
* Only RAM postcopy supports recovery. Still, if RAM postcopy is
|
||||
* enabled, canceled bitmaps postcopy will not affect RAM postcopy
|
||||
* recovering.
|
||||
*/
|
||||
if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
|
||||
postcopy_pause_incoming(mis)) {
|
||||
migrate_postcopy_ram() && postcopy_pause_incoming(mis)) {
|
||||
/* Reset f to point to the newly created channel */
|
||||
f = mis->from_src_file;
|
||||
goto retry;
|
||||
|
||||
+194
-52
@@ -20,17 +20,76 @@
|
||||
|
||||
import os
|
||||
import iotests
|
||||
import time
|
||||
from iotests import qemu_img
|
||||
|
||||
debug = False
|
||||
|
||||
disk_a = os.path.join(iotests.test_dir, 'disk_a')
|
||||
disk_b = os.path.join(iotests.test_dir, 'disk_b')
|
||||
size = '256G'
|
||||
fifo = os.path.join(iotests.test_dir, 'mig_fifo')
|
||||
|
||||
class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
|
||||
granularity = 512
|
||||
nb_bitmaps = 15
|
||||
|
||||
GiB = 1024 * 1024 * 1024
|
||||
|
||||
discards1 = (
|
||||
(0, GiB),
|
||||
(2 * GiB + 512 * 5, 512),
|
||||
(3 * GiB + 512 * 5, 512),
|
||||
(100 * GiB, GiB)
|
||||
)
|
||||
|
||||
discards2 = (
|
||||
(3 * GiB + 512 * 8, 512),
|
||||
(4 * GiB + 512 * 8, 512),
|
||||
(50 * GiB, GiB),
|
||||
(100 * GiB + GiB // 2, GiB)
|
||||
)
|
||||
|
||||
|
||||
def apply_discards(vm, discards):
|
||||
for d in discards:
|
||||
vm.hmp_qemu_io('drive0', 'discard {} {}'.format(*d))
|
||||
|
||||
|
||||
def event_seconds(event):
|
||||
return event['timestamp']['seconds'] + \
|
||||
event['timestamp']['microseconds'] / 1000000.0
|
||||
|
||||
|
||||
def event_dist(e1, e2):
|
||||
return event_seconds(e2) - event_seconds(e1)
|
||||
|
||||
|
||||
def check_bitmaps(vm, count):
|
||||
result = vm.qmp('query-block')
|
||||
|
||||
if count == 0:
|
||||
assert 'dirty-bitmaps' not in result['return'][0]
|
||||
else:
|
||||
assert len(result['return'][0]['dirty-bitmaps']) == count
|
||||
|
||||
|
||||
class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
|
||||
def tearDown(self):
|
||||
if debug:
|
||||
self.vm_a_events += self.vm_a.get_qmp_events()
|
||||
self.vm_b_events += self.vm_b.get_qmp_events()
|
||||
for e in self.vm_a_events:
|
||||
e['vm'] = 'SRC'
|
||||
for e in self.vm_b_events:
|
||||
e['vm'] = 'DST'
|
||||
events = (self.vm_a_events + self.vm_b_events)
|
||||
events = [(e['timestamp']['seconds'],
|
||||
e['timestamp']['microseconds'],
|
||||
e['vm'],
|
||||
e['event'],
|
||||
e.get('data', '')) for e in events]
|
||||
for e in sorted(events):
|
||||
print('{}.{:06} {} {} {}'.format(*e))
|
||||
|
||||
self.vm_a.shutdown()
|
||||
self.vm_b.shutdown()
|
||||
os.remove(disk_a)
|
||||
@@ -41,51 +100,66 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
|
||||
os.mkfifo(fifo)
|
||||
qemu_img('create', '-f', iotests.imgfmt, disk_a, size)
|
||||
qemu_img('create', '-f', iotests.imgfmt, disk_b, size)
|
||||
self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a)
|
||||
self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
|
||||
self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a,
|
||||
'discard=unmap')
|
||||
self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b,
|
||||
'discard=unmap')
|
||||
self.vm_b.add_incoming("exec: cat '" + fifo + "'")
|
||||
self.vm_a.launch()
|
||||
self.vm_b.launch()
|
||||
|
||||
def test_postcopy(self):
|
||||
write_size = 0x40000000
|
||||
granularity = 512
|
||||
chunk = 4096
|
||||
# collect received events for debug
|
||||
self.vm_a_events = []
|
||||
self.vm_b_events = []
|
||||
|
||||
result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0',
|
||||
name='bitmap', granularity=granularity)
|
||||
self.assert_qmp(result, 'return', {});
|
||||
|
||||
s = 0
|
||||
while s < write_size:
|
||||
self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
|
||||
s += 0x10000
|
||||
s = 0x8000
|
||||
while s < write_size:
|
||||
self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
|
||||
s += 0x10000
|
||||
def start_postcopy(self):
|
||||
""" Run migration until RESUME event on target. Return this event. """
|
||||
for i in range(nb_bitmaps):
|
||||
result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0',
|
||||
name='bitmap{}'.format(i),
|
||||
granularity=granularity,
|
||||
persistent=True)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||
node='drive0', name='bitmap')
|
||||
sha256 = result['return']['sha256']
|
||||
node='drive0', name='bitmap0')
|
||||
empty_sha256 = result['return']['sha256']
|
||||
|
||||
result = self.vm_a.qmp('block-dirty-bitmap-clear', node='drive0',
|
||||
name='bitmap')
|
||||
self.assert_qmp(result, 'return', {});
|
||||
s = 0
|
||||
while s < write_size:
|
||||
self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
|
||||
s += 0x10000
|
||||
apply_discards(self.vm_a, discards1)
|
||||
|
||||
bitmaps_cap = {'capability': 'dirty-bitmaps', 'state': True}
|
||||
events_cap = {'capability': 'events', 'state': True}
|
||||
result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||
node='drive0', name='bitmap0')
|
||||
self.discards1_sha256 = result['return']['sha256']
|
||||
|
||||
result = self.vm_a.qmp('migrate-set-capabilities',
|
||||
capabilities=[bitmaps_cap, events_cap])
|
||||
# Check, that updating the bitmap by discards works
|
||||
assert self.discards1_sha256 != empty_sha256
|
||||
|
||||
# We want to calculate resulting sha256. Do it in bitmap0, so, disable
|
||||
# other bitmaps
|
||||
for i in range(1, nb_bitmaps):
|
||||
result = self.vm_a.qmp('block-dirty-bitmap-disable', node='drive0',
|
||||
name='bitmap{}'.format(i))
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
apply_discards(self.vm_a, discards2)
|
||||
|
||||
result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||
node='drive0', name='bitmap0')
|
||||
self.all_discards_sha256 = result['return']['sha256']
|
||||
|
||||
# Now, enable some bitmaps, to be updated during migration
|
||||
for i in range(2, nb_bitmaps, 2):
|
||||
result = self.vm_a.qmp('block-dirty-bitmap-enable', node='drive0',
|
||||
name='bitmap{}'.format(i))
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
caps = [{'capability': 'dirty-bitmaps', 'state': True},
|
||||
{'capability': 'events', 'state': True}]
|
||||
|
||||
result = self.vm_a.qmp('migrate-set-capabilities', capabilities=caps)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
result = self.vm_b.qmp('migrate-set-capabilities',
|
||||
capabilities=[bitmaps_cap])
|
||||
result = self.vm_b.qmp('migrate-set-capabilities', capabilities=caps)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo)
|
||||
@@ -94,26 +168,94 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
|
||||
result = self.vm_a.qmp('migrate-start-postcopy')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
while True:
|
||||
event = self.vm_a.event_wait('MIGRATION')
|
||||
if event['data']['status'] == 'completed':
|
||||
break
|
||||
event_resume = self.vm_b.event_wait('RESUME')
|
||||
self.vm_b_events.append(event_resume)
|
||||
return event_resume
|
||||
|
||||
s = 0x8000
|
||||
while s < write_size:
|
||||
self.vm_b.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
|
||||
s += 0x10000
|
||||
def test_postcopy_success(self):
|
||||
event_resume = self.start_postcopy()
|
||||
|
||||
result = self.vm_b.qmp('query-block');
|
||||
while len(result['return'][0]['dirty-bitmaps']) > 1:
|
||||
time.sleep(2)
|
||||
result = self.vm_b.qmp('query-block');
|
||||
# enabled bitmaps should be updated
|
||||
apply_discards(self.vm_b, discards2)
|
||||
|
||||
result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||
node='drive0', name='bitmap')
|
||||
match = {'data': {'status': 'completed'}}
|
||||
event_complete = self.vm_b.event_wait('MIGRATION', match=match)
|
||||
self.vm_b_events.append(event_complete)
|
||||
|
||||
# take queued event, should already been happened
|
||||
event_stop = self.vm_a.event_wait('STOP')
|
||||
self.vm_a_events.append(event_stop)
|
||||
|
||||
downtime = event_dist(event_stop, event_resume)
|
||||
postcopy_time = event_dist(event_resume, event_complete)
|
||||
|
||||
assert downtime * 10 < postcopy_time
|
||||
if debug:
|
||||
print('downtime:', downtime)
|
||||
print('postcopy_time:', postcopy_time)
|
||||
|
||||
# check that there are no bitmaps stored on source
|
||||
self.vm_a_events += self.vm_a.get_qmp_events()
|
||||
self.vm_a.shutdown()
|
||||
self.vm_a.launch()
|
||||
check_bitmaps(self.vm_a, 0)
|
||||
|
||||
# check that bitmaps are migrated and persistence works
|
||||
check_bitmaps(self.vm_b, nb_bitmaps)
|
||||
self.vm_b.shutdown()
|
||||
# recreate vm_b, so there is no incoming option, which prevents
|
||||
# loading bitmaps from disk
|
||||
self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
|
||||
self.vm_b.launch()
|
||||
check_bitmaps(self.vm_b, nb_bitmaps)
|
||||
|
||||
# Check content of migrated bitmaps. Still, don't waste time checking
|
||||
# every bitmap
|
||||
for i in range(0, nb_bitmaps, 5):
|
||||
result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||
node='drive0', name='bitmap{}'.format(i))
|
||||
sha = self.discards1_sha256 if i % 2 else self.all_discards_sha256
|
||||
self.assert_qmp(result, 'return/sha256', sha)
|
||||
|
||||
def test_early_shutdown_destination(self):
|
||||
self.start_postcopy()
|
||||
|
||||
self.vm_b_events += self.vm_b.get_qmp_events()
|
||||
self.vm_b.shutdown()
|
||||
# recreate vm_b, so there is no incoming option, which prevents
|
||||
# loading bitmaps from disk
|
||||
self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
|
||||
self.vm_b.launch()
|
||||
check_bitmaps(self.vm_b, 0)
|
||||
|
||||
# Bitmaps will be lost if we just shutdown the vm, as they are marked
|
||||
# to skip storing to disk when prepared for migration. And that's
|
||||
# correct, as actual data may be modified in target vm, so we play
|
||||
# safe.
|
||||
# Still, this mark would be taken away if we do 'cont', and bitmaps
|
||||
# become persistent again. (see iotest 169 for such behavior case)
|
||||
result = self.vm_a.qmp('query-status')
|
||||
assert not result['return']['running']
|
||||
self.vm_a_events += self.vm_a.get_qmp_events()
|
||||
self.vm_a.shutdown()
|
||||
self.vm_a.launch()
|
||||
check_bitmaps(self.vm_a, 0)
|
||||
|
||||
def test_early_kill_source(self):
|
||||
self.start_postcopy()
|
||||
|
||||
self.vm_a_events = self.vm_a.get_qmp_events()
|
||||
self.vm_a.kill()
|
||||
|
||||
self.vm_a.launch()
|
||||
|
||||
match = {'data': {'status': 'completed'}}
|
||||
e_complete = self.vm_b.event_wait('MIGRATION', match=match)
|
||||
self.vm_b_events.append(e_complete)
|
||||
|
||||
check_bitmaps(self.vm_a, 0)
|
||||
check_bitmaps(self.vm_b, 0)
|
||||
|
||||
self.assert_qmp(result, 'return/sha256', sha256);
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2'], supported_cache_modes=['none'],
|
||||
supported_protocols=['file'])
|
||||
iotests.main(supported_fmts=['qcow2'])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.
|
||||
...
|
||||
----------------------------------------------------------------------
|
||||
Ran 1 tests
|
||||
Ran 3 tests
|
||||
|
||||
OK
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
088 rw quick
|
||||
089 rw auto quick
|
||||
090 rw auto quick
|
||||
091 rw migration
|
||||
091 rw migration quick
|
||||
092 rw quick
|
||||
093 throttle
|
||||
094 rw quick
|
||||
@@ -186,7 +186,7 @@
|
||||
162 quick
|
||||
163 rw
|
||||
165 rw quick
|
||||
169 rw quick migration
|
||||
169 rw migration
|
||||
170 rw auto quick
|
||||
171 rw quick
|
||||
172 auto
|
||||
@@ -197,9 +197,9 @@
|
||||
177 rw auto quick
|
||||
178 img
|
||||
179 rw auto quick
|
||||
181 rw auto migration
|
||||
181 rw auto migration quick
|
||||
182 rw quick
|
||||
183 rw migration
|
||||
183 rw migration quick
|
||||
184 rw auto quick
|
||||
185 rw
|
||||
186 rw auto
|
||||
@@ -216,9 +216,9 @@
|
||||
198 rw
|
||||
199 rw migration
|
||||
200 rw
|
||||
201 rw migration
|
||||
201 rw migration quick
|
||||
202 rw quick
|
||||
203 rw auto migration
|
||||
203 rw auto migration quick
|
||||
204 rw quick
|
||||
205 rw quick
|
||||
206 rw
|
||||
|
||||
Reference in New Issue
Block a user