qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

migration: Support QLIST migration

Support QLIST migration using the same principle as QTAILQ:
94869d5c52 ("migration: migrate QTAILQ").

The VMSTATE_QLIST_V macro has the same proto as VMSTATE_QTAILQ_V.
The change mainly resides in QLIST RAW macros: QLIST_RAW_INSERT_HEAD
and QLIST_RAW_REVERSE.

Tests also are provided.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>

authored by

Eric Auger and committed by
Juan Quintela
4746dbf8 0ab99486

+305
+21
include/migration/vmstate.h
··· 229 229 extern const VMStateInfo vmstate_info_bitmap; 230 230 extern const VMStateInfo vmstate_info_qtailq; 231 231 extern const VMStateInfo vmstate_info_gtree; 232 + extern const VMStateInfo vmstate_info_qlist; 232 233 233 234 #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0) 234 235 /* ··· 796 797 .start = 0, \ 797 798 .size = sizeof(_val_type), \ 798 799 .offset = offsetof(_state, _field), \ 800 + } 801 + 802 + /* 803 + * For migrating a QLIST 804 + * Target QLIST needs be properly initialized. 805 + * _type: type of QLIST element 806 + * _next: name of QLIST_ENTRY entry field in QLIST element 807 + * _vmsd: VMSD for QLIST element 808 + * size: size of QLIST element 809 + * start: offset of QLIST_ENTRY in QTAILQ element 810 + */ 811 + #define VMSTATE_QLIST_V(_field, _state, _version, _vmsd, _type, _next) \ 812 + { \ 813 + .name = (stringify(_field)), \ 814 + .version_id = (_version), \ 815 + .vmsd = &(_vmsd), \ 816 + .size = sizeof(_type), \ 817 + .info = &vmstate_info_qlist, \ 818 + .offset = offsetof(_state, _field), \ 819 + .start = offsetof(_type, _next), \ 799 820 } 800 821 801 822 /* _f : field name
+39
include/qemu/queue.h
··· 501 501 QTAILQ_RAW_TQH_CIRC(head)->tql_prev = QTAILQ_RAW_TQE_CIRC(elm, entry); \ 502 502 } while (/*CONSTCOND*/0) 503 503 504 + #define QLIST_RAW_FIRST(head) \ 505 + field_at_offset(head, 0, void *) 506 + 507 + #define QLIST_RAW_NEXT(elm, entry) \ 508 + field_at_offset(elm, entry, void *) 509 + 510 + #define QLIST_RAW_PREVIOUS(elm, entry) \ 511 + field_at_offset(elm, entry + sizeof(void *), void *) 512 + 513 + #define QLIST_RAW_FOREACH(elm, head, entry) \ 514 + for ((elm) = *QLIST_RAW_FIRST(head); \ 515 + (elm); \ 516 + (elm) = *QLIST_RAW_NEXT(elm, entry)) 517 + 518 + #define QLIST_RAW_INSERT_HEAD(head, elm, entry) do { \ 519 + void *first = *QLIST_RAW_FIRST(head); \ 520 + *QLIST_RAW_FIRST(head) = elm; \ 521 + *QLIST_RAW_PREVIOUS(elm, entry) = QLIST_RAW_FIRST(head); \ 522 + if (first) { \ 523 + *QLIST_RAW_NEXT(elm, entry) = first; \ 524 + *QLIST_RAW_PREVIOUS(first, entry) = QLIST_RAW_NEXT(elm, entry); \ 525 + } else { \ 526 + *QLIST_RAW_NEXT(elm, entry) = NULL; \ 527 + } \ 528 + } while (0) 529 + 530 + #define QLIST_RAW_REVERSE(head, elm, entry) do { \ 531 + void *iter = *QLIST_RAW_FIRST(head), *prev = NULL, *next; \ 532 + while (iter) { \ 533 + next = *QLIST_RAW_NEXT(iter, entry); \ 534 + *QLIST_RAW_PREVIOUS(iter, entry) = QLIST_RAW_NEXT(next, entry); \ 535 + *QLIST_RAW_NEXT(iter, entry) = prev; \ 536 + prev = iter; \ 537 + iter = next; \ 538 + } \ 539 + *QLIST_RAW_FIRST(head) = prev; \ 540 + *QLIST_RAW_PREVIOUS(prev, entry) = QLIST_RAW_FIRST(head); \ 541 + } while (0) 542 + 504 543 #endif /* QEMU_SYS_QUEUE_H */
+5
migration/trace-events
··· 76 76 put_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" 77 77 put_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d" 78 78 79 + get_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" 80 + get_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" 81 + put_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" 82 + put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" 83 + 79 84 # qemu-file.c 80 85 qemu_file_fclose(void) "" 81 86
+70
migration/vmstate-types.c
··· 843 843 .get = get_gtree, 844 844 .put = put_gtree, 845 845 }; 846 + 847 + static int put_qlist(QEMUFile *f, void *pv, size_t unused_size, 848 + const VMStateField *field, QJSON *vmdesc) 849 + { 850 + const VMStateDescription *vmsd = field->vmsd; 851 + /* offset of the QTAILQ entry in a QTAILQ element*/ 852 + size_t entry_offset = field->start; 853 + void *elm; 854 + int ret; 855 + 856 + trace_put_qlist(field->name, vmsd->name, vmsd->version_id); 857 + QLIST_RAW_FOREACH(elm, pv, entry_offset) { 858 + qemu_put_byte(f, true); 859 + ret = vmstate_save_state(f, vmsd, elm, vmdesc); 860 + if (ret) { 861 + error_report("%s: failed to save %s (%d)", field->name, 862 + vmsd->name, ret); 863 + return ret; 864 + } 865 + } 866 + qemu_put_byte(f, false); 867 + trace_put_qlist_end(field->name, vmsd->name); 868 + 869 + return 0; 870 + } 871 + 872 + static int get_qlist(QEMUFile *f, void *pv, size_t unused_size, 873 + const VMStateField *field) 874 + { 875 + int ret = 0; 876 + const VMStateDescription *vmsd = field->vmsd; 877 + /* size of a QLIST element */ 878 + size_t size = field->size; 879 + /* offset of the QLIST entry in a QLIST element */ 880 + size_t entry_offset = field->start; 881 + int version_id = field->version_id; 882 + void *elm; 883 + 884 + trace_get_qlist(field->name, vmsd->name, vmsd->version_id); 885 + if (version_id > vmsd->version_id) { 886 + error_report("%s %s", vmsd->name, "too new"); 887 + return -EINVAL; 888 + } 889 + if (version_id < vmsd->minimum_version_id) { 890 + error_report("%s %s", vmsd->name, "too old"); 891 + return -EINVAL; 892 + } 893 + 894 + while (qemu_get_byte(f)) { 895 + elm = g_malloc(size); 896 + ret = vmstate_load_state(f, vmsd, elm, version_id); 897 + if (ret) { 898 + error_report("%s: failed to load %s (%d)", field->name, 899 + vmsd->name, ret); 900 + g_free(elm); 901 + return ret; 902 + } 903 + QLIST_RAW_INSERT_HEAD(pv, elm, entry_offset); 904 + } 905 + QLIST_RAW_REVERSE(pv, elm, entry_offset); 906 + trace_get_qlist_end(field->name, vmsd->name); 907 + 908 + return ret; 909 + } 910 + 911 + const VMStateInfo vmstate_info_qlist = { 912 + .name = "qlist", 913 + .get = get_qlist, 914 + .put = put_qlist, 915 + };
+170
tests/test-vmstate.c
··· 926 926 } 927 927 }; 928 928 929 + /* test QLIST Migration */ 930 + 931 + typedef struct TestQListElement { 932 + uint32_t id; 933 + QLIST_ENTRY(TestQListElement) next; 934 + } TestQListElement; 935 + 936 + typedef struct TestQListContainer { 937 + uint32_t id; 938 + QLIST_HEAD(, TestQListElement) list; 939 + } TestQListContainer; 940 + 941 + static const VMStateDescription vmstate_qlist_element = { 942 + .name = "test/queue list", 943 + .version_id = 1, 944 + .minimum_version_id = 1, 945 + .fields = (VMStateField[]) { 946 + VMSTATE_UINT32(id, TestQListElement), 947 + VMSTATE_END_OF_LIST() 948 + } 949 + }; 950 + 929 951 static const VMStateDescription vmstate_iommu = { 930 952 .name = "iommu", 931 953 .version_id = 1, ··· 935 957 VMSTATE_INT32(id, TestGTreeIOMMU), 936 958 VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1, 937 959 &vmstate_domain, TestGTreeDomain), 960 + VMSTATE_END_OF_LIST() 961 + } 962 + }; 963 + 964 + static const VMStateDescription vmstate_container = { 965 + .name = "test/container/qlist", 966 + .version_id = 1, 967 + .minimum_version_id = 1, 968 + .fields = (VMStateField[]) { 969 + VMSTATE_UINT32(id, TestQListContainer), 970 + VMSTATE_QLIST_V(list, TestQListContainer, 1, vmstate_qlist_element, 971 + TestQListElement, next), 938 972 VMSTATE_END_OF_LIST() 939 973 } 940 974 }; ··· 1229 1263 qemu_fclose(fload); 1230 1264 } 1231 1265 1266 + static uint8_t qlist_dump[] = { 1267 + 0x00, 0x00, 0x00, 0x01, /* container id */ 1268 + 0x1, /* start of a */ 1269 + 0x00, 0x00, 0x00, 0x0a, 1270 + 0x1, /* start of b */ 1271 + 0x00, 0x00, 0x0b, 0x00, 1272 + 0x1, /* start of c */ 1273 + 0x00, 0x0c, 0x00, 0x00, 1274 + 0x1, /* start of d */ 1275 + 0x0d, 0x00, 0x00, 0x00, 1276 + 0x0, /* end of list */ 1277 + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 1278 + }; 1279 + 1280 + static TestQListContainer *alloc_container(void) 1281 + { 1282 + TestQListElement *a = g_malloc(sizeof(TestQListElement)); 1283 + TestQListElement *b = g_malloc(sizeof(TestQListElement)); 1284 + TestQListElement *c = g_malloc(sizeof(TestQListElement)); 1285 + TestQListElement *d = g_malloc(sizeof(TestQListElement)); 1286 + TestQListContainer *container = g_malloc(sizeof(TestQListContainer)); 1287 + 1288 + a->id = 0x0a; 1289 + b->id = 0x0b00; 1290 + c->id = 0xc0000; 1291 + d->id = 0xd000000; 1292 + container->id = 1; 1293 + 1294 + QLIST_INIT(&container->list); 1295 + QLIST_INSERT_HEAD(&container->list, d, next); 1296 + QLIST_INSERT_HEAD(&container->list, c, next); 1297 + QLIST_INSERT_HEAD(&container->list, b, next); 1298 + QLIST_INSERT_HEAD(&container->list, a, next); 1299 + return container; 1300 + } 1301 + 1302 + static void free_container(TestQListContainer *container) 1303 + { 1304 + TestQListElement *iter, *tmp; 1305 + 1306 + QLIST_FOREACH_SAFE(iter, &container->list, next, tmp) { 1307 + QLIST_REMOVE(iter, next); 1308 + g_free(iter); 1309 + } 1310 + g_free(container); 1311 + } 1312 + 1313 + static void compare_containers(TestQListContainer *c1, TestQListContainer *c2) 1314 + { 1315 + TestQListElement *first_item_c1, *first_item_c2; 1316 + 1317 + while (!QLIST_EMPTY(&c1->list)) { 1318 + first_item_c1 = QLIST_FIRST(&c1->list); 1319 + first_item_c2 = QLIST_FIRST(&c2->list); 1320 + assert(first_item_c2); 1321 + assert(first_item_c1->id == first_item_c2->id); 1322 + QLIST_REMOVE(first_item_c1, next); 1323 + QLIST_REMOVE(first_item_c2, next); 1324 + g_free(first_item_c1); 1325 + g_free(first_item_c2); 1326 + } 1327 + assert(QLIST_EMPTY(&c2->list)); 1328 + } 1329 + 1330 + /* 1331 + * Check the prev & next fields are correct by doing list 1332 + * manipulations on the container. We will do that for both 1333 + * the source and the destination containers 1334 + */ 1335 + static void manipulate_container(TestQListContainer *c) 1336 + { 1337 + TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list); 1338 + TestQListElement *elem; 1339 + 1340 + elem = g_malloc(sizeof(TestQListElement)); 1341 + elem->id = 0x12; 1342 + QLIST_INSERT_AFTER(iter, elem, next); 1343 + 1344 + elem = g_malloc(sizeof(TestQListElement)); 1345 + elem->id = 0x13; 1346 + QLIST_INSERT_HEAD(&c->list, elem, next); 1347 + 1348 + while (iter) { 1349 + prev = iter; 1350 + iter = QLIST_NEXT(iter, next); 1351 + } 1352 + 1353 + elem = g_malloc(sizeof(TestQListElement)); 1354 + elem->id = 0x14; 1355 + QLIST_INSERT_BEFORE(prev, elem, next); 1356 + 1357 + elem = g_malloc(sizeof(TestQListElement)); 1358 + elem->id = 0x15; 1359 + QLIST_INSERT_AFTER(prev, elem, next); 1360 + 1361 + QLIST_REMOVE(prev, next); 1362 + g_free(prev); 1363 + } 1364 + 1365 + static void test_save_qlist(void) 1366 + { 1367 + TestQListContainer *container = alloc_container(); 1368 + 1369 + save_vmstate(&vmstate_container, container); 1370 + compare_vmstate(qlist_dump, sizeof(qlist_dump)); 1371 + free_container(container); 1372 + } 1373 + 1374 + static void test_load_qlist(void) 1375 + { 1376 + QEMUFile *fsave, *fload; 1377 + TestQListContainer *orig_container = alloc_container(); 1378 + TestQListContainer *dest_container = g_malloc0(sizeof(TestQListContainer)); 1379 + char eof; 1380 + 1381 + QLIST_INIT(&dest_container->list); 1382 + 1383 + fsave = open_test_file(true); 1384 + qemu_put_buffer(fsave, qlist_dump, sizeof(qlist_dump)); 1385 + g_assert(!qemu_file_get_error(fsave)); 1386 + qemu_fclose(fsave); 1387 + 1388 + fload = open_test_file(false); 1389 + vmstate_load_state(fload, &vmstate_container, dest_container, 1); 1390 + eof = qemu_get_byte(fload); 1391 + g_assert(!qemu_file_get_error(fload)); 1392 + g_assert_cmpint(eof, ==, QEMU_VM_EOF); 1393 + manipulate_container(orig_container); 1394 + manipulate_container(dest_container); 1395 + compare_containers(orig_container, dest_container); 1396 + free_container(orig_container); 1397 + free_container(dest_container); 1398 + } 1399 + 1232 1400 typedef struct TmpTestStruct { 1233 1401 TestStruct *parent; 1234 1402 int64_t diff; ··· 1353 1521 g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain); 1354 1522 g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu); 1355 1523 g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu); 1524 + g_test_add_func("/vmstate/qlist/save/saveqlist", test_save_qlist); 1525 + g_test_add_func("/vmstate/qlist/load/loadqlist", test_load_qlist); 1356 1526 g_test_add_func("/vmstate/tmp_struct", test_tmp_struct); 1357 1527 g_test_run(); 1358 1528